@@ -575,10 +575,13 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
575
575
} `) ;
576
576
}
577
577
578
- const types = options . query
578
+ const { types, exportedCount } = options . query
579
579
? await this . unloadWithSql ( tableName , options )
580
580
: await this . unloadWithTable ( tableName , options ) ;
581
- const csvFile = await this . getCsvFiles ( tableName ) ;
581
+ // Snowflake doesn't produce csv files if no data is exported (no data rows)
582
+ // so it's important not to call getCsvFiles(), because it checks for empty files list
583
+ // and throws an error.
584
+ const csvFile = exportedCount > 0 ? await this . getCsvFiles ( tableName ) : [ ] ;
582
585
583
586
return {
584
587
exportBucketCsvEscapeSymbol : this . config . exportBucketCsvEscapeSymbol ,
@@ -588,37 +591,42 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
588
591
} ;
589
592
}
590
593
594
+ private buildBucketUrl ( tableName : string ) : string {
595
+ const { bucketType } = < SnowflakeDriverExportBucket > this . config . exportBucket ;
596
+
597
+ let bucketName : string ;
598
+ let exportPrefix : string ;
599
+ let path : string ;
600
+
601
+ if ( bucketType === 'azure' ) {
602
+ ( { bucketName, path } = this . parseBucketUrl ( this . config . exportBucket ! . bucketName ) ) ;
603
+ const pathArr = path . split ( '/' ) ;
604
+ bucketName = `${ bucketName } /${ pathArr [ 0 ] } ` ;
605
+ exportPrefix = pathArr . length > 1 ? `${ pathArr . slice ( 1 ) . join ( '/' ) } /${ tableName } ` : tableName ;
606
+ } else {
607
+ ( { bucketName, path } = this . parseBucketUrl ( this . config . exportBucket ! . bucketName ) ) ;
608
+ exportPrefix = path ? `${ path } /${ tableName } ` : tableName ;
609
+ }
610
+
611
+ return `${ bucketType } ://${ bucketName } /${ exportPrefix } /` ;
612
+ }
613
+
591
614
/**
592
615
* Unload data from a SQL query to an export bucket.
593
616
*/
594
617
private async unloadWithSql (
595
618
tableName : string ,
596
619
options : UnloadOptions ,
597
- ) : Promise < TableStructure > {
620
+ ) : Promise < { types : TableStructure , exportedCount : number } > {
598
621
if ( ! options . query ) {
599
622
throw new Error ( 'Unload query is missed.' ) ;
600
623
} else {
601
624
const types = await this . queryColumnTypes ( options . query . sql , options . query . params ) ;
602
625
const connection = await this . getConnection ( ) ;
603
- const { bucketType } =
604
- < SnowflakeDriverExportBucket > this . config . exportBucket ;
605
-
606
- let bucketName : string ;
607
- let exportPrefix : string ;
608
- let path : string ;
609
-
610
- if ( bucketType === 'azure' ) {
611
- ( { bucketName, path } = this . parseBucketUrl ( this . config . exportBucket ! . bucketName ) ) ;
612
- const pathArr = path . split ( '/' ) ;
613
- bucketName = `${ bucketName } /${ pathArr [ 0 ] } ` ;
614
- exportPrefix = pathArr . length > 1 ? `${ pathArr . slice ( 1 ) . join ( '/' ) } /${ tableName } ` : tableName ;
615
- } else {
616
- ( { bucketName, path } = this . parseBucketUrl ( this . config . exportBucket ! . bucketName ) ) ;
617
- exportPrefix = path ? `${ path } /${ tableName } ` : tableName ;
618
- }
626
+ const bucketUrl = this . buildBucketUrl ( tableName ) ;
619
627
620
628
const unloadSql = `
621
- COPY INTO '${ bucketType } :// ${ bucketName } / ${ exportPrefix } / '
629
+ COPY INTO '${ bucketUrl } '
622
630
FROM (${ options . query . sql } )
623
631
${ this . exportOptionsClause ( options ) } ` ;
624
632
const result = await this . execute < UnloadResponse [ ] > (
@@ -630,7 +638,7 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
630
638
if ( ! result ) {
631
639
throw new Error ( 'Missing `COPY INTO` query result.' ) ;
632
640
}
633
- return types ;
641
+ return { types, exportedCount : parseInt ( result [ 0 ] . rows_unloaded , 10 ) } ;
634
642
}
635
643
}
636
644
@@ -661,28 +669,13 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
661
669
private async unloadWithTable (
662
670
tableName : string ,
663
671
options : UnloadOptions ,
664
- ) : Promise < TableStructure > {
672
+ ) : Promise < { types : TableStructure , exportedCount : number } > {
665
673
const types = await this . tableColumnTypes ( tableName ) ;
666
674
const connection = await this . getConnection ( ) ;
667
- const { bucketType } =
668
- < SnowflakeDriverExportBucket > this . config . exportBucket ;
669
-
670
- let bucketName : string ;
671
- let exportPrefix : string ;
672
- let path : string ;
673
-
674
- if ( bucketType === 'azure' ) {
675
- ( { bucketName, path } = this . parseBucketUrl ( this . config . exportBucket ! . bucketName ) ) ;
676
- const pathArr = path . split ( '/' ) ;
677
- bucketName = `${ bucketName } /${ pathArr [ 0 ] } ` ;
678
- exportPrefix = pathArr . length > 1 ? `${ pathArr . slice ( 1 ) . join ( '/' ) } /${ tableName } ` : tableName ;
679
- } else {
680
- ( { bucketName, path } = this . parseBucketUrl ( this . config . exportBucket ! . bucketName ) ) ;
681
- exportPrefix = path ? `${ path } /${ tableName } ` : tableName ;
682
- }
675
+ const bucketUrl = this . buildBucketUrl ( tableName ) ;
683
676
684
677
const unloadSql = `
685
- COPY INTO '${ bucketType } :// ${ bucketName } / ${ exportPrefix } / '
678
+ COPY INTO '${ bucketUrl } '
686
679
FROM ${ tableName }
687
680
${ this . exportOptionsClause ( options ) } ` ;
688
681
const result = await this . execute < UnloadResponse [ ] > (
@@ -691,10 +684,12 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
691
684
[ ] ,
692
685
false ,
693
686
) ;
687
+
694
688
if ( ! result ) {
695
689
throw new Error ( 'Missing `COPY INTO` query result.' ) ;
696
690
}
697
- return types ;
691
+
692
+ return { types, exportedCount : parseInt ( result [ 0 ] . rows_unloaded , 10 ) } ;
698
693
}
699
694
700
695
/**
0 commit comments