snowflake-sqlalchemy 1.7.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. snowflake/sqlalchemy/__init__.py +162 -0
  2. snowflake/sqlalchemy/_constants.py +14 -0
  3. snowflake/sqlalchemy/base.py +1188 -0
  4. snowflake/sqlalchemy/compat.py +36 -0
  5. snowflake/sqlalchemy/custom_commands.py +627 -0
  6. snowflake/sqlalchemy/custom_types.py +155 -0
  7. snowflake/sqlalchemy/exc.py +82 -0
  8. snowflake/sqlalchemy/functions.py +16 -0
  9. snowflake/sqlalchemy/parser/custom_type_parser.py +245 -0
  10. snowflake/sqlalchemy/provision.py +12 -0
  11. snowflake/sqlalchemy/requirements.py +313 -0
  12. snowflake/sqlalchemy/snowdialect.py +1029 -0
  13. snowflake/sqlalchemy/sql/__init__.py +3 -0
  14. snowflake/sqlalchemy/sql/custom_schema/__init__.py +9 -0
  15. snowflake/sqlalchemy/sql/custom_schema/clustered_table.py +37 -0
  16. snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +127 -0
  17. snowflake/sqlalchemy/sql/custom_schema/custom_table_prefix.py +13 -0
  18. snowflake/sqlalchemy/sql/custom_schema/dynamic_table.py +117 -0
  19. snowflake/sqlalchemy/sql/custom_schema/hybrid_table.py +63 -0
  20. snowflake/sqlalchemy/sql/custom_schema/iceberg_table.py +102 -0
  21. snowflake/sqlalchemy/sql/custom_schema/options/__init__.py +33 -0
  22. snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +63 -0
  23. snowflake/sqlalchemy/sql/custom_schema/options/cluster_by_option.py +58 -0
  24. snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +63 -0
  25. snowflake/sqlalchemy/sql/custom_schema/options/invalid_table_option.py +25 -0
  26. snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +65 -0
  27. snowflake/sqlalchemy/sql/custom_schema/options/keywords.py +14 -0
  28. snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +67 -0
  29. snowflake/sqlalchemy/sql/custom_schema/options/table_option.py +84 -0
  30. snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +94 -0
  31. snowflake/sqlalchemy/sql/custom_schema/snowflake_table.py +70 -0
  32. snowflake/sqlalchemy/sql/custom_schema/table_from_query.py +54 -0
  33. snowflake/sqlalchemy/util.py +344 -0
  34. snowflake/sqlalchemy/version.py +6 -0
  35. snowflake_sqlalchemy-1.7.3.dist-info/METADATA +737 -0
  36. snowflake_sqlalchemy-1.7.3.dist-info/RECORD +39 -0
  37. snowflake_sqlalchemy-1.7.3.dist-info/WHEEL +4 -0
  38. snowflake_sqlalchemy-1.7.3.dist-info/entry_points.txt +2 -0
  39. snowflake_sqlalchemy-1.7.3.dist-info/licenses/LICENSE.txt +202 -0
@@ -0,0 +1,25 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from typing import Optional
5
+
6
+ from .table_option import TableOption, TableOptionKey
7
+
8
+
9
+ class InvalidTableOption(TableOption):
10
+ """Class to store errors and raise them after table initialization in order to avoid recursion error."""
11
+
12
+ def __init__(self, name: TableOptionKey, value: Exception) -> None:
13
+ super().__init__()
14
+ self.exception: Exception = value
15
+ self._name = name
16
+
17
+ @staticmethod
18
+ def create(name: TableOptionKey, value: Exception) -> Optional[TableOption]:
19
+ return InvalidTableOption(name, value)
20
+
21
+ def _render(self, compiler) -> str:
22
+ raise self.exception
23
+
24
+ def __repr__(self) -> str:
25
+ return f"ErrorOption(value='{self.exception}')"
@@ -0,0 +1,65 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from typing import Optional, Union
5
+
6
+ from snowflake.sqlalchemy.custom_commands import NoneType
7
+
8
+ from .keywords import SnowflakeKeyword
9
+ from .table_option import Priority, TableOption, TableOptionKey
10
+
11
+
12
+ class KeywordOption(TableOption):
13
+ """Class to represent a keyword option in Snowflake Tables.
14
+
15
+ Example:
16
+ target_lag = KeywordOption(SnowflakeKeyword.DOWNSTREAM)
17
+
18
+ is equivalent to:
19
+
20
+ TARGET_LAG = DOWNSTREAM
21
+ """
22
+
23
+ def __init__(self, value: Union[SnowflakeKeyword]) -> None:
24
+ super().__init__()
25
+ self.value: str = value.value
26
+
27
+ @property
28
+ def priority(self):
29
+ return Priority.HIGH
30
+
31
+ def template(self) -> str:
32
+ return f"{self.option_name.upper()} = %s"
33
+
34
+ def _render(self, compiler) -> str:
35
+ return self.template() % self.value.upper()
36
+
37
+ @staticmethod
38
+ def create(
39
+ name: TableOptionKey, value: Optional[Union[SnowflakeKeyword, "KeywordOption"]]
40
+ ) -> Optional[TableOption]:
41
+ if isinstance(value, NoneType):
42
+ return value
43
+ if isinstance(value, SnowflakeKeyword):
44
+ value = KeywordOption(value)
45
+
46
+ if isinstance(value, KeywordOption):
47
+ value._set_option_name(name)
48
+ return value
49
+
50
+ return TableOption._get_invalid_table_option(
51
+ name,
52
+ str(type(value).__name__),
53
+ [KeywordOption.__name__, SnowflakeKeyword.__name__],
54
+ )
55
+
56
+ def __repr__(self) -> str:
57
+ option_name = (
58
+ f", table_option_key={self.option_name}"
59
+ if isinstance(self.option_name, NoneType)
60
+ else ""
61
+ )
62
+ return f"KeywordOption(value='{self.value}'{option_name})"
63
+
64
+
65
+ KeywordOptionType = Union[KeywordOption, SnowflakeKeyword]
@@ -0,0 +1,14 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class SnowflakeKeyword(Enum):
8
+ # TARGET_LAG
9
+ DOWNSTREAM = "DOWNSTREAM"
10
+
11
+ # REFRESH_MODE
12
+ AUTO = "AUTO"
13
+ FULL = "FULL"
14
+ INCREMENTAL = "INCREMENTAL"
@@ -0,0 +1,67 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from typing import Any, Optional, Union
5
+
6
+ from snowflake.sqlalchemy.custom_commands import NoneType
7
+
8
+ from .table_option import Priority, TableOption, TableOptionKey
9
+
10
+
11
+ class LiteralOption(TableOption):
12
+ """Class to represent a literal option in Snowflake Table.
13
+
14
+ Example:
15
+ warehouse = LiteralOption('my_warehouse')
16
+
17
+ is equivalent to:
18
+
19
+ WAREHOUSE = 'my_warehouse'
20
+ """
21
+
22
+ def __init__(self, value: Union[int, str]) -> None:
23
+ super().__init__()
24
+ self.value: Any = value
25
+
26
+ @property
27
+ def priority(self):
28
+ return Priority.HIGH
29
+
30
+ @staticmethod
31
+ def create(
32
+ name: TableOptionKey, value: Optional[Union[str, int, "LiteralOption"]]
33
+ ) -> Optional[TableOption]:
34
+ if isinstance(value, NoneType):
35
+ return None
36
+ if isinstance(value, (str, int)):
37
+ value = LiteralOption(value)
38
+
39
+ if isinstance(value, LiteralOption):
40
+ value._set_option_name(name)
41
+ return value
42
+
43
+ return TableOption._get_invalid_table_option(
44
+ name,
45
+ str(type(value).__name__),
46
+ [LiteralOption.__name__, str.__name__, int.__name__],
47
+ )
48
+
49
+ def template(self) -> str:
50
+ if isinstance(self.value, int):
51
+ return f"{self.option_name.upper()} = %d"
52
+ else:
53
+ return f"{self.option_name.upper()} = '%s'"
54
+
55
+ def _render(self, compiler) -> str:
56
+ return self.template() % self.value
57
+
58
+ def __repr__(self) -> str:
59
+ option_name = (
60
+ f", table_option_key={self.option_name}"
61
+ if not isinstance(self.option_name, NoneType)
62
+ else ""
63
+ )
64
+ return f"LiteralOption(value='{self.value}'{option_name})"
65
+
66
+
67
+ LiteralOptionType = Union[LiteralOption, str, int]
@@ -0,0 +1,84 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from enum import Enum
5
+ from typing import List, Optional
6
+
7
+ from snowflake.sqlalchemy import exc
8
+ from snowflake.sqlalchemy.custom_commands import NoneType
9
+
10
+
11
+ class Priority(Enum):
12
+ LOWEST = 0
13
+ VERY_LOW = 1
14
+ LOW = 2
15
+ MEDIUM = 4
16
+ HIGH = 6
17
+ VERY_HIGH = 7
18
+ HIGHEST = 8
19
+
20
+
21
+ class TableOption:
22
+
23
+ def __init__(self) -> None:
24
+ self._name: Optional[TableOptionKey] = None
25
+
26
+ @property
27
+ def option_name(self) -> str:
28
+ if isinstance(self._name, NoneType):
29
+ return None
30
+ return str(self._name.value)
31
+
32
+ def _set_option_name(self, name: Optional["TableOptionKey"]):
33
+ self._name = name
34
+
35
+ @property
36
+ def priority(self) -> Priority:
37
+ return Priority.MEDIUM
38
+
39
+ @staticmethod
40
+ def create(**kwargs) -> "TableOption":
41
+ raise NotImplementedError
42
+
43
+ @staticmethod
44
+ def _get_invalid_table_option(
45
+ parameter_name: "TableOptionKey", input_type: str, expected_types: List[str]
46
+ ) -> "TableOption":
47
+ from .invalid_table_option import InvalidTableOption
48
+
49
+ return InvalidTableOption(
50
+ parameter_name,
51
+ exc.InvalidTableParameterTypeError(
52
+ parameter_name.value, input_type, expected_types
53
+ ),
54
+ )
55
+
56
+ def _validate_option(self):
57
+ if isinstance(self.option_name, NoneType):
58
+ raise exc.OptionKeyNotProvidedError(self.__class__.__name__)
59
+
60
+ def template(self) -> str:
61
+ return f"{self.option_name.upper()} = %s"
62
+
63
+ def render_option(self, compiler) -> str:
64
+ self._validate_option()
65
+ return self._render(compiler)
66
+
67
+ def _render(self, compiler) -> str:
68
+ raise NotImplementedError
69
+
70
+
71
+ class TableOptionKey(Enum):
72
+ AS_QUERY = "as_query"
73
+ BASE_LOCATION = "base_location"
74
+ CATALOG = "catalog"
75
+ CATALOG_SYNC = "catalog_sync"
76
+ CLUSTER_BY = "cluster by"
77
+ DATA_RETENTION_TIME_IN_DAYS = "data_retention_time_in_days"
78
+ DEFAULT_DDL_COLLATION = "default_ddl_collation"
79
+ EXTERNAL_VOLUME = "external_volume"
80
+ MAX_DATA_EXTENSION_TIME_IN_DAYS = "max_data_extension_time_in_days"
81
+ REFRESH_MODE = "refresh_mode"
82
+ STORAGE_SERIALIZATION_POLICY = "storage_serialization_policy"
83
+ TARGET_LAG = "target_lag"
84
+ WAREHOUSE = "warehouse"
@@ -0,0 +1,94 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ # from enum import Enum
4
+ from enum import Enum
5
+ from typing import Optional, Tuple, Union
6
+
7
+ from snowflake.sqlalchemy.custom_commands import NoneType
8
+
9
+ from .keyword_option import KeywordOption, KeywordOptionType
10
+ from .keywords import SnowflakeKeyword
11
+ from .table_option import Priority, TableOption, TableOptionKey
12
+
13
+
14
+ class TimeUnit(Enum):
15
+ SECONDS = "seconds"
16
+ MINUTES = "minutes"
17
+ HOURS = "hours"
18
+ DAYS = "days"
19
+
20
+
21
+ class TargetLagOption(TableOption):
22
+ """Class to represent the target lag clause in Dynamic Tables.
23
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table
24
+
25
+ Example using the time and unit parameters:
26
+
27
+ target_lag = TargetLagOption(10, TimeUnit.SECONDS)
28
+
29
+ is equivalent to:
30
+
31
+ TARGET_LAG = '10 SECONDS'
32
+
33
+ Example using keyword parameter:
34
+
35
+ target_lag = KeywordOption(SnowflakeKeyword.DOWNSTREAM)
36
+
37
+ is equivalent to:
38
+
39
+ TARGET_LAG = DOWNSTREAM
40
+
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ time: Optional[int] = 0,
46
+ unit: Optional[TimeUnit] = TimeUnit.MINUTES,
47
+ ) -> None:
48
+ super().__init__()
49
+ self.time = time
50
+ self.unit = unit
51
+ self._name: TableOptionKey = TableOptionKey.TARGET_LAG
52
+
53
+ @staticmethod
54
+ def create(
55
+ value: Union["TargetLagOption", Tuple[int, TimeUnit], KeywordOptionType]
56
+ ) -> Optional[TableOption]:
57
+ if isinstance(value, NoneType):
58
+ return value
59
+
60
+ if isinstance(value, Tuple):
61
+ time, unit = value
62
+ value = TargetLagOption(time, unit)
63
+
64
+ if isinstance(value, TargetLagOption):
65
+ return value
66
+
67
+ if isinstance(value, (KeywordOption, SnowflakeKeyword)):
68
+ return KeywordOption.create(TableOptionKey.TARGET_LAG, value)
69
+
70
+ return TableOption._get_invalid_table_option(
71
+ TableOptionKey.TARGET_LAG,
72
+ str(type(value).__name__),
73
+ [
74
+ TargetLagOption.__name__,
75
+ f"Tuple[int, {TimeUnit.__name__}])",
76
+ SnowflakeKeyword.__name__,
77
+ ],
78
+ )
79
+
80
+ def __get_expression(self):
81
+ return f"'{str(self.time)} {str(self.unit.value)}'"
82
+
83
+ @property
84
+ def priority(self) -> Priority:
85
+ return Priority.HIGH
86
+
87
+ def _render(self, compiler) -> str:
88
+ return self.template() % (self.__get_expression())
89
+
90
+ def __repr__(self) -> str:
91
+ return "TargetLagOption(%s)" % self.__get_expression()
92
+
93
+
94
+ TargetLagOptionType = Union[TargetLagOption, Tuple[int, TimeUnit]]
@@ -0,0 +1,70 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ from typing import Any
6
+
7
+ from sqlalchemy.sql.schema import MetaData, SchemaItem
8
+
9
+ from .table_from_query import TableFromQueryBase
10
+
11
+
12
+ class SnowflakeTable(TableFromQueryBase):
13
+ """
14
+ A class representing a table in Snowflake with configurable options and settings.
15
+
16
+ While it does not support reflection at this time, it provides a flexible
17
+ interface for creating tables and management.
18
+
19
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-table
20
+ Example usage:
21
+
22
+ SnowflakeTable(
23
+ table_name,
24
+ metadata,
25
+ Column("id", Integer, primary_key=True),
26
+ Column("name", String),
27
+ cluster_by = ["id", text("name > 5")]
28
+ )
29
+
30
+ Example using explict options:
31
+
32
+ SnowflakeTable(
33
+ table_name,
34
+ metadata,
35
+ Column("id", Integer, primary_key=True),
36
+ Column("name", String),
37
+ cluster_by = ClusterByOption("id", text("name > 5"))
38
+ )
39
+
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ name: str,
45
+ metadata: MetaData,
46
+ *args: SchemaItem,
47
+ **kw: Any,
48
+ ) -> None:
49
+ if kw.get("_no_init", True):
50
+ return
51
+ super().__init__(name, metadata, *args, **kw)
52
+
53
+ def _init(
54
+ self,
55
+ name: str,
56
+ metadata: MetaData,
57
+ *args: SchemaItem,
58
+ **kw: Any,
59
+ ) -> None:
60
+ self.__init__(name, metadata, *args, _no_init=False, **kw)
61
+
62
+ def __repr__(self) -> str:
63
+ return "SnowflakeTable(%s)" % ", ".join(
64
+ [repr(self.name)]
65
+ + [repr(self.metadata)]
66
+ + [repr(x) for x in self.columns]
67
+ + [repr(self.cluster_by)]
68
+ + [repr(self.as_query)]
69
+ + [f"{k}={repr(getattr(self, k))}" for k in ["schema"]]
70
+ )
@@ -0,0 +1,54 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ import typing
5
+ from typing import Any, Optional
6
+
7
+ from sqlalchemy.sql import Selectable
8
+ from sqlalchemy.sql.schema import Column, MetaData, SchemaItem
9
+
10
+ from .clustered_table import ClusteredTableBase
11
+ from .options.as_query_option import AsQueryOption, AsQueryOptionType
12
+ from .options.table_option import TableOptionKey
13
+
14
+
15
+ class TableFromQueryBase(ClusteredTableBase):
16
+
17
+ @property
18
+ def as_query(self) -> Optional[AsQueryOption]:
19
+ return self._get_dialect_option(TableOptionKey.AS_QUERY)
20
+
21
+ def __init__(
22
+ self,
23
+ name: str,
24
+ metadata: MetaData,
25
+ *args: SchemaItem,
26
+ as_query: AsQueryOptionType = None,
27
+ **kw: Any,
28
+ ) -> None:
29
+ items = [item for item in args]
30
+ as_query = AsQueryOption.create(as_query) # noqa
31
+ kw.update(self._as_dialect_options([as_query]))
32
+ if (
33
+ isinstance(as_query, AsQueryOption)
34
+ and isinstance(as_query.query, Selectable)
35
+ and not self.__has_defined_columns(items)
36
+ ):
37
+ columns = self.__create_columns_from_selectable(as_query.query)
38
+ args = items + columns
39
+ super().__init__(name, metadata, *args, **kw)
40
+
41
+ def __has_defined_columns(self, items: typing.List[SchemaItem]) -> bool:
42
+ for item in items:
43
+ if isinstance(item, Column):
44
+ return True
45
+
46
+ def __create_columns_from_selectable(
47
+ self, selectable: Selectable
48
+ ) -> Optional[typing.List[Column]]:
49
+ if not isinstance(selectable, Selectable):
50
+ return
51
+ columns: typing.List[Column] = []
52
+ for _, c in selectable.exported_columns.items():
53
+ columns += [Column(c.name, c.type)]
54
+ return columns