Skip to content

Commit cdde2db

Browse files
Fall back to default argument in native query creation (#64)
1 parent 1767195 commit cdde2db

2 files changed

Lines changed: 150 additions & 149 deletions

File tree

ndc-cli/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies {
2525
implementation("com.mysql:mysql-connector-j:9.2.0")
2626
implementation("net.snowflake:snowflake-jdbc:3.23.1")
2727
implementation("io.trino:trino-jdbc:466")
28+
implementation("com.jakewharton.picnic:picnic:0.6.0")
2829
}
2930

3031
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {

ndc-cli/src/main/kotlin/io/hasura/cli/NativeQueries.kt

Lines changed: 149 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.hasura.cli
22

3+
import com.jakewharton.picnic.BorderStyle
4+
import com.jakewharton.picnic.TextAlignment
5+
import com.jakewharton.picnic.table
36
import org.jooq.impl.DSL
47
import java.io.File
58
import java.sql.ParameterMetaData
@@ -10,6 +13,8 @@ import io.hasura.ndc.common.NativeQueryInfo
1013
import io.hasura.ndc.ir.ArgumentInfo
1114
import io.hasura.ndc.ir.Type
1215

16+
const val DEFAULT_SQL_TYPE="VARCHAR"
17+
1318
fun readNativeQuerySQL(
1419
configurationDir: String,
1520
nativeQuerySQLFile: String,
@@ -118,9 +123,21 @@ fun prettyPrintSQL(sql: String) {
118123

119124
fun prettyPrintParameters(paramNames: List<String>) {
120125
if (paramNames.isNotEmpty()) {
121-
println("\n=== Named Parameters ===")
122-
paramNames.forEachIndexed { index, name -> println("Parameter ${index + 1}: $name") }
123-
println("=======================\n")
126+
val paramTable = table {
127+
header {
128+
row {
129+
cell("=== Named Parameters ===") {
130+
columnSpan = 2
131+
}
132+
}
133+
row("Parameter #", "Name")
134+
}
135+
136+
paramNames.forEachIndexed { index, name ->
137+
row(index + 1, name)
138+
}
139+
}
140+
println(paramTable)
124141
} else {
125142
println("\n=== No Named Parameters Found ===\n")
126143
}
@@ -186,54 +203,45 @@ fun createNativeQuery(
186203
val columnCount = resultSetMetaData.columnCount
187204
println("Query returns the following $columnCount columns:")
188205

189-
println(
190-
"|--------------------------------|--------------------------------|----------------|------------|--------------------------------|",
191-
)
192-
println(
193-
"| Column Name | Type Name | SQL Type | Nullable | Class name |",
194-
)
195-
println(
196-
"|--------------------------------|--------------------------------|----------------|------------|--------------------------------|",
197-
)
198-
199-
for (i in 1..columnCount) {
200-
val columnName = resultSetMetaData.getColumnName(i)
201-
val columnTypeName = resultSetMetaData.getColumnTypeName(i)
202-
val columnClassName = resultSetMetaData.getColumnClassName(i)
203-
val columnType = getJavaSQLTypeName(resultSetMetaData.getColumnType(i))
204-
val columnNullable =
205-
when (resultSetMetaData.isNullable(i)) {
206-
ResultSetMetaData.columnNoNulls -> false
207-
ResultSetMetaData.columnNullable -> true
208-
ResultSetMetaData.columnNullableUnknown -> true
209-
else -> true
210-
}
211-
212-
val formattedColumnName = columnName.padEnd(30).substring(0, 30)
213-
val formattedTypeName = columnTypeName.padEnd(30).substring(0, 30)
214-
val formattedSqlType = columnType.padEnd(12).substring(0, 12)
215-
val formattedNullable = columnNullable.toString().padEnd(10).substring(0, 10)
216-
val formattedClassName = columnClassName.padEnd(30).substring(0, 30)
217-
218-
println(
219-
"| $formattedColumnName | $formattedTypeName | $formattedSqlType | $formattedNullable | $formattedClassName |",
220-
)
221-
222-
val nativeOperationColumn =
223-
if (columnNullable) {
224-
Type.Nullable(Type.Named (
225-
name = columnTypeName
226-
))
227-
} else {
228-
Type.Named(name = columnTypeName)
229-
}
230-
231-
nativeQueryColumns[columnName] = nativeOperationColumn
206+
// Create a table using Picnic
207+
val columnsTable = table {
208+
cellStyle {
209+
border = true
210+
paddingLeft = 1
211+
paddingRight = 1
212+
}
213+
214+
header {
215+
row("Column Name", "Type Name", "SQL Type", "Nullable", "Class name")
216+
}
217+
218+
for (i in 1..columnCount) {
219+
val columnName = resultSetMetaData.getColumnName(i)
220+
val columnTypeName = resultSetMetaData.getColumnTypeName(i)
221+
val columnClassName = resultSetMetaData.getColumnClassName(i)
222+
val columnType = getJavaSQLTypeName(resultSetMetaData.getColumnType(i))
223+
val columnNullable =
224+
when (resultSetMetaData.isNullable(i)) {
225+
ResultSetMetaData.columnNoNulls -> false
226+
ResultSetMetaData.columnNullable -> true
227+
ResultSetMetaData.columnNullableUnknown -> true
228+
else -> true
229+
}
230+
231+
row(columnName, columnTypeName, columnType, columnNullable.toString(), columnClassName)
232+
233+
val nativeOperationColumn =
234+
if (columnNullable) {
235+
Type.Nullable(Type.Named(name = columnTypeName))
236+
} else {
237+
Type.Named(name = columnTypeName)
238+
}
239+
240+
nativeQueryColumns[columnName] = nativeOperationColumn
241+
}
232242
}
233243

234-
println(
235-
"|--------------------------------|--------------------------------|----------------|------------|--------------------------------|",
236-
)
244+
println(columnsTable)
237245
} else {
238246
println("Unable to retrieve result set metadata for this query")
239247
}
@@ -283,86 +291,79 @@ private fun processParameterMetadata(
283291
println("Query has $paramCount parameters:")
284292

285293
if (paramCount > 0) {
286-
println(
287-
"|------------|--------------------------------|----------------|------------|--------------------------------|----------------------------------------------|",
288-
)
289-
println(
290-
"| Param # | Parameter Name | SQL Type | Nullable | Type Name | Class Name |",
291-
)
292-
println(
293-
"|------------|--------------------------------|----------------|------------|--------------------------------|----------------------------------------------|",
294-
)
295-
296-
for (i in 1..paramCount) {
297-
try {
298-
val jdbcTypeName =
299-
try {
300-
paramMetadata.getParameterTypeName(i)
301-
} catch (e: Exception) {
302-
"UNKNOWN"
303-
}
304-
305-
val sqlType =
306-
try {
307-
getJavaSQLTypeName(paramMetadata.getParameterType(i))
308-
} catch (e: Exception) {
309-
"UNKNOWN"
310-
}
294+
try {
295+
// Test if we can access the metadata properly by trying to get the first parameter's type name
296+
// This will throw an exception if the driver doesn't support parameter metadata properly
297+
paramMetadata.getParameterTypeName(1)
298+
299+
// Create a table using Picnic
300+
val paramTable = table {
301+
cellStyle {
302+
border = true
303+
paddingLeft = 1
304+
paddingRight = 1
305+
}
311306

312-
val paramClassName =
313-
try {
314-
paramMetadata.getParameterClassName(i)
315-
} catch (e: Exception) {
316-
"UNKNOWN"
317-
}
307+
header {
308+
row("Param #", "Parameter Name", "SQL Type", "Nullable", "Type Name", "Class Name")
309+
}
318310

319-
val isNullable =
311+
for (i in 1..paramCount) {
320312
try {
321-
when (paramMetadata.isNullable(i)) {
313+
val jdbcTypeName = paramMetadata.getParameterTypeName(i)
314+
val sqlType = getJavaSQLTypeName(paramMetadata.getParameterType(i))
315+
val paramClassName = paramMetadata.getParameterClassName(i)
316+
val isNullable = when (paramMetadata.isNullable(i)) {
322317
ParameterMetaData.parameterNoNulls -> false
323318
ParameterMetaData.parameterNullable -> true
324319
ParameterMetaData.parameterNullableUnknown -> true
325320
else -> true
326321
}
327-
} catch (e: Exception) {
328-
true
329-
}
330322

331-
val paramName = parsedResult.parameterPositions[i]
323+
val paramName = parsedResult.parameterPositions[i]
332324

333-
val formattedParamNum = i.toString().padEnd(10).substring(0, 10)
334-
val formattedParamName = (paramName ?: "").padEnd(30).substring(0, 30)
335-
val formattedSqlType = sqlType.padEnd(12).substring(0, 12)
336-
val formattedNullable = isNullable.toString().padEnd(10).substring(0, 10)
337-
val formattedTypeName = jdbcTypeName.padEnd(30).substring(0, 30)
338-
val formattedClassName = paramClassName.padEnd(44).substring(0, 44)
325+
row(i, paramName ?: "", sqlType, isNullable.toString(), jdbcTypeName, paramClassName)
339326

340-
println(
341-
"| $formattedParamNum | $formattedParamName | $formattedSqlType | $formattedNullable | $formattedTypeName | $formattedClassName |",
342-
)
343-
344-
val type = if (isNullable) {
345-
Type.Nullable(Type.Named(jdbcTypeName))
346-
} else {
347-
Type.Named(jdbcTypeName)
348-
}
327+
val type = if (isNullable) {
328+
Type.Nullable(Type.Named(jdbcTypeName))
329+
} else {
330+
Type.Named(jdbcTypeName)
331+
}
349332

350-
if (paramName != null) {
351-
val nativeOperationArgument =
352-
ArgumentInfo(argument_type = type)
353-
nativeQueryArgs[paramName] = nativeOperationArgument
333+
if (paramName != null) {
334+
val nativeOperationArgument = ArgumentInfo(argument_type = type)
335+
nativeQueryArgs[paramName] = nativeOperationArgument
336+
}
337+
} catch (e: Exception) {
338+
// If we encounter an exception for a specific parameter, fall back to defaults for this parameter
339+
println("Error getting metadata for parameter $i: ${e.message}")
340+
println("Falling back to default for this parameter")
341+
342+
val paramName = parsedResult.parameterPositions[i]
343+
if (paramName != null) {
344+
row(i, paramName, "$DEFAULT_SQL_TYPE (DEFAULT)", "true", "$DEFAULT_SQL_TYPE (DEFAULT)", "java.lang.String")
345+
346+
val type =
347+
Type.Nullable(
348+
underlying_type =
349+
Type.Named(name = DEFAULT_SQL_TYPE))
350+
val nativeOperationArgument = ArgumentInfo(
351+
argument_type = type,
352+
description = "Auto-detected parameter, type defaulted to varchar"
353+
)
354+
nativeQueryArgs[paramName] = nativeOperationArgument
355+
}
356+
}
354357
}
355-
} catch (e: Exception) {
356-
e.printStackTrace()
357-
358-
println(
359-
"| ${"ERROR".padEnd(10).substring(0, 10)} | ${"ERROR".padEnd(30).substring(0, 30)} | ${"ERROR".padEnd(12).substring(0, 12)} | ${"ERROR".padEnd(10).substring(0, 10)} | ${"ERROR".padEnd(30).substring(0, 30)} | ${"ERROR".padEnd(44).substring(0, 44)} |",
360-
)
361358
}
359+
360+
println(paramTable)
361+
} catch (e: Exception) {
362+
// If we encounter an exception while testing metadata access, fall back to defaults for all parameters
363+
println("Parameter metadata not supported by this JDBC driver: ${e.message}")
364+
println("Falling back to defaults for all parameters")
365+
processParametersWithDefaults(parsedResult, nativeQueryArgs)
362366
}
363-
println(
364-
"|------------|--------------------------------|----------------|------------|--------------------------------|----------------------------------------------|",
365-
)
366367
}
367368
}
368369

@@ -373,45 +374,44 @@ private fun processParametersWithDefaults(
373374
) {
374375
println("Query has ${parsedResult.parameterPositions.size} parameters:")
375376
println("\n⚠️ WARNING: Parameter metadata not available or skipped")
376-
println("⚠️ Defaulting all parameters to VARCHAR type")
377+
println("⚠️ Defaulting all parameters to $DEFAULT_SQL_TYPE type")
377378
println("⚠️ You may need to manually correct the parameter types after generation\n")
378379

379380
if (parsedResult.parameterPositions.isNotEmpty()) {
380-
println(
381-
"|------------|--------------------------------|----------------------|----------------------|------------|--------------------------------|",
382-
)
383-
println(
384-
"| Param # | Parameter Name | SQL Type | Class Name | Nullable | Type Name |",
385-
)
386-
println(
387-
"|------------|--------------------------------|----------------------|----------------------|------------|--------------------------------|",
388-
)
389-
390-
parsedResult.parameterPositions.forEach { (position, paramName) ->
391-
val defaultSqlType = "varchar"
392-
393-
val formattedPosition = position.toString().padEnd(10).substring(0, 10)
394-
val formattedParamName = paramName.padEnd(30).substring(0, 30)
395-
val formattedSqlType = "$defaultSqlType (DEFAULT)".padEnd(20).substring(0, 20)
396-
val formattedClassName = "java.lang.String".padEnd(20).substring(0, 20)
397-
val formattedNullable = "true".padEnd(10).substring(0, 10)
398-
val formattedTypeName = "VARCHAR (DEFAULT)".padEnd(30).substring(0, 30)
399-
400-
println(
401-
"| $formattedPosition | $formattedParamName | $formattedSqlType | $formattedClassName | $formattedNullable | $formattedTypeName |",
402-
)
403-
404-
val type = Type.Named(name = defaultSqlType)
405-
406-
val nativeOperationArgument =
407-
ArgumentInfo(
408-
argument_type = type,
409-
description = "Auto-detected parameter, type defaulted to varchar",
381+
// Create a table using Picnic
382+
val defaultParamTable = table {
383+
384+
cellStyle {
385+
border = true
386+
paddingLeft = 1
387+
paddingRight = 1
388+
}
389+
390+
header {
391+
row("Param #", "Parameter Name", "SQL Type", "Class Name", "Nullable", "Type Name")
392+
}
393+
394+
parsedResult.parameterPositions.forEach { (position, paramName) ->
395+
row(
396+
position,
397+
paramName,
398+
"$DEFAULT_SQL_TYPE (DEFAULT)",
399+
"java.lang.String",
400+
"true",
401+
"$DEFAULT_SQL_TYPE (DEFAULT)"
410402
)
411-
nativeQueryArgs[paramName] = nativeOperationArgument
403+
404+
val type = Type.Nullable (underlying_type = Type.Named(name = DEFAULT_SQL_TYPE))
405+
406+
val nativeOperationArgument =
407+
ArgumentInfo(
408+
argument_type = type,
409+
description = "Auto-detected parameter, type defaulted to varchar",
410+
)
411+
nativeQueryArgs[paramName] = nativeOperationArgument
412+
}
412413
}
413-
println(
414-
"|------------|--------------------------------|----------------------|----------------------|------------|--------------------------------|",
415-
)
414+
415+
println(defaultParamTable)
416416
}
417417
}

0 commit comments

Comments
 (0)