@@ -163,6 +163,18 @@ export class PLpgSQLDeparser {
163163
164164 const parts : string [ ] = [ ] ;
165165
166+ // Extract label from action block - it should come before DECLARE
167+ // In PL/pgSQL, the syntax is: <<label>> DECLARE ... BEGIN ... END label
168+ let blockLabel : string | undefined ;
169+ if ( func . action && 'PLpgSQL_stmt_block' in func . action ) {
170+ blockLabel = func . action . PLpgSQL_stmt_block . label ;
171+ }
172+
173+ // Output label before DECLARE if present
174+ if ( blockLabel ) {
175+ parts . push ( `<<${ blockLabel } >>` ) ;
176+ }
177+
166178 // Collect loop-introduced variables before generating DECLARE section
167179 const loopVarLinenos = new Set < number > ( ) ;
168180 if ( func . action ) {
@@ -176,8 +188,9 @@ export class PLpgSQLDeparser {
176188 }
177189
178190 // Deparse the action block (BEGIN...END)
191+ // Pass skipLabel=true since we already output the label
179192 if ( func . action ) {
180- parts . push ( this . deparseStmt ( func . action , context ) ) ;
193+ parts . push ( this . deparseStmt ( func . action , context , blockLabel ? true : false ) ) ;
181194 }
182195
183196 return parts . join ( this . options . newline ) ;
@@ -417,6 +430,14 @@ export class PLpgSQLDeparser {
417430 parts . push ( kw ( 'CONSTANT' ) ) ;
418431 }
419432
433+ // Handle cursor declarations - don't output the type for cursors
434+ // The syntax is: cursor_name CURSOR FOR query
435+ if ( v . cursor_explicit_expr ) {
436+ parts . push ( kw ( 'CURSOR FOR' ) ) ;
437+ parts . push ( this . deparseExpr ( v . cursor_explicit_expr ) ) ;
438+ return parts . join ( ' ' ) ;
439+ }
440+
420441 if ( v . datatype ) {
421442 parts . push ( this . deparseType ( v . datatype ) ) ;
422443 }
@@ -430,12 +451,6 @@ export class PLpgSQLDeparser {
430451 parts . push ( this . deparseExpr ( v . default_val ) ) ;
431452 }
432453
433- // Handle cursor declarations
434- if ( v . cursor_explicit_expr ) {
435- parts . push ( kw ( 'CURSOR FOR' ) ) ;
436- parts . push ( this . deparseExpr ( v . cursor_explicit_expr ) ) ;
437- }
438-
439454 return parts . join ( ' ' ) ;
440455 }
441456
@@ -484,14 +499,15 @@ export class PLpgSQLDeparser {
484499
485500 /**
486501 * Deparse a statement node
502+ * @param skipLabel - If true, skip outputting the label (used when label is output before DECLARE)
487503 */
488- private deparseStmt ( stmt : PLpgSQLStmtNode , context : PLpgSQLDeparserContext ) : string {
504+ private deparseStmt ( stmt : PLpgSQLStmtNode , context : PLpgSQLDeparserContext , skipLabel ?: boolean ) : string {
489505 const nodeType = Object . keys ( stmt ) [ 0 ] ;
490506 const nodeData = ( stmt as any ) [ nodeType ] ;
491507
492508 switch ( nodeType ) {
493509 case 'PLpgSQL_stmt_block' :
494- return this . deparseBlock ( nodeData , context ) ;
510+ return this . deparseBlock ( nodeData , context , skipLabel ) ;
495511 case 'PLpgSQL_stmt_assign' :
496512 return this . deparseAssign ( nodeData , context ) ;
497513 case 'PLpgSQL_stmt_if' :
@@ -553,13 +569,14 @@ export class PLpgSQLDeparser {
553569
554570 /**
555571 * Deparse a block statement (BEGIN...END)
572+ * @param skipLabel - If true, skip outputting the label (used when label is output before DECLARE)
556573 */
557- private deparseBlock ( block : PLpgSQL_stmt_block , context : PLpgSQLDeparserContext ) : string {
574+ private deparseBlock ( block : PLpgSQL_stmt_block , context : PLpgSQLDeparserContext , skipLabel ?: boolean ) : string {
558575 const kw = this . keyword ;
559576 const parts : string [ ] = [ ] ;
560577
561- // Label
562- if ( block . label ) {
578+ // Label - skip if already output before DECLARE
579+ if ( block . label && ! skipLabel ) {
563580 parts . push ( `<<${ block . label } >>` ) ;
564581 }
565582
@@ -1558,13 +1575,20 @@ export class PLpgSQLDeparser {
15581575 if ( 'PLpgSQL_row' in datum ) {
15591576 const row = datum . PLpgSQL_row ;
15601577 // If this is an "(unnamed row)" with fields, expand the fields to get actual variable names
1561- if ( row . refname === '(unnamed row)' && row . fields && row . fields . length > 0 && context ?. datums ) {
1578+ if ( row . refname === '(unnamed row)' && row . fields && row . fields . length > 0 ) {
15621579 const fieldNames = row . fields . map ( field => {
1580+ // If the field name contains a dot (qualified reference like lbl.a), use it directly
1581+ // This preserves block-qualified variable references
1582+ if ( field . name && field . name . includes ( '.' ) ) {
1583+ return field . name ;
1584+ }
15631585 // Try to resolve the varno to get the actual variable name
1564- const fieldDatum = context . datums [ field . varno ] ;
1565- if ( fieldDatum ) {
1566- // Recursively get the name, passing context to resolve parent records
1567- return this . deparseDatumName ( fieldDatum , context ) ;
1586+ if ( context ?. datums ) {
1587+ const fieldDatum = context . datums [ field . varno ] ;
1588+ if ( fieldDatum ) {
1589+ // Recursively get the name, passing context to resolve parent records
1590+ return this . deparseDatumName ( fieldDatum , context ) ;
1591+ }
15681592 }
15691593 // Fall back to the field name if we can't resolve the varno
15701594 return field . name ;
0 commit comments