@@ -30,39 +30,43 @@ export function extractParameters(sql: string): string[] {
3030// parameters that are injected by the server
3131export const SERVER_INJECTED_PARAMS = [ "workspaceId" ] ;
3232
33+ /**
34+ * Generates the TypeScript type literal for query parameters from SQL.
35+ * Shared by both the success and failure paths.
36+ */
37+ function formatParametersType ( sql : string ) : string {
38+ const params = extractParameters ( sql ) . filter (
39+ ( p ) => ! SERVER_INJECTED_PARAMS . includes ( p ) ,
40+ ) ;
41+ const paramTypes = extractParameterTypes ( sql ) ;
42+
43+ return params . length > 0
44+ ? `{\n ${ params
45+ . map ( ( p ) => {
46+ const sqlType = paramTypes [ p ] ;
47+ const markerType = sqlType
48+ ? sqlTypeToMarker [ sqlType ]
49+ : "SQLTypeMarker" ;
50+ const helper = sqlType ? sqlTypeToHelper [ sqlType ] : "sql.*()" ;
51+ return `/** ${ sqlType || "any" } - use ${ helper } */\n ${ p } : ${ markerType } ` ;
52+ } )
53+ . join ( ";\n " ) } ;\n }`
54+ : "Record<string, never>" ;
55+ }
56+
3357export function convertToQueryType (
3458 result : DatabricksStatementExecutionResponse ,
3559 sql : string ,
3660 queryName : string ,
37- ) : string {
61+ ) : { type : string ; hasResults : boolean } {
3862 const dataRows = result . result ?. data_array || [ ] ;
3963 const columns = dataRows . map ( ( row ) => ( {
4064 name : row [ 0 ] || "" ,
4165 type_name : row [ 1 ] ?. toUpperCase ( ) || "STRING" ,
4266 comment : row [ 2 ] || undefined ,
4367 } ) ) ;
4468
45- const params = extractParameters ( sql ) . filter (
46- ( p ) => ! SERVER_INJECTED_PARAMS . includes ( p ) ,
47- ) ;
48-
49- const paramTypes = extractParameterTypes ( sql ) ;
50-
51- // generate parameters types with JSDoc hints
52- const paramsType =
53- params . length > 0
54- ? `{\n ${ params
55- . map ( ( p ) => {
56- const sqlType = paramTypes [ p ] ;
57- // if no type annotation, use SQLTypeMarker (union type)
58- const markerType = sqlType
59- ? sqlTypeToMarker [ sqlType ]
60- : "SQLTypeMarker" ;
61- const helper = sqlType ? sqlTypeToHelper [ sqlType ] : "sql.*()" ;
62- return `/** ${ sqlType || "any" } - use ${ helper } */\n ${ p } : ${ markerType } ` ;
63- } )
64- . join ( ";\n " ) } ;\n }`
65- : "Record<string, never>" ;
69+ const paramsType = formatParametersType ( sql ) ;
6670
6771 // generate result fields with JSDoc
6872 const resultFields = columns . map ( ( column ) => {
@@ -81,15 +85,50 @@ export function convertToQueryType(
8185 return `${ comment } ${ name } : ${ mappedType } ` ;
8286 } ) ;
8387
84- return `{
88+ const hasResults = resultFields . length > 0 ;
89+
90+ const type = `{
8591 name: "${ queryName } ";
8692 parameters: ${ paramsType } ;
87- result: Array<{
93+ result: ${
94+ hasResults
95+ ? `Array<{
8896 ${ resultFields . join ( ";\n " ) } ;
89- }>;
97+ }>`
98+ : "unknown"
99+ } ;
100+ }` ;
101+
102+ return { type, hasResults } ;
103+ }
104+
105+ /**
106+ * Used when DESCRIBE QUERY fails so the query still appears in QueryRegistry.
107+ * Generates a type with unknown result from SQL alone (no warehouse call).
108+ */
109+ function generateUnknownResultQuery ( sql : string , queryName : string ) : string {
110+ const paramsType = formatParametersType ( sql ) ;
111+
112+ return `{
113+ name: "${ queryName } ";
114+ parameters: ${ paramsType } ;
115+ result: unknown;
90116 }` ;
91117}
92118
119+ function cacheFailedQuery (
120+ cache : ReturnType < typeof loadCache > ,
121+ querySchemas : QuerySchema [ ] ,
122+ sql : string ,
123+ queryName : string ,
124+ sqlHash : string ,
125+ ) : void {
126+ const type = generateUnknownResultQuery ( sql , queryName ) ;
127+ querySchemas . push ( { name : queryName , type } ) ;
128+ cache . queries [ queryName ] = { hash : sqlHash , type, retry : true } ;
129+ saveCache ( cache ) ;
130+ }
131+
93132export function extractParameterTypes ( sql : string ) : Record < string , string > {
94133 const paramTypes : Record < string , string > = { } ;
95134 const regex =
@@ -131,7 +170,6 @@ export async function generateQueriesFromDescribe(
131170
132171 const client = new WorkspaceClient ( { } ) ;
133172 const querySchemas : QuerySchema [ ] = [ ] ;
134- const failedQueries : { name : string ; error : string } [ ] = [ ] ;
135173 const spinner = new Spinner ( ) ;
136174
137175 // process each query file
@@ -144,9 +182,9 @@ export async function generateQueriesFromDescribe(
144182 const sql = fs . readFileSync ( path . join ( queryFolder , file ) , "utf8" ) ;
145183 const sqlHash = hashSQL ( sql ) ;
146184
147- // check cache
185+ // check cache (skip if marked for retry after a failed DESCRIBE)
148186 const cached = cache . queries [ queryName ] ;
149- if ( cached && cached . hash === sqlHash ) {
187+ if ( cached && cached . hash === sqlHash && ! cached . retry ) {
150188 querySchemas . push ( { name : queryName , type : cached . type } ) ;
151189 spinner . start ( `Processing ${ queryName } (${ i + 1 } /${ queryFiles . length } )` ) ;
152190 spinner . stop ( `✓ ${ queryName } (cached)` ) ;
@@ -170,40 +208,33 @@ export async function generateQueriesFromDescribe(
170208 if ( result . status . state === "FAILED" ) {
171209 const sqlError =
172210 result . status . error ?. message || "Query execution failed" ;
211+ cacheFailedQuery ( cache , querySchemas , sql , queryName , sqlHash ) ;
173212 spinner . stop ( `✗ ${ queryName } - failed` ) ;
174213 spinner . printDetail ( `SQL Error: ${ sqlError } ` ) ;
175214 spinner . printDetail ( `Query: ${ cleanedSql . slice ( 0 , 200 ) } ` ) ;
176- failedQueries . push ( {
177- name : queryName ,
178- error : sqlError ,
179- } ) ;
180215 continue ;
181216 }
182217
183218 // convert result to query schema
184- const type = convertToQueryType ( result , sql , queryName ) ;
219+ const { type, hasResults } = convertToQueryType ( result , sql , queryName ) ;
185220 querySchemas . push ( { name : queryName , type } ) ;
186221
187- // update cache
188- cache . queries [ queryName ] = { hash : sqlHash , type } ;
222+ // update cache immediately so successful results survive partial failures
223+ // retry if DESCRIBE returned no columns (result: unknown)
224+ const retry = ! hasResults ;
225+ cache . queries [ queryName ] = { hash : sqlHash , type, retry } ;
226+ saveCache ( cache ) ;
189227
190228 spinner . stop ( `✓ ${ queryName } ` ) ;
191229 } catch ( error ) {
192230 const errorMessage =
193231 error instanceof Error ? error . message : "Unknown error" ;
194- spinner . stop ( `✗ ${ queryName } - ${ errorMessage } ` ) ;
195- failedQueries . push ( { name : queryName , error : errorMessage } ) ;
232+ spinner . stop ( `✗ ${ queryName } ` ) ;
233+ spinner . printDetail ( errorMessage ) ;
234+ cacheFailedQuery ( cache , querySchemas , sql , queryName , sqlHash ) ;
196235 }
197236 }
198237
199- // save cache
200- saveCache ( cache ) ;
201-
202- // log warning if there are failed queries
203- if ( failedQueries . length > 0 ) {
204- logger . debug ( "Warning: %d queries failed" , failedQueries . length ) ;
205- }
206-
207238 return querySchemas ;
208239}
209240
0 commit comments