11package io.hasura.cli
22
3+ import com.jakewharton.picnic.BorderStyle
4+ import com.jakewharton.picnic.TextAlignment
5+ import com.jakewharton.picnic.table
36import org.jooq.impl.DSL
47import java.io.File
58import java.sql.ParameterMetaData
@@ -10,6 +13,8 @@ import io.hasura.ndc.common.NativeQueryInfo
1013import io.hasura.ndc.ir.ArgumentInfo
1114import io.hasura.ndc.ir.Type
1215
16+ const val DEFAULT_SQL_TYPE = " VARCHAR"
17+
1318fun readNativeQuerySQL (
1419 configurationDir : String ,
1520 nativeQuerySQLFile : String ,
@@ -118,9 +123,21 @@ fun prettyPrintSQL(sql: String) {
118123
119124fun 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