Skip to content

Commit 9d6fe68

Browse files
šŸ›Destination-Snowflake: Added "Table/Stage already exists, but no permissions" handler on Sync action (#21912)
* [21843] Destination-Snowflake: Added "Table/Stage already exists, but no permissions" handler on Sync action --------- Co-authored-by: Octavia Squidington III <[email protected]>
1 parent 98054d6 commit 9d6fe68

File tree

9 files changed

+74
-11
lines changed

9 files changed

+74
-11
lines changed

ā€Žairbyte-config/init/src/main/resources/seed/destination_definitions.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@
348348
- name: Snowflake
349349
destinationDefinitionId: 424892c4-daac-4491-b35d-c6688ba547ba
350350
dockerRepository: airbyte/destination-snowflake
351-
dockerImageTag: 0.4.46
351+
dockerImageTag: 0.4.47
352352
documentationUrl: https://docs.airbyte.com/integrations/destinations/snowflake
353353
icon: snowflake.svg
354354
normalizationConfig:

ā€Žairbyte-config/init/src/main/resources/seed/destination_specs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6109,7 +6109,7 @@
61096109
supported_destination_sync_modes:
61106110
- "overwrite"
61116111
- "append"
6112-
- dockerImage: "airbyte/destination-snowflake:0.4.46"
6112+
- dockerImage: "airbyte/destination-snowflake:0.4.47"
61136113
spec:
61146114
documentationUrl: "https://docs.airbyte.com/integrations/destinations/snowflake"
61156115
connectionSpecification:

ā€Žairbyte-integrations/connectors/destination-jdbc/src/main/java/io/airbyte/integrations/destination/jdbc/JdbcSqlOperations.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ protected Optional<ConfigErrorException> checkForKnownConfigExceptions(Exception
6767

6868
@Override
6969
public void createTableIfNotExists(final JdbcDatabase database, final String schemaName, final String tableName) throws SQLException {
70-
database.execute(createTableQuery(database, schemaName, tableName));
70+
try {
71+
database.execute(createTableQuery(database, schemaName, tableName));
72+
} catch (SQLException e) {
73+
throw checkForKnownConfigExceptions(e).orElseThrow(() -> e);
74+
}
7175
}
7276

7377
@Override

ā€Žairbyte-integrations/connectors/destination-snowflake/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ RUN tar xf ${APPLICATION}.tar --strip-components=1
2020

2121
ENV ENABLE_SENTRY true
2222

23-
LABEL io.airbyte.version=0.4.46
23+
LABEL io.airbyte.version=0.4.47
2424
LABEL io.airbyte.name=airbyte/destination-snowflake

ā€Žairbyte-integrations/connectors/destination-snowflake/src/main/java/io/airbyte/integrations/destination/snowflake/SnowflakeInternalStagingSqlOperations.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ protected String getListQuery(final String stageName, final String stagingPath,
130130
public void createStageIfNotExists(final JdbcDatabase database, final String stageName) throws Exception {
131131
final String query = getCreateStageQuery(stageName);
132132
LOGGER.debug("Executing query: {}", query);
133-
database.execute(query);
133+
try {
134+
database.execute(query);
135+
} catch (Exception e) {
136+
throw checkForKnownConfigExceptions(e).orElseThrow(() -> e);
137+
}
134138
}
135139

136140
/**

ā€Žairbyte-integrations/connectors/destination-snowflake/src/main/java/io/airbyte/integrations/destination/snowflake/SnowflakeSqlOperations.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ protected String generateFilesList(final List<String> files) {
8383
@Override
8484
protected Optional<ConfigErrorException> checkForKnownConfigExceptions(Exception e) {
8585
if (e instanceof SnowflakeSQLException && e.getMessage().contains(NO_PRIVILEGES_ERROR_MESSAGE)) {
86-
return Optional.of(new ConfigErrorException("Encountered Error with Snowflake Configuration: Current role does not have permissions on the target schema please verify your privileges", e));
86+
return Optional.of(new ConfigErrorException(
87+
"Encountered Error with Snowflake Configuration: Current role does not have permissions on the target schema please verify your privileges",
88+
e));
8789
}
8890
return Optional.empty();
8991
}

ā€Žairbyte-integrations/connectors/destination-snowflake/src/test/java/io/airbyte/integrations/destination/snowflake/SnowflakeInternalStagingSqlOperationsTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,19 @@
55
package io.airbyte.integrations.destination.snowflake;
66

77
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
89
import static org.junit.jupiter.api.Assertions.assertTrue;
910

11+
import io.airbyte.commons.exceptions.ConfigErrorException;
12+
import io.airbyte.db.jdbc.JdbcDatabase;
13+
import java.sql.SQLException;
1014
import java.util.List;
15+
import net.snowflake.client.jdbc.SnowflakeSQLException;
16+
import org.junit.jupiter.api.Assertions;
1117
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
import org.junit.jupiter.params.provider.CsvSource;
20+
import org.mockito.Mockito;
1221

1322
class SnowflakeInternalStagingSqlOperationsTest {
1423

@@ -66,4 +75,24 @@ void removeStage() {
6675
assertEquals(expectedQuery, actualRemoveQuery);
6776
}
6877

78+
@ParameterizedTest
79+
@CsvSource({"TEST,false", "but current role has no privileges on it,true"})
80+
public void testCreateStageIfNotExists(final String message, final boolean shouldCapture) {
81+
final JdbcDatabase db = Mockito.mock(JdbcDatabase.class);
82+
final String stageName = "foo";
83+
try {
84+
Mockito.doThrow(new SnowflakeSQLException(message)).when(db).execute(Mockito.anyString());
85+
} catch (SQLException e) {
86+
// This would not be expected, but the `execute` method above will flag as an unhandled exception
87+
assert false;
88+
}
89+
final Exception exception = Assertions.assertThrows(Exception.class, () -> snowflakeStagingSqlOperations.createStageIfNotExists(db, stageName));
90+
if (shouldCapture) {
91+
assertInstanceOf(ConfigErrorException.class, exception);
92+
} else {
93+
assertInstanceOf(SnowflakeSQLException.class, exception);
94+
assertEquals(exception.getMessage(), message);
95+
}
96+
}
97+
6998
}

ā€Žairbyte-integrations/connectors/destination-snowflake/src/test/java/io/airbyte/integrations/destination/snowflake/SnowflakeSqlOperationsTest.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package io.airbyte.integrations.destination.snowflake;
66

77
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
810
import static org.mockito.ArgumentMatchers.any;
911
import static org.mockito.ArgumentMatchers.anyString;
1012
import static org.mockito.Mockito.mock;
@@ -20,7 +22,6 @@
2022
import java.util.ArrayList;
2123
import java.util.List;
2224
import net.snowflake.client.jdbc.SnowflakeSQLException;
23-
import org.junit.jupiter.api.Assertions;
2425
import org.junit.jupiter.api.Test;
2526
import org.junit.jupiter.params.ParameterizedTest;
2627
import org.junit.jupiter.params.provider.CsvSource;
@@ -70,12 +71,34 @@ public void testCreateSchemaIfNotExists(final String message, final boolean shou
7071
// This would not be expected, but the `execute` method above will flag as an unhandled exception
7172
assert false;
7273
}
73-
Exception exception = Assertions.assertThrows(Exception.class, () -> snowflakeSqlOperations.createSchemaIfNotExists(db, schemaName));
74+
Exception exception = assertThrows(Exception.class, () -> snowflakeSqlOperations.createSchemaIfNotExists(db, schemaName));
7475
if (shouldCapture) {
75-
Assertions.assertInstanceOf(ConfigErrorException.class, exception);
76+
assertInstanceOf(ConfigErrorException.class, exception);
7677
} else {
77-
Assertions.assertInstanceOf(SnowflakeSQLException.class, exception);
78-
Assertions.assertEquals(exception.getMessage(), message);
78+
assertInstanceOf(SnowflakeSQLException.class, exception);
79+
assertEquals(exception.getMessage(), message);
80+
}
81+
}
82+
83+
@ParameterizedTest
84+
@CsvSource({"TEST,false", "but current role has no privileges on it,true"})
85+
public void testCreateTableIfNotExists(final String message, final boolean shouldCapture) {
86+
final JdbcDatabase db = Mockito.mock(JdbcDatabase.class);
87+
final String schemaName = "foo";
88+
final String tableName = "bar";
89+
try {
90+
Mockito.doThrow(new SnowflakeSQLException(message)).when(db).execute(Mockito.anyString());
91+
} catch (SQLException e) {
92+
// This would not be expected, but the `execute` method above will flag as an unhandled exception
93+
assert false;
94+
}
95+
final Exception exception =
96+
assertThrows(Exception.class, () -> snowflakeSqlOperations.createTableIfNotExists(db, schemaName, tableName));
97+
if (shouldCapture) {
98+
assertInstanceOf(ConfigErrorException.class, exception);
99+
} else {
100+
assertInstanceOf(SnowflakeSQLException.class, exception);
101+
assertEquals(exception.getMessage(), message);
79102
}
80103
}
81104

ā€Ždocs/integrations/destinations/snowflake.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ Otherwise, make sure to grant the role the required permissions in the desired n
287287
288288
| Version | Date | Pull Request | Subject |
289289
|:--------|:-----------|:-----------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------|
290+
| 0.4.47 | 2023-01-30 | [\#21912](https://github.com/airbytehq/airbyte/pull/21912) | Catch "Create" Table and Stage Known Permissions and rethrow as ConfigExceptions |
290291
| 0.4.46 | 2023-01-26 | [\#20631](https://github.com/airbytehq/airbyte/pull/20631) | Added support for destination checkpointing with staging |
291292
| 0.4.45 | 2023-01-25 | [#21087](https://github.com/airbytehq/airbyte/pull/21764) | Catch Known Permissions and rethrow as ConfigExceptions |
292293
| 0.4.44 | 2023-01-20 | [#21087](https://github.com/airbytehq/airbyte/pull/21087) | Wrap Authentication Errors as Config Exceptions |

0 commit comments

Comments
Ā (0)