@@ -201,9 +201,12 @@ export class V13ToV14Transformer {
201201
202202 if ( isInCreateDomainContext ) {
203203 funcname = funcname . slice ( 1 ) ;
204- } else if ( ( isInSelectTargetContext || this . isInReturningContext ( context ) ) && functionName === 'substring' ) {
205- const hasThreeArgs = node . args && Array . isArray ( node . args ) && node . args . length === 3 ;
206- if ( hasThreeArgs ) {
204+ } else if ( ( isInSelectTargetContext || this . isInReturningContext ( context ) || isInCallStmtContext ) && functionName === 'substring' ) {
205+ // For substring functions in SELECT contexts, remove pg_catalog prefix for function call syntax
206+ // Function call syntax: substring(string, start, length) - 3 args with simple types
207+ // SQL syntax: SUBSTRING(string FROM start FOR length) - 3 args but with special FROM/FOR structure
208+ const isFunctionCallSyntax = this . isStandardFunctionCallSyntax ( node , context ) ;
209+ if ( isFunctionCallSyntax ) {
207210 funcname = funcname . slice ( 1 ) ;
208211 }
209212 }
@@ -505,14 +508,28 @@ export class V13ToV14Transformer {
505508 return true ;
506509 }
507510
508- if ( node . args . length === 2 ) {
509- return false ; // FROM syntax
510- }
511-
512- if ( node . args . length === 3 ) {
513- const thirdArg = node . args [ 2 ] ;
514- if ( thirdArg && typeof thirdArg === 'object' && 'TypeCast' in thirdArg ) {
515- return false ; // FOR syntax with length cast
511+ // For substring function, detect SQL syntax patterns
512+ const funcname = node . funcname || [ ] ;
513+ const functionName = funcname [ funcname . length - 1 ] ?. String ?. str ;
514+
515+ if ( functionName === 'substring' ) {
516+ // SQL syntax patterns:
517+ // 2. SUBSTRING(string FROM position FOR length) - 3 args with simple types
518+ // Function call syntax:
519+
520+ if ( node . args . length === 2 ) {
521+ return false ; // SQL syntax: FROM only
522+ }
523+
524+ if ( node . args . length === 3 ) {
525+ const firstArg = node . args [ 0 ] ;
526+ // If first argument is complex (TypeCast, FuncCall), it's likely function call syntax
527+ if ( firstArg && typeof firstArg === 'object' && ( 'TypeCast' in firstArg || 'FuncCall' in firstArg ) ) {
528+ return true ; // Function call syntax
529+ }
530+ if ( firstArg && typeof firstArg === 'object' && 'ColumnRef' in firstArg ) {
531+ return false ; // SQL syntax: FROM...FOR
532+ }
516533 }
517534 }
518535
0 commit comments