Skip to content

Commit cdadb85

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

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
@@ -575,10 +575,13 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
575575
}`);
576576
}
577577

578-
const types = options.query
578+
const { types, exportedCount } = options.query
579579
? await this.unloadWithSql(tableName, options)
580580
: 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) : [];
582585

583586
return {
584587
exportBucketCsvEscapeSymbol: this.config.exportBucketCsvEscapeSymbol,
@@ -588,37 +591,42 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
588591
};
589592
}
590593

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+
591614
/**
592615
* Unload data from a SQL query to an export bucket.
593616
*/
594617
private async unloadWithSql(
595618
tableName: string,
596619
options: UnloadOptions,
597-
): Promise<TableStructure> {
620+
): Promise<{ types: TableStructure, exportedCount: number }> {
598621
if (!options.query) {
599622
throw new Error('Unload query is missed.');
600623
} else {
601624
const types = await this.queryColumnTypes(options.query.sql, options.query.params);
602625
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);
619627

620628
const unloadSql = `
621-
COPY INTO '${bucketType}://${bucketName}/${exportPrefix}/'
629+
COPY INTO '${bucketUrl}'
622630
FROM (${options.query.sql})
623631
${this.exportOptionsClause(options)}`;
624632
const result = await this.execute<UnloadResponse[]>(
@@ -630,7 +638,7 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
630638
if (!result) {
631639
throw new Error('Missing `COPY INTO` query result.');
632640
}
633-
return types;
641+
return { types, exportedCount: parseInt(result[0].rows_unloaded, 10) };
634642
}
635643
}
636644

@@ -661,28 +669,13 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
661669
private async unloadWithTable(
662670
tableName: string,
663671
options: UnloadOptions,
664-
): Promise<TableStructure> {
672+
): Promise<{ types: TableStructure, exportedCount: number }> {
665673
const types = await this.tableColumnTypes(tableName);
666674
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);
683676

684677
const unloadSql = `
685-
COPY INTO '${bucketType}://${bucketName}/${exportPrefix}/'
678+
COPY INTO '${bucketUrl}'
686679
FROM ${tableName}
687680
${this.exportOptionsClause(options)}`;
688681
const result = await this.execute<UnloadResponse[]>(
@@ -691,10 +684,12 @@ export class SnowflakeDriver extends BaseDriver implements DriverInterface {
691684
[],
692685
false,
693686
);
687+
694688
if (!result) {
695689
throw new Error('Missing `COPY INTO` query result.');
696690
}
697-
return types;
691+
692+
return { types, exportedCount: parseInt(result[0].rows_unloaded, 10) };
698693
}
699694

700695
/**

0 commit comments

Comments
 (0)