Skip to content

Commit 1f989ba

Browse files
authored
Add support for Snowflake secure views. (#1017)
1 parent ccb09da commit 1f989ba

File tree

9 files changed

+98
-49
lines changed

9 files changed

+98
-49
lines changed

core/adapters/bigquery.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ export class BigQueryAdapter extends Adapter implements IAdapter {
8080
return tasks;
8181
}
8282

83-
public createOrReplace(table: dataform.ITable) {
83+
public dropIfExists(target: dataform.ITarget, type: dataform.TableMetadata.Type) {
84+
return `drop ${this.tableTypeAsSql(type)} if exists ${this.resolveTarget(target)}`;
85+
}
86+
87+
private createOrReplace(table: dataform.ITable) {
8488
return `create or replace ${this.tableTypeAsSql(
8589
this.baseTableType(table.type)
8690
)} ${this.resolveTarget(table.target)} ${
@@ -94,16 +98,12 @@ export class BigQueryAdapter extends Adapter implements IAdapter {
9498
}as ${table.query}`;
9599
}
96100

97-
public createOrReplaceView(target: dataform.ITarget, query: string) {
101+
private createOrReplaceView(target: dataform.ITarget, query: string) {
98102
return `
99103
create or replace view ${this.resolveTarget(target)} as ${query}`;
100104
}
101105

102-
public dropIfExists(target: dataform.ITarget, type: dataform.TableMetadata.Type) {
103-
return `drop ${this.tableTypeAsSql(type)} if exists ${this.resolveTarget(target)}`;
104-
}
105-
106-
public mergeInto(
106+
private mergeInto(
107107
target: dataform.ITarget,
108108
columns: string[],
109109
query: string,

core/adapters/redshift.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export class RedshiftAdapter extends Adapter implements IAdapter {
3838
table.uniqueKey && table.uniqueKey.length > 0
3939
? this.mergeInto(
4040
table.target,
41-
tableMetadata.fields.map(f => f.name),
4241
this.where(table.incrementalQuery || table.query, table.where),
4342
table.uniqueKey
4443
)
@@ -78,7 +77,15 @@ export class RedshiftAdapter extends Adapter implements IAdapter {
7877
.add(Task.assertion(`select sum(1) as row_count from ${this.resolveTarget(target)}`));
7978
}
8079

81-
public createOrReplaceView(target: dataform.ITarget, query: string, bind: boolean) {
80+
public dropIfExists(target: dataform.ITarget, type: dataform.TableMetadata.Type) {
81+
const query = `drop ${this.tableTypeAsSql(type)} if exists ${this.resolveTarget(target)}`;
82+
if (!this.isBindSupported()) {
83+
return query;
84+
}
85+
return `${query} cascade`;
86+
}
87+
88+
private createOrReplaceView(target: dataform.ITarget, query: string, bind: boolean) {
8289
const createQuery = `create or replace view ${this.resolveTarget(target)} as ${query}`;
8390
// Postgres doesn't support with no schema binding.
8491
if (bind || this.project.warehouse === "postgres") {
@@ -87,7 +94,7 @@ export class RedshiftAdapter extends Adapter implements IAdapter {
8794
return `${createQuery} with no schema binding`;
8895
}
8996

90-
public createOrReplace(table: dataform.ITable) {
97+
private createOrReplace(table: dataform.ITable) {
9198
if (table.type === "view") {
9299
const isBindDefined = table.redshift && table.redshift.hasOwnProperty("bind");
93100
const bindDefaultValue = semver.gte(this.dataformCoreVersion, "1.4.1") ? false : true;
@@ -116,7 +123,7 @@ export class RedshiftAdapter extends Adapter implements IAdapter {
116123
);
117124
}
118125

119-
public createTable(table: dataform.ITable, target: dataform.ITarget) {
126+
private createTable(table: dataform.ITable, target: dataform.ITarget) {
120127
if (table.redshift) {
121128
let query = `create table ${this.resolveTarget(target)}`;
122129

@@ -135,20 +142,7 @@ export class RedshiftAdapter extends Adapter implements IAdapter {
135142
return `create table ${this.resolveTarget(target)} as ${table.query}`;
136143
}
137144

138-
public dropIfExists(target: dataform.ITarget, type: dataform.TableMetadata.Type) {
139-
const query = `drop ${this.tableTypeAsSql(type)} if exists ${this.resolveTarget(target)}`;
140-
if (!this.isBindSupported()) {
141-
return query;
142-
}
143-
return `${query} cascade`;
144-
}
145-
146-
public mergeInto(
147-
target: dataform.ITarget,
148-
columns: string[],
149-
query: string,
150-
uniqueKey: string[]
151-
) {
145+
private mergeInto(target: dataform.ITarget, query: string, uniqueKey: string[]) {
152146
const finalTarget = this.resolveTarget(target);
153147
// Schema name not allowed for temporary tables.
154148
const tempTarget = `"${target.schema}__${target.name}_incremental_temp"`;

core/adapters/snowflake.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,25 @@ export class SnowflakeAdapter extends Adapter implements IAdapter {
7373
schema: projectConfig.assertionSchema,
7474
name: assertion.name
7575
});
76-
tasks.add(Task.statement(this.createOrReplaceView(target, assertion.query)));
76+
tasks.add(Task.statement(this.createOrReplaceView(target, assertion.query, false)));
7777
tasks.add(Task.assertion(`select sum(1) as row_count from ${this.resolveTarget(target)}`));
7878
return tasks;
7979
}
8080

81-
public createOrReplaceView(target: dataform.ITarget, query: string) {
82-
return `
83-
create or replace view ${this.resolveTarget(target)} as ${query}`;
81+
private createOrReplaceView(target: dataform.ITarget, query: string, secure: boolean) {
82+
return `create or replace ${secure ? "secure " : ""}view ${this.resolveTarget(
83+
target
84+
)} as ${query}`;
8485
}
8586

86-
public createOrReplace(table: dataform.ITable) {
87-
return `create or replace ${this.tableTypeAsSql(
88-
this.baseTableType(table.type || "table")
89-
)} ${this.resolveTarget(table.target)} as ${table.query}`;
87+
private createOrReplace(table: dataform.ITable) {
88+
if (table.type === "view") {
89+
return this.createOrReplaceView(table.target, table.query, table.snowflake?.secure);
90+
}
91+
return `create or replace table ${this.resolveTarget(table.target)} as ${table.query}`;
9092
}
9193

92-
public mergeInto(
94+
private mergeInto(
9395
target: dataform.ITarget,
9496
columns: string[],
9597
query: string,

core/adapters/sqldatawarehouse.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,16 @@ export class SQLDataWarehouseAdapter extends Adapter implements IAdapter {
9090
)}','U') is not null drop table ${this.resolveTarget(target)}`;
9191
}
9292

93-
public createOrReplace(table: dataform.ITable, alreadyExists: boolean) {
93+
public insertInto(target: dataform.ITarget, columns: string[], query: string) {
94+
return `
95+
insert into ${this.resolveTarget(target)}
96+
(${columns.join(",")})
97+
select ${columns.join(",")}
98+
from (${query}
99+
) as insertions`;
100+
}
101+
102+
private createOrReplace(table: dataform.ITable, alreadyExists: boolean) {
94103
if (table.type === "view") {
95104
return Tasks.create().add(
96105
Task.statement(
@@ -116,7 +125,7 @@ export class SQLDataWarehouseAdapter extends Adapter implements IAdapter {
116125
);
117126
}
118127

119-
public createTable(table: dataform.ITable, target: dataform.ITarget) {
128+
private createTable(table: dataform.ITable, target: dataform.ITarget) {
120129
const distribution =
121130
table.sqlDataWarehouse && table.sqlDataWarehouse.distribution
122131
? table.sqlDataWarehouse.distribution
@@ -127,13 +136,4 @@ export class SQLDataWarehouseAdapter extends Adapter implements IAdapter {
127136
)
128137
as ${table.query}`;
129138
}
130-
131-
public insertInto(target: dataform.ITarget, columns: string[], query: string) {
132-
return `
133-
insert into ${this.resolveTarget(target)}
134-
(${columns.join(",")})
135-
select ${columns.join(",")}
136-
from (${query}
137-
) as insertions`;
138-
}
139139
}

core/session.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,17 @@ export class Session {
572572
);
573573
}
574574

575+
// snowflake config
576+
if (!!table.snowflake) {
577+
if (table.snowflake.secure && table.type !== "view") {
578+
this.compileError(
579+
new Error(`The 'secure' option is only valid for Snowflake views`),
580+
table.fileName,
581+
table.name
582+
);
583+
}
584+
}
585+
575586
// sqldatawarehouse config
576587
if (!!table.sqlDataWarehouse) {
577588
if (!!table.uniqueKey && table.uniqueKey.length > 0) {

core/table.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const SortStyleType = ["compound", "interleaved"] as const;
5959
export type SortStyleType = typeof SortStyleType[number];
6060

6161
/**
62-
* Redshift specific warehouse options.
62+
* Redshift-specific warehouse options.
6363
*/
6464
export interface IRedshiftOptions {
6565
/**
@@ -95,7 +95,20 @@ const IRedshiftOptionsProperties = () =>
9595
strictKeysOf<IRedshiftOptions>()(["distKey", "distStyle", "sortKeys", "sortStyle"]);
9696

9797
/**
98-
* Options for creating tables within Azure SQL Data Warehouse projects.
98+
* Snowflake-specific warehouse options.
99+
*/
100+
export interface ISnowflakeOptions {
101+
/**
102+
* If set to true, a secure view will be created.
103+
*
104+
* For more information, read the [Snowflake Secure Views docs](https://docs.snowflake.com/en/user-guide/views-secure.html).
105+
*/
106+
secure?: boolean;
107+
}
108+
const ISnowflakeOptionsProperties = () => strictKeysOf<ISnowflakeOptions>()(["secure"]);
109+
110+
/**
111+
* Azure SQL Data Warehouse-specific warehouse options.
99112
*/
100113
export interface ISQLDataWarehouseOptions {
101114
/**
@@ -109,7 +122,7 @@ const ISQLDataWarehouseOptionsProperties = () =>
109122
strictKeysOf<ISQLDataWarehouseOptions>()(["distribution"]);
110123

111124
/**
112-
* Options for creating tables within BigQuery projects.
125+
* BigQuery-specific warehouse options.
113126
*/
114127
export interface IBigQueryOptions {
115128
/**
@@ -206,6 +219,11 @@ export interface ITableConfig
206219
*/
207220
bigquery?: IBigQueryOptions;
208221

222+
/**
223+
* Snowflake-specific options.
224+
*/
225+
snowflake?: ISnowflakeOptions;
226+
209227
/**
210228
* Azure SQL Data Warehouse-specific options.
211229
*/
@@ -235,6 +253,7 @@ export const ITableConfigProperties = () =>
235253
"name",
236254
"redshift",
237255
"bigquery",
256+
"snowflake",
238257
"sqldatawarehouse",
239258
"tags",
240259
"uniqueKey",
@@ -274,6 +293,7 @@ export class Table {
274293
inline: [
275294
"bigquery",
276295
"redshift",
296+
"snowflake",
277297
"sqlDataWarehouse",
278298
"preOps",
279299
"postOps",
@@ -326,6 +346,9 @@ export class Table {
326346
if (config.bigquery) {
327347
this.bigquery(config.bigquery);
328348
}
349+
if (config.snowflake) {
350+
this.snowflake(config.snowflake);
351+
}
329352
if (config.sqldatawarehouse) {
330353
this.sqldatawarehouse(config.sqldatawarehouse);
331354
}
@@ -393,6 +416,17 @@ export class Table {
393416
this.proto.uniqueKey = uniqueKey;
394417
}
395418

419+
public snowflake(snowflake: dataform.ISnowflakeOptions) {
420+
checkExcessProperties(
421+
(e: Error) => this.session.compileError(e),
422+
snowflake,
423+
ISnowflakeOptionsProperties(),
424+
"snowflake config"
425+
);
426+
this.proto.snowflake = dataform.SnowflakeOptions.create(snowflake);
427+
return this;
428+
}
429+
396430
public sqldatawarehouse(sqlDataWarehouse: dataform.ISQLDataWarehouseOptions) {
397431
checkExcessProperties(
398432
(e: Error) => this.session.compileError(e),

protos/core.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ message RedshiftOptions {
164164
bool bind = 5 [deprecated = true];
165165
}
166166

167+
message SnowflakeOptions {
168+
bool secure = 1;
169+
}
170+
167171
message SQLDataWarehouseOptions {
168172
string distribution = 1;
169173
}
@@ -285,6 +289,7 @@ message Table {
285289
// Warehouse specific features.
286290
RedshiftOptions redshift = 21;
287291
BigQueryOptions bigquery = 22;
292+
SnowflakeOptions snowflake = 33;
288293
SQLDataWarehouseOptions sql_data_warehouse = 25;
289294

290295
// Generated.

tests/integration/snowflake_project/definitions/example_view.sqlx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ config {
33
description: "An example view",
44
columns: {
55
val: "val doc",
6+
},
7+
snowflake: {
8+
secure: true
69
}
710
}
811
select * from ${ref("sample_data")}

version.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# NOTE: If you change the format of this line, you must change the bash command
22
# in /scripts/publish to extract the version string correctly.
3-
DF_VERSION = "1.11.1"
3+
DF_VERSION = "1.12.0"

0 commit comments

Comments
 (0)