Skip to content

Commit 856febf

Browse files
authored
chore(bqjdbc): helper tools (#12973)
1 parent d690333 commit 856febf

13 files changed

Lines changed: 1022 additions & 1 deletion

File tree

.github/workflows/generated_files_sync.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ jobs:
175175
|grep --invert-match samples \
176176
|grep --invert-match benchmark \
177177
|grep --invert-match grafeas \
178+
|grep --invert-match '/tools/' \
178179
|grep --invert-match 'cloud-build.*v2' \
179180
|grep --invert-match 'google/monitoring/v3/DroppedLabelsOuterClass.java' \
180181
|grep --invert-match 'google/cloud/policytroubleshooter/v1/Explanations.java')
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
drivers/**
22
target-it/**
33
*logs*/**
4-
**/ITBigQueryJDBCLocalTest.java
4+
**/ITBigQueryJDBCLocalTest.java
5+
6+
tools/**/*.class
7+
tools/**/*.jfr
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import java.sql.*;
18+
import java.util.*;
19+
20+
public class JDBCClient {
21+
public static void main(String[] args) throws Exception {
22+
String url = null;
23+
String driverClass = "com.google.cloud.bigquery.jdbc.BigQueryDriver";
24+
String action = null;
25+
String query = null;
26+
boolean noOutput = false;
27+
int generateRows = 0;
28+
int generateCols = 5;
29+
String metadataMethod = "getTables";
30+
String catalog = null;
31+
String schema = null;
32+
String table = null;
33+
String typesStr = null;
34+
35+
Map<String, String> extraArgs = new HashMap<>();
36+
37+
for (int i = 0; i < args.length; i++) {
38+
String arg = args[i];
39+
if (arg.startsWith("--")) {
40+
String key = arg.substring(2);
41+
String val = "";
42+
if (key.contains("=")) {
43+
String[] parts = key.split("=", 2);
44+
key = parts[0];
45+
val = parts[1];
46+
} else if (i + 1 < args.length && !args[i+1].startsWith("-")) {
47+
val = args[i+1];
48+
i++;
49+
}
50+
51+
switch (key) {
52+
case "url": url = val; break;
53+
case "driver-class": driverClass = val; break;
54+
case "action": action = val; break;
55+
case "query": query = val; break;
56+
case "no-output": noOutput = true; break;
57+
case "generate-rows": generateRows = Integer.parseInt(val); break;
58+
case "generate-cols": generateCols = Integer.parseInt(val); break;
59+
case "metadata-method": metadataMethod = val; break;
60+
case "catalog": catalog = val; break;
61+
case "schema": schema = val; break;
62+
case "table": table = val; break;
63+
case "types": typesStr = val; break;
64+
default: extraArgs.put(key, val); break;
65+
}
66+
}
67+
}
68+
69+
if (url == null || action == null) {
70+
System.err.println("Error: --url and --action are required.");
71+
System.exit(1);
72+
}
73+
74+
StringBuilder sb = new StringBuilder(url);
75+
if (!url.endsWith(";")) sb.append(";");
76+
for (Map.Entry<String, String> entry : extraArgs.entrySet()) {
77+
sb.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
78+
}
79+
String finalUrl = sb.toString();
80+
81+
System.out.println("Final Connection String: " + finalUrl);
82+
System.out.println("Driver Class: " + driverClass);
83+
84+
Class.forName(driverClass);
85+
86+
long startE2E = System.currentTimeMillis();
87+
Connection conn = DriverManager.getConnection(finalUrl);
88+
System.out.println("Connection successful.\n");
89+
90+
if ("query".equals(action)) {
91+
if (generateRows > 0) {
92+
query = generateDataQuery(generateRows, generateCols);
93+
}
94+
if (query == null) {
95+
System.err.println("Error: --query or --generate-rows is required when action is 'query'");
96+
System.exit(1);
97+
}
98+
warmup(conn);
99+
runQuery(conn, query, noOutput);
100+
} else if ("metadata".equals(action)) {
101+
String[] types = typesStr != null ? typesStr.split(",") : null;
102+
runMetadata(conn, metadataMethod, catalog, schema, table, types);
103+
}
104+
105+
conn.close();
106+
long e2eTime = System.currentTimeMillis() - startE2E;
107+
System.out.println("Total e2e time: " + (e2eTime / 1000.0) + " seconds");
108+
}
109+
110+
private static void warmup(Connection conn) {
111+
System.out.println("Performing warmup query...");
112+
try (Statement stmt = conn.createStatement()) {
113+
stmt.execute("SELECT 1");
114+
try (ResultSet rs = stmt.getResultSet()) {
115+
while (rs.next()) {
116+
rs.getObject(1);
117+
}
118+
}
119+
} catch (Exception e) {
120+
System.err.println("Warning: Warmup query failed: " + e.getMessage());
121+
}
122+
System.out.println("Warmup complete.\n");
123+
}
124+
125+
private static String generateDataQuery(int rows, int cols) {
126+
int N = (int) Math.ceil(Math.sqrt(rows));
127+
String idxExpr = "(i - 1) * " + N + " + j";
128+
129+
StringBuilder sb = new StringBuilder();
130+
sb.append("SELECT ").append(idxExpr).append(" AS idx");
131+
for (int k = 1; k < cols; k++) {
132+
sb.append(", CONCAT('val_', ").append(idxExpr).append(") AS col").append(k);
133+
}
134+
sb.append(" FROM UNNEST(GENERATE_ARRAY(1, ").append(N).append(")) AS i");
135+
sb.append(" CROSS JOIN UNNEST(GENERATE_ARRAY(1, ").append(N).append(")) AS j");
136+
sb.append(" LIMIT ").append(rows);
137+
138+
return sb.toString();
139+
}
140+
141+
private static void runQuery(Connection conn, String query, boolean noOutput) throws Exception {
142+
System.out.println("Executing query: " + query);
143+
144+
long startExec = System.currentTimeMillis();
145+
Statement stmt = conn.createStatement();
146+
boolean hasResultSet = stmt.execute(query);
147+
long execTime = System.currentTimeMillis() - startExec;
148+
149+
if (!hasResultSet) {
150+
System.out.println("Query executed but returned no results.");
151+
return;
152+
}
153+
154+
long startFetch = System.currentTimeMillis();
155+
ResultSet rs = stmt.getResultSet();
156+
ResultSetMetaData rsMeta = rs.getMetaData();
157+
int colCount = rsMeta.getColumnCount();
158+
159+
if (!noOutput) {
160+
for (int i = 1; i <= colCount; i++) {
161+
System.out.print(rsMeta.getColumnName(i) + " [" + rsMeta.getColumnTypeName(i) + "]");
162+
if (i < colCount) System.out.print(" | ");
163+
}
164+
System.out.println();
165+
System.out.println("------------------------------------------------------------------------------------------------------------------------");
166+
}
167+
168+
int rowCount = 0;
169+
Object[] row = new Object[colCount];
170+
while (rs.next()) {
171+
rowCount++;
172+
for (int i = 1; i <= colCount; i++) {
173+
row[i - 1] = rs.getObject(i);
174+
}
175+
if (!noOutput) {
176+
StringBuilder sb = new StringBuilder();
177+
for (int i = 0; i < colCount; i++) {
178+
sb.append(row[i]);
179+
if (i < colCount - 1) {
180+
sb.append(" | ");
181+
}
182+
}
183+
System.out.println(sb.toString());
184+
}
185+
}
186+
long fetchTime = System.currentTimeMillis() - startFetch;
187+
188+
System.out.println("\nMetrics:");
189+
System.out.println(" Rows retrieved: " + rowCount);
190+
System.out.println(" Values retrieved: " + (long) rowCount * colCount);
191+
System.out.println(" Execution time: " + (execTime / 1000.0) + " seconds");
192+
System.out.println(" Fetch time: " + (fetchTime / 1000.0) + " seconds");
193+
194+
rs.close();
195+
stmt.close();
196+
}
197+
198+
private static void runMetadata(Connection conn, String method, String catalog, String schema, String table, String[] types) throws Exception {
199+
DatabaseMetaData dbMeta = conn.getMetaData();
200+
System.out.println("Executing metadata method: " + method);
201+
202+
ResultSet rs = null;
203+
Object result = null;
204+
205+
if ("getTables".equals(method)) {
206+
rs = dbMeta.getTables(catalog, schema, table, types);
207+
} else if ("getColumns".equals(method)) {
208+
rs = dbMeta.getColumns(catalog, schema, table, null);
209+
} else if ("getSchemas".equals(method)) {
210+
rs = dbMeta.getSchemas(catalog, schema);
211+
} else if ("getCatalogs".equals(method)) {
212+
rs = dbMeta.getCatalogs();
213+
} else if ("getTableTypes".equals(method)) {
214+
rs = dbMeta.getTableTypes();
215+
} else if ("getTypeInfo".equals(method)) {
216+
rs = dbMeta.getTypeInfo();
217+
} else {
218+
try {
219+
java.lang.reflect.Method m = DatabaseMetaData.class.getMethod(method);
220+
result = m.invoke(dbMeta);
221+
} catch (NoSuchMethodException e) {
222+
System.err.println("Error: Method '" + method + "' not supported or found.");
223+
return;
224+
}
225+
}
226+
227+
if (rs != null) {
228+
ResultSetMetaData rsMeta = rs.getMetaData();
229+
int colCount = rsMeta.getColumnCount();
230+
231+
for (int i = 1; i <= colCount; i++) {
232+
System.out.print(rsMeta.getColumnName(i));
233+
if (i < colCount) System.out.print(" | ");
234+
}
235+
System.out.println();
236+
System.out.println("------------------------------------------------------------------------------------------------------------------------");
237+
238+
int rowCount = 0;
239+
while (rs.next()) {
240+
rowCount++;
241+
for (int i = 1; i <= colCount; i++) {
242+
System.out.print(rs.getObject(i));
243+
if (i < colCount) System.out.print(" | ");
244+
}
245+
System.out.println();
246+
}
247+
System.out.println("\n(" + rowCount + " rows)");
248+
rs.close();
249+
} else if (result != null) {
250+
System.out.println("Result: " + result);
251+
} else {
252+
System.out.println("Method returned null or void.");
253+
}
254+
}
255+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Makefile for Java JDBC Client
2+
3+
JC = javac
4+
J = java --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED -Xmx2g
5+
6+
default: classes
7+
8+
classes: JDBCClient.class
9+
10+
JDBCClient.class: JDBCClient.java
11+
$(JC) JDBCClient.java
12+
13+
clean:
14+
rm -f *.class
15+
16+
17+
# Default configuration
18+
VERSION = $(shell sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' ../../pom.xml | head -n 1)
19+
DRIVER_JAR ?= ../../drivers/google-cloud-bigquery-jdbc-$(VERSION)-all.jar
20+
DRIVER_CLASS = com.google.cloud.bigquery.jdbc.BigQueryDriver
21+
URL ?= jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;
22+
CREDENTIALS ?= $(GOOGLE_APPLICATION_CREDENTIALS)
23+
24+
ROWS ?= 10
25+
COLS ?= 5
26+
METHOD ?= getTables
27+
28+
OUTPUT ?= false
29+
QUERY ?= SELECT 1
30+
31+
ifeq ($(OUTPUT),false)
32+
OUTPUT_FLAG = --no-output
33+
else
34+
OUTPUT_FLAG =
35+
endif
36+
37+
# JFR Configuration
38+
JFR ?= false
39+
ifeq ($(JFR),true)
40+
JFR_FLAGS = -XX:StartFlightRecording=settings=./config/sampler.jfc,filename=recording.jfr
41+
else
42+
JFR_FLAGS =
43+
endif
44+
45+
COMMON_FLAGS = --url "$(URL)" \
46+
--driver-jar "$(DRIVER_JAR)" \
47+
--driver-class "$(DRIVER_CLASS)" \
48+
--ProjectId "bigquery-devtools-drivers" \
49+
--OAuthType=0 \
50+
--OAuthServiceAcctEmail="" \
51+
--OAuthPvtKeyPath="$(CREDENTIALS)" \
52+
--EnableHighThroughputAPI=1 \
53+
--HighThroughputActivationRatio=0 \
54+
--HighThroughputMinTableSize=0 \
55+
--MaxResults=20000 \
56+
--EnableSession=1
57+
58+
query: classes
59+
$(J) $(JFR_FLAGS) -cp .:$(DRIVER_JAR) JDBCClient --action query $(COMMON_FLAGS) --query "$(QUERY)" $(OUTPUT_FLAG) $(EXTRA_ARGS)
60+
61+
query-generated: classes
62+
$(J) $(JFR_FLAGS) -cp .:$(DRIVER_JAR) JDBCClient --action query $(COMMON_FLAGS) $(OUTPUT_FLAG) --generate-rows $(ROWS) --generate-cols $(COLS) $(EXTRA_ARGS)
63+
64+
metadata: classes
65+
$(J) $(JFR_FLAGS) -cp .:$(DRIVER_JAR) JDBCClient --action metadata --metadata-method $(METHOD) $(COMMON_FLAGS) $(EXTRA_ARGS)

0 commit comments

Comments
 (0)