Skip to content

Commit b11a459

Browse files
committed
Update snowflake driver, do not try downloading CSV files if there were no rows exported.
+ removed copy/paste
1 parent e23214f commit b11a459

File tree

1 file changed

+35
-40
lines changed

1 file changed

+35
-40
lines changed

packages/cubejs-snowflake-driver/src/SnowflakeDriver.ts

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -555,10 +555,13 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
555555
}`);
556556
}
557557

558-
const types = options.query
558+
const { types, exportedCount } = options.query
559559
? await this.unloadWithSql(tableName, options)
560560
: await this.unloadWithTable(tableName, options);
561-
const csvFile = await this.getCsvFiles(tableName);
561+
// Snowflake doesn't produce csv files if no data is exported (no data rows)
562+
// so it's important not to call getCsvFiles(), because it checks for empty files list
563+
// and throws an error.
564+
const csvFile = exportedCount > 0 ? await this.getCsvFiles(tableName) : [];
562565

563566
return {
564567
exportBucketCsvEscapeSymbol: this.config.exportBucketCsvEscapeSymbol,
@@ -568,37 +571,42 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
568571
};
569572
}
570573

574+
private buildBucketUrl(tableName: string): string {
575+
const { bucketType } = <SnowflakeDriverExportBucket> this.config.exportBucket;
576+
577+
let bucketName: string;
578+
let exportPrefix: string;
579+
let path: string;
580+
581+
if (bucketType === 'azure') {
582+
({ bucketName, path } = this.parseBucketUrl(this.config.exportBucket!.bucketName));
583+
const pathArr = path.split('/');
584+
bucketName = `${bucketName}/${pathArr[0]}`;
585+
exportPrefix = pathArr.length > 1 ? `${pathArr.slice(1).join('/')}/${tableName}` : tableName;
586+
} else {
587+
({ bucketName, path } = this.parseBucketUrl(this.config.exportBucket!.bucketName));
588+
exportPrefix = path ? `${path}/${tableName}` : tableName;
589+
}
590+
591+
return `${bucketType}://${bucketName}/${exportPrefix}/`;
592+
}
593+
571594
/**
572595
* Unload data from a SQL query to an export bucket.
573596
*/
574597
private async unloadWithSql(
575598
tableName: string,
576599
options: UnloadOptions,
577-
): Promise<TableStructure> {
600+
): Promise<{ types: TableStructure, exportedCount: number }> {
578601
if (!options.query) {
579602
throw new Error('Unload query is missed.');
580603
} else {
581604
const types = await this.queryColumnTypes(options.query.sql, options.query.params);
582605
const connection = await this.getConnection();
583-
const { bucketType } =
584-
<SnowflakeDriverExportBucket> this.config.exportBucket;
585-
586-
let bucketName: string;
587-
let exportPrefix: string;
588-
let path: string;
589-
590-
if (bucketType === 'azure') {
591-
({ bucketName, path } = this.parseBucketUrl(this.config.exportBucket!.bucketName));
592-
const pathArr = path.split('/');
593-
bucketName = `${bucketName}/${pathArr[0]}`;
594-
exportPrefix = pathArr.length > 1 ? `${pathArr.slice(1).join('/')}/${tableName}` : tableName;
595-
} else {
596-
({ bucketName, path } = this.parseBucketUrl(this.config.exportBucket!.bucketName));
597-
exportPrefix = path ? `${path}/${tableName}` : tableName;
598-
}
606+
const bucketUrl = this.buildBucketUrl(tableName);
599607

600608
const unloadSql = `
601-
COPY INTO '${bucketType}://${bucketName}/${exportPrefix}/'
609+
COPY INTO '${bucketUrl}'
602610
FROM (${options.query.sql})
603611
${this.exportOptionsClause(options)}`;
604612
const result = await this.execute<UnloadResponse[]>(
@@ -610,7 +618,7 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
610618
if (!result) {
611619
throw new Error('Missing `COPY INTO` query result.');
612620
}
613-
return types;
621+
return { types, exportedCount: parseInt(result[0].rows_unloaded, 10) };
614622
}
615623
}
616624

@@ -641,28 +649,13 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
641649
private async unloadWithTable(
642650
tableName: string,
643651
options: UnloadOptions,
644-
): Promise<TableStructure> {
652+
): Promise<{ types: TableStructure, exportedCount: number }> {
645653
const types = await this.tableColumnTypes(tableName);
646654
const connection = await this.getConnection();
647-
const { bucketType } =
648-
<SnowflakeDriverExportBucket> this.config.exportBucket;
649-
650-
let bucketName: string;
651-
let exportPrefix: string;
652-
let path: string;
653-
654-
if (bucketType === 'azure') {
655-
({ bucketName, path } = this.parseBucketUrl(this.config.exportBucket!.bucketName));
656-
const pathArr = path.split('/');
657-
bucketName = `${bucketName}/${pathArr[0]}`;
658-
exportPrefix = pathArr.length > 1 ? `${pathArr.slice(1).join('/')}/${tableName}` : tableName;
659-
} else {
660-
({ bucketName, path } = this.parseBucketUrl(this.config.exportBucket!.bucketName));
661-
exportPrefix = path ? `${path}/${tableName}` : tableName;
662-
}
655+
const bucketUrl = this.buildBucketUrl(tableName);
663656

664657
const unloadSql = `
665-
COPY INTO '${bucketType}://${bucketName}/${exportPrefix}/'
658+
COPY INTO '${bucketUrl}'
666659
FROM ${tableName}
667660
${this.exportOptionsClause(options)}`;
668661
const result = await this.execute<UnloadResponse[]>(
@@ -671,10 +664,12 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
671664
[],
672665
false,
673666
);
667+
674668
if (!result) {
675669
throw new Error('Missing `COPY INTO` query result.');
676670
}
677-
return types;
671+
672+
return { types, exportedCount: parseInt(result[0].rows_unloaded, 10) };
678673
}
679674

680675
/**

0 commit comments

Comments
 (0)