11import type { MigrationOptions } from '../../migrationOptions' ;
2+ import { isPgLiteral } from '../../utils' ;
23import type { Literal } from '../../utils/createTransformer' ;
3- import type { Name } from '../generalTypes' ;
4+ import { isNameObject , isSchemaNameObject , type Name } from '../generalTypes' ;
45import type { CreateIndexOptions } from './createIndex' ;
56import type { DropIndexOptions } from './dropIndex' ;
67
@@ -12,24 +13,44 @@ export interface IndexColumn {
1213 sort ?: 'ASC' | 'DESC' ;
1314}
1415
16+ function isIndexColumn ( value : unknown ) : value is IndexColumn {
17+ return (
18+ typeof value === 'object' &&
19+ value !== null &&
20+ 'name' in value &&
21+ typeof ( value as { name ?: unknown } ) . name === 'string'
22+ ) ;
23+ }
24+
1525export function generateIndexName (
1626 table : Name ,
1727 columns : Array < string | IndexColumn > ,
1828 options : CreateIndexOptions | DropIndexOptions ,
1929 schemalize : Literal
2030) : Name {
2131 if ( options . name ) {
22- return typeof table === 'object'
32+ return isSchemaNameObject ( table )
2333 ? { schema : table . schema , name : options . name }
2434 : options . name ;
2535 }
2636
2737 const cols = columns
28- . map ( ( col ) => schemalize ( typeof col === 'string' ? col : col . name ) )
38+ . map ( ( col , idx ) => {
39+ if ( isIndexColumn ( col ) ) return schemalize ( col . name ) ;
40+
41+ if ( isPgLiteral ( col ) ) {
42+ const literalValue = 'value' in col ? col . value : String ( col ) ;
43+ throw new Error (
44+ `Index name must be provided when using PgLiteral columns (column #${ idx + 1 } : ${ literalValue } )`
45+ ) ;
46+ }
47+
48+ return schemalize ( col ) ;
49+ } )
2950 . join ( '_' ) ;
3051 const uniq = 'unique' in options && options . unique ? '_unique' : '' ;
3152
32- return typeof table === 'object'
53+ return isNameObject ( table )
3354 ? {
3455 schema : table . schema ,
3556 name : `${ table . name } _${ cols } ${ uniq } _index` ,
@@ -41,29 +62,39 @@ export function generateColumnString(
4162 column : Name ,
4263 mOptions : MigrationOptions
4364) : string {
65+ if ( isPgLiteral ( column ) ) {
66+ return column . toString ( ) ;
67+ }
68+
4469 const name = mOptions . schemalize ( column ) ;
45- const isSpecial = / [ ( ) . ] / . test ( name ) ;
70+ const isExpression = / [ ^ \w " . ] / . test ( name ) ;
71+ if ( ! isExpression ) {
72+ return mOptions . literal ( name ) ;
73+ }
4674
47- return isSpecial
48- ? name // expression
49- : mOptions . literal ( name ) ; // single column
75+ // Expressions need parentheses in index definitions, unless they're already
76+ // wrapped (we consider any expression ending with ')' as already wrapped).
77+ const alreadyWrapped = / \) $ / . test ( name ) ;
78+ return alreadyWrapped ? name : `(${ name } )` ;
5079}
5180
5281export function generateColumnsString (
5382 columns : Array < string | IndexColumn > ,
5483 mOptions : MigrationOptions
5584) : string {
5685 return columns
57- . map ( ( column ) =>
58- typeof column === 'string'
59- ? generateColumnString ( column , mOptions )
60- : [
61- generateColumnString ( column . name , mOptions ) ,
62- column . opclass ? mOptions . literal ( column . opclass ) : undefined ,
63- column . sort ,
64- ]
65- . filter ( ( s ) => typeof s === 'string' && s !== '' )
66- . join ( ' ' )
67- )
86+ . map ( ( column ) => {
87+ if ( typeof column === 'string' || isPgLiteral ( column ) ) {
88+ return generateColumnString ( column as unknown as Name , mOptions ) ;
89+ }
90+
91+ return [
92+ generateColumnString ( column . name , mOptions ) ,
93+ column . opclass ? mOptions . literal ( column . opclass ) : undefined ,
94+ column . sort ,
95+ ]
96+ . filter ( ( s ) => typeof s === 'string' && s !== '' )
97+ . join ( ' ' ) ;
98+ } )
6899 . join ( ', ' ) ;
69100}
0 commit comments