1
- import { fmap , normalizeIndent } from "@ts-safeql/shared" ;
1
+ import { defaultTypeExprMapping , fmap , normalizeIndent } from "@ts-safeql/shared" ;
2
2
import * as LibPgQueryAST from "@ts-safeql/sql-ast" ;
3
3
import {
4
4
isColumnStarRef ,
5
5
isColumnTableColumnRef ,
6
6
isColumnTableStarRef ,
7
7
isColumnUnknownRef ,
8
8
isSingleCell ,
9
+ isTuple ,
9
10
} from "./ast-decribe.utils" ;
10
11
import { ResolvedColumn , SourcesResolver , getSources } from "./ast-get-sources" ;
11
12
import { PgColRow , PgEnumsMaps , PgTypesMap } from "./generate" ;
@@ -20,7 +21,7 @@ type ASTDescriptionOptions = {
20
21
pgColsBySchemaAndTableName : Map < string , Map < string , PgColRow [ ] > > ;
21
22
pgTypes : PgTypesMap ;
22
23
pgEnums : PgEnumsMaps ;
23
- pgFns : Map < string , string > ;
24
+ pgFns : Map < string , { ts : string ; pg : string } > ;
24
25
} ;
25
26
26
27
type ASTDescriptionContext = ASTDescriptionOptions & {
@@ -38,7 +39,7 @@ export type ASTDescribedColumnType =
38
39
| { kind : "union" ; value : ASTDescribedColumnType [ ] }
39
40
| { kind : "array" ; value : ASTDescribedColumnType }
40
41
| { kind : "object" ; value : [ string , ASTDescribedColumnType ] [ ] }
41
- | { kind : "type" ; value : string }
42
+ | { kind : "type" ; value : string ; type : string }
42
43
| { kind : "literal" ; value : string ; base : ASTDescribedColumnType } ;
43
44
44
45
export function getASTDescription ( params : ASTDescriptionOptions ) : Map < number , ASTDescribedColumn > {
@@ -82,20 +83,32 @@ export function getASTDescription(params: ASTDescriptionOptions): Map<number, AS
82
83
p : { oid : number ; baseOid : number | null } | { name : string } ,
83
84
) : ASTDescribedColumnType => {
84
85
if ( "name" in p ) {
85
- return { kind : "type" , value : params . typesMap . get ( p . name ) ?. value ?? "unknown" } ;
86
+ return {
87
+ kind : "type" ,
88
+ value : params . typesMap . get ( p . name ) ?. value ?? "unknown" ,
89
+ type : p . name ,
90
+ } ;
86
91
}
87
92
88
93
const typeByOid = getTypeByOid ( p . oid ) ;
89
94
90
95
if ( typeByOid . override ) {
91
- const baseType : ASTDescribedColumnType = { kind : "type" , value : typeByOid . value } ;
96
+ const baseType : ASTDescribedColumnType = {
97
+ kind : "type" ,
98
+ value : typeByOid . value ,
99
+ type : params . pgTypes . get ( p . oid ) ?. name ?? "unknown" ,
100
+ } ;
92
101
return typeByOid . isArray ? { kind : "array" , value : baseType } : baseType ;
93
102
}
94
103
95
104
const typeByBaseOid = fmap ( p . baseOid , getTypeByOid ) ;
96
105
97
106
if ( typeByBaseOid ?. override === true ) {
98
- const baseType : ASTDescribedColumnType = { kind : "type" , value : typeByBaseOid . value } ;
107
+ const baseType : ASTDescribedColumnType = {
108
+ kind : "type" ,
109
+ value : typeByBaseOid . value ,
110
+ type : params . pgTypes . get ( p . baseOid ! ) ?. name ?? "unknown" ,
111
+ } ;
99
112
return typeByBaseOid . isArray ? { kind : "array" , value : baseType } : baseType ;
100
113
}
101
114
@@ -104,13 +117,21 @@ export function getASTDescription(params: ASTDescriptionOptions): Map<number, AS
104
117
if ( enumValue !== undefined ) {
105
118
return {
106
119
kind : "union" ,
107
- value : enumValue . values . map ( ( value ) => ( { kind : "type" , value : `'${ value } '` } ) ) ,
120
+ value : enumValue . values . map ( ( value ) => ( {
121
+ kind : "type" ,
122
+ value : `'${ value } '` ,
123
+ type : enumValue . name ,
124
+ } ) ) ,
108
125
} ;
109
126
}
110
127
111
128
const { isArray, value } = typeByBaseOid ?? typeByOid ;
112
129
113
- const type : ASTDescribedColumnType = { kind : "type" , value : value } ;
130
+ const type : ASTDescribedColumnType = {
131
+ kind : "type" ,
132
+ value : value ,
133
+ type : params . pgTypes . get ( p . oid ) ?. name ?? "unknown" ,
134
+ } ;
114
135
115
136
return isArray ? { kind : "array" , value : type } : type ;
116
137
} ,
@@ -215,15 +236,81 @@ function getDescribedNode(params: {
215
236
216
237
function getDescribedAExpr ( {
217
238
alias,
239
+ node,
218
240
context,
219
241
} : GetDescribedParamsOf < LibPgQueryAST . AExpr > ) : ASTDescribedColumn [ ] {
242
+ const name = alias ?? "?column?" ;
243
+
244
+ if ( node . lexpr === undefined && node . rexpr !== undefined ) {
245
+ const described = getDescribedNode ( { alias, node : node . rexpr , context } ) . at ( 0 ) ;
246
+ const type = fmap ( described , ( x ) => getBaseType ( x . type ) ) ;
247
+
248
+ if ( type === null ) return [ ] ;
249
+
250
+ return [ { name, type } ] ;
251
+ }
252
+
253
+ if ( node . lexpr === undefined || node . rexpr === undefined ) {
254
+ return [ ] ;
255
+ }
256
+
257
+ const getResolvedNullableValueOrNull = ( node : LibPgQueryAST . Node ) => {
258
+ const column = getDescribedNode ( { alias : undefined , node, context } ) . at ( 0 ) ;
259
+
260
+ if ( column === undefined ) return null ;
261
+
262
+ if ( column . type . kind === "array" ) {
263
+ return { value : "array" , nullable : false } ;
264
+ }
265
+
266
+ if ( column . type . kind === "type" ) {
267
+ return { value : column . type . type , nullable : false } ;
268
+ }
269
+
270
+ if ( column . type . kind === "literal" && column . type . base . kind === "type" ) {
271
+ return { value : column . type . base . type , nullable : false } ;
272
+ }
273
+
274
+ if ( column . type . kind === "union" && isTuple ( column . type . value ) ) {
275
+ let nullable = false ;
276
+ let value : string | undefined = undefined ;
277
+
278
+ for ( const type of column . type . value ) {
279
+ if ( type . kind !== "type" ) return null ;
280
+ if ( type . value === "null" ) nullable = true ;
281
+ if ( type . value !== "null" ) value = type . type ;
282
+ }
283
+
284
+ if ( value === undefined ) return null ;
285
+
286
+ return { value, nullable } ;
287
+ }
288
+
289
+ return null ;
290
+ } ;
291
+
292
+ const lnode = getResolvedNullableValueOrNull ( node . lexpr ) ;
293
+ const rnode = getResolvedNullableValueOrNull ( node . rexpr ) ;
294
+
295
+ if ( lnode === null || rnode === null ) {
296
+ return [ ] ;
297
+ }
298
+
299
+ const operator = concatStringNodes ( node . name ) ;
300
+ const resolved : string | undefined =
301
+ defaultTypeExprMapping [ `${ lnode . value } ${ operator } ${ rnode . value } ` ] ;
302
+
303
+ if ( resolved === undefined ) {
304
+ return [ ] ;
305
+ }
306
+
220
307
return [
221
308
{
222
- name : alias ?? "?column?" ,
309
+ name : name ,
223
310
type : resolveType ( {
224
311
context : context ,
225
- nullable : false ,
226
- type : context . toTypeScriptType ( { name : "boolean" } ) ,
312
+ nullable : ! context . nonNullableColumns . has ( name ) && ( lnode . nullable || rnode . nullable ) ,
313
+ type : context . toTypeScriptType ( { name : resolved } ) ,
227
314
} ) ,
228
315
} ,
229
316
] ;
@@ -239,7 +326,7 @@ function getDescribedNullTest({
239
326
type : resolveType ( {
240
327
context : context ,
241
328
nullable : false ,
242
- type : context . toTypeScriptType ( { name : "boolean " } ) ,
329
+ type : context . toTypeScriptType ( { name : "bool " } ) ,
243
330
} ) ,
244
331
} ,
245
332
] ;
@@ -298,7 +385,7 @@ function getDescribedBoolExpr({
298
385
type : resolveType ( {
299
386
context : context ,
300
387
nullable : false ,
301
- type : context . toTypeScriptType ( { name : "boolean " } ) ,
388
+ type : context . toTypeScriptType ( { name : "bool " } ) ,
302
389
} ) ,
303
390
} ,
304
391
] ;
@@ -317,7 +404,7 @@ function getDescribedSubLink({
317
404
nullable : false ,
318
405
type : ( ( ) => {
319
406
if ( node . subLinkType === LibPgQueryAST . SubLinkType . EXISTS_SUBLINK ) {
320
- return context . toTypeScriptType ( { name : "boolean " } ) ;
407
+ return context . toTypeScriptType ( { name : "bool " } ) ;
321
408
}
322
409
323
410
return context . toTypeScriptType ( { name : "unknown" } ) ;
@@ -412,7 +499,7 @@ function mergeDescribedColumnTypes(types: ASTDescribedColumnType[]): ASTDescribe
412
499
413
500
if ( ! seenSymbols . has ( "boolean" ) && seenSymbols . has ( "true" ) && seenSymbols . has ( "false" ) ) {
414
501
seenSymbols . add ( "boolean" ) ;
415
- result . push ( { kind : "type" , value : "boolean" } ) ;
502
+ result . push ( { kind : "type" , value : "boolean" , type : "bool" } ) ;
416
503
}
417
504
418
505
if ( seenSymbols . has ( "boolean" ) && ( seenSymbols . has ( "true" ) || seenSymbols . has ( "false" ) ) ) {
@@ -537,15 +624,15 @@ function getDescribedFuncCallByPgFn({
537
624
538
625
const pgFnValue =
539
626
args . length === 0
540
- ? context . pgFns . get ( functionName )
627
+ ? ( context . pgFns . get ( functionName ) ?? context . pgFns . get ( ` ${ functionName } (string)` ) )
541
628
: ( context . pgFns . get ( `${ functionName } (${ args . join ( ", " ) } )` ) ??
542
629
context . pgFns . get ( `${ functionName } (any)` ) ??
543
630
context . pgFns . get ( `${ functionName } (unknown)` ) ) ;
544
631
545
632
const type = resolveType ( {
546
633
context : context ,
547
634
nullable : ! context . nonNullableColumns . has ( name ) ,
548
- type : { kind : "type" , value : pgFnValue ?? "unknown" } ,
635
+ type : { kind : "type" , value : pgFnValue ?. ts ?? "unknown" , type : pgFnValue ?. pg ?? "unknown" } ,
549
636
} ) ;
550
637
551
638
return [ { name, type } ] ;
@@ -758,7 +845,11 @@ function getDescribedColumnByResolvedColumns(params: {
758
845
?. get ( column . colName ) ;
759
846
760
847
if ( overridenType !== undefined ) {
761
- return { kind : "type" , value : overridenType } ;
848
+ return {
849
+ kind : "type" ,
850
+ value : overridenType ,
851
+ type : params . context . pgTypes . get ( column . colTypeOid ) ?. name ?? "unknown" ,
852
+ } ;
762
853
}
763
854
764
855
return params . context . toTypeScriptType ( {
@@ -789,7 +880,7 @@ function getDescribedAConst({
789
880
return {
790
881
kind : "literal" ,
791
882
value : node . boolval . boolval ? "true" : "false" ,
792
- base : context . toTypeScriptType ( { name : "boolean " } ) ,
883
+ base : context . toTypeScriptType ( { name : "bool " } ) ,
793
884
} ;
794
885
case node . bsval !== undefined :
795
886
return context . toTypeScriptType ( { name : "bytea" } ) ;
@@ -838,7 +929,7 @@ function asNonNullableType(type: ASTDescribedColumnType): ASTDescribedColumnType
838
929
) ;
839
930
840
931
if ( filtered . length === 0 ) {
841
- return { kind : "type" , value : "unknown" } ;
932
+ return { kind : "type" , value : "unknown" , type : "unknown" } ;
842
933
}
843
934
844
935
if ( filtered . length === 1 ) {
@@ -848,7 +939,7 @@ function asNonNullableType(type: ASTDescribedColumnType): ASTDescribedColumnType
848
939
return { kind : "union" , value : filtered } ;
849
940
}
850
941
case "type" :
851
- return type . value === "null" ? { kind : "type" , value : "unknown" } : type ;
942
+ return type . value === "null" ? { kind : "type" , value : "unknown" , type : "unknown" } : type ;
852
943
}
853
944
}
854
945
0 commit comments