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,3 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
@@ -0,0 +1,9 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from .dynamic_table import DynamicTable
5
+ from .hybrid_table import HybridTable
6
+ from .iceberg_table import IcebergTable
7
+ from .snowflake_table import SnowflakeTable
8
+
9
+ __all__ = ["DynamicTable", "HybridTable", "IcebergTable", "SnowflakeTable"]
@@ -0,0 +1,37 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ from typing import Any, Optional
6
+
7
+ from sqlalchemy.sql.schema import MetaData, SchemaItem
8
+
9
+ from .custom_table_base import CustomTableBase
10
+ from .options.as_query_option import AsQueryOption
11
+ from .options.cluster_by_option import ClusterByOption, ClusterByOptionType
12
+ from .options.table_option import TableOptionKey
13
+
14
+
15
+ class ClusteredTableBase(CustomTableBase):
16
+
17
+ @property
18
+ def cluster_by(self) -> Optional[AsQueryOption]:
19
+ return self._get_dialect_option(TableOptionKey.CLUSTER_BY)
20
+
21
+ def __init__(
22
+ self,
23
+ name: str,
24
+ metadata: MetaData,
25
+ *args: SchemaItem,
26
+ cluster_by: ClusterByOptionType = None,
27
+ **kw: Any,
28
+ ) -> None:
29
+ if kw.get("_no_init", True):
30
+ return
31
+
32
+ options = [
33
+ ClusterByOption.create(cluster_by),
34
+ ]
35
+
36
+ kw.update(self._as_dialect_options(options))
37
+ super().__init__(name, metadata, *args, **kw)
@@ -0,0 +1,127 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ import typing
5
+ from typing import Any, List
6
+
7
+ from sqlalchemy.sql.schema import MetaData, SchemaItem, Table
8
+
9
+ from ..._constants import DIALECT_NAME
10
+ from ...compat import IS_VERSION_20
11
+ from ...custom_commands import NoneType
12
+ from ...custom_types import StructuredType
13
+ from ...exc import (
14
+ MultipleErrors,
15
+ NoPrimaryKeyError,
16
+ RequiredParametersNotProvidedError,
17
+ StructuredTypeNotSupportedInTableColumnsError,
18
+ UnsupportedPrimaryKeysAndForeignKeysError,
19
+ )
20
+ from .custom_table_prefix import CustomTablePrefix
21
+ from .options.invalid_table_option import InvalidTableOption
22
+ from .options.table_option import TableOption, TableOptionKey
23
+
24
+
25
+ class CustomTableBase(Table):
26
+ __table_prefixes__: typing.List[CustomTablePrefix] = []
27
+ _support_primary_and_foreign_keys: bool = True
28
+ _enforce_primary_keys: bool = False
29
+ _required_parameters: List[TableOptionKey] = []
30
+ _support_structured_types: bool = False
31
+
32
+ @property
33
+ def table_prefixes(self) -> typing.List[str]:
34
+ return [prefix.name for prefix in self.__table_prefixes__]
35
+
36
+ def __init__(
37
+ self,
38
+ name: str,
39
+ metadata: MetaData,
40
+ *args: SchemaItem,
41
+ **kw: Any,
42
+ ) -> None:
43
+ if len(self.__table_prefixes__) > 0:
44
+ prefixes = kw.get("prefixes", []) + self.table_prefixes
45
+ kw.update(prefixes=prefixes)
46
+
47
+ if not IS_VERSION_20 and hasattr(super(), "_init"):
48
+ kw.pop("_no_init", True)
49
+ super()._init(name, metadata, *args, **kw)
50
+ else:
51
+ super().__init__(name, metadata, *args, **kw)
52
+
53
+ if not kw.get("autoload_with", False):
54
+ self._validate_table()
55
+
56
+ def _validate_table(self):
57
+ exceptions: List[Exception] = []
58
+
59
+ columns_validation = self.__validate_columns()
60
+ if columns_validation is not None:
61
+ exceptions.append(columns_validation)
62
+
63
+ for _, option in self.dialect_options[DIALECT_NAME].items():
64
+ if isinstance(option, InvalidTableOption):
65
+ exceptions.append(option.exception)
66
+
67
+ if isinstance(self.key, NoneType) and self._enforce_primary_keys:
68
+ exceptions.append(NoPrimaryKeyError(self.__class__.__name__))
69
+ missing_parameters: List[str] = []
70
+
71
+ for required_parameter in self._required_parameters:
72
+ if isinstance(self._get_dialect_option(required_parameter), NoneType):
73
+ missing_parameters.append(required_parameter.name.lower())
74
+ if missing_parameters:
75
+ exceptions.append(
76
+ RequiredParametersNotProvidedError(
77
+ self.__class__.__name__, missing_parameters
78
+ )
79
+ )
80
+
81
+ if not self._support_primary_and_foreign_keys and (
82
+ self.primary_key or self.foreign_keys
83
+ ):
84
+ exceptions.append(
85
+ UnsupportedPrimaryKeysAndForeignKeysError(self.__class__.__name__)
86
+ )
87
+
88
+ if len(exceptions) > 1:
89
+ exceptions.sort(key=lambda e: str(e))
90
+ raise MultipleErrors(exceptions)
91
+ elif len(exceptions) == 1:
92
+ raise exceptions[0]
93
+
94
+ def __validate_columns(self):
95
+ for column in self.columns:
96
+ if not self._support_structured_types and isinstance(
97
+ column.type, StructuredType
98
+ ):
99
+ return StructuredTypeNotSupportedInTableColumnsError(
100
+ self.__class__.__name__, self.name, column.name
101
+ )
102
+
103
+ def _get_dialect_option(
104
+ self, option_name: TableOptionKey
105
+ ) -> typing.Optional[TableOption]:
106
+ if option_name.value in self.dialect_options[DIALECT_NAME]:
107
+ return self.dialect_options[DIALECT_NAME][option_name.value]
108
+ return None
109
+
110
+ def _as_dialect_options(
111
+ self, table_options: List[TableOption]
112
+ ) -> typing.Dict[str, TableOption]:
113
+ result = {}
114
+ for table_option in table_options:
115
+ if isinstance(table_option, TableOption) and isinstance(
116
+ table_option.option_name, str
117
+ ):
118
+ result[DIALECT_NAME + "_" + table_option.option_name] = table_option
119
+ return result
120
+
121
+ @classmethod
122
+ def is_equal_type(cls, table: Table) -> bool:
123
+ for prefix in cls.__table_prefixes__:
124
+ if prefix.name not in table._prefixes:
125
+ return False
126
+
127
+ return True
@@ -0,0 +1,13 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class CustomTablePrefix(Enum):
8
+ DEFAULT = 0
9
+ EXTERNAL = 1
10
+ EVENT = 2
11
+ HYBRID = 3
12
+ ICEBERG = 4
13
+ DYNAMIC = 5
@@ -0,0 +1,117 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ import typing
6
+ from typing import Any, Union
7
+
8
+ from sqlalchemy.sql.schema import MetaData, SchemaItem
9
+
10
+ from .custom_table_prefix import CustomTablePrefix
11
+ from .options import (
12
+ IdentifierOption,
13
+ IdentifierOptionType,
14
+ KeywordOptionType,
15
+ TableOptionKey,
16
+ TargetLagOption,
17
+ TargetLagOptionType,
18
+ )
19
+ from .options.keyword_option import KeywordOption
20
+ from .table_from_query import TableFromQueryBase
21
+
22
+
23
+ class DynamicTable(TableFromQueryBase):
24
+ """
25
+ A class representing a dynamic table with configurable options and settings.
26
+
27
+ The `DynamicTable` class allows for the creation and querying of tables with
28
+ specific options, such as `Warehouse` and `TargetLag`.
29
+
30
+ While it does not support reflection at this time, it provides a flexible
31
+ interface for creating dynamic tables and management.
32
+
33
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table
34
+
35
+ Example using option values:
36
+ DynamicTable(
37
+ "dynamic_test_table_1",
38
+ metadata,
39
+ Column("id", Integer),
40
+ Column("name", String),
41
+ target_lag=(1, TimeUnit.HOURS),
42
+ warehouse='warehouse_name',
43
+ refresh_mode=SnowflakeKeyword.AUTO
44
+ as_query="SELECT id, name from test_table_1;"
45
+ )
46
+
47
+ Example using explicit options:
48
+ DynamicTable(
49
+ "dynamic_test_table_1",
50
+ metadata,
51
+ Column("id", Integer),
52
+ Column("name", String),
53
+ target_lag=TargetLag(1, TimeUnit.HOURS),
54
+ warehouse=Identifier('warehouse_name'),
55
+ refresh_mode=KeywordOption(SnowflakeKeyword.AUTO)
56
+ as_query=AsQuery("SELECT id, name from test_table_1;")
57
+ )
58
+ """
59
+
60
+ __table_prefixes__ = [CustomTablePrefix.DYNAMIC]
61
+ _support_primary_and_foreign_keys = False
62
+ _required_parameters = [
63
+ TableOptionKey.WAREHOUSE,
64
+ TableOptionKey.AS_QUERY,
65
+ TableOptionKey.TARGET_LAG,
66
+ ]
67
+
68
+ @property
69
+ def warehouse(self) -> typing.Optional[IdentifierOption]:
70
+ return self._get_dialect_option(TableOptionKey.WAREHOUSE)
71
+
72
+ @property
73
+ def target_lag(self) -> typing.Optional[TargetLagOption]:
74
+ return self._get_dialect_option(TableOptionKey.TARGET_LAG)
75
+
76
+ def __init__(
77
+ self,
78
+ name: str,
79
+ metadata: MetaData,
80
+ *args: SchemaItem,
81
+ warehouse: IdentifierOptionType = None,
82
+ target_lag: Union[TargetLagOptionType, KeywordOptionType] = None,
83
+ refresh_mode: KeywordOptionType = None,
84
+ **kw: Any,
85
+ ) -> None:
86
+ if kw.get("_no_init", True):
87
+ return
88
+
89
+ options = [
90
+ IdentifierOption.create(TableOptionKey.WAREHOUSE, warehouse),
91
+ TargetLagOption.create(target_lag),
92
+ KeywordOption.create(TableOptionKey.REFRESH_MODE, refresh_mode),
93
+ ]
94
+
95
+ kw.update(self._as_dialect_options(options))
96
+ super().__init__(name, metadata, *args, **kw)
97
+
98
+ def _init(
99
+ self,
100
+ name: str,
101
+ metadata: MetaData,
102
+ *args: SchemaItem,
103
+ **kw: Any,
104
+ ) -> None:
105
+ self.__init__(name, metadata, *args, _no_init=False, **kw)
106
+
107
+ def __repr__(self) -> str:
108
+ return "DynamicTable(%s)" % ", ".join(
109
+ [repr(self.name)]
110
+ + [repr(self.metadata)]
111
+ + [repr(x) for x in self.columns]
112
+ + [repr(self.target_lag)]
113
+ + [repr(self.warehouse)]
114
+ + [repr(self.cluster_by)]
115
+ + [repr(self.as_query)]
116
+ + [f"{k}={repr(getattr(self, k))}" for k in ["schema"]]
117
+ )
@@ -0,0 +1,63 @@
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 .custom_table_base import CustomTableBase
10
+ from .custom_table_prefix import CustomTablePrefix
11
+
12
+
13
+ class HybridTable(CustomTableBase):
14
+ """
15
+ A class representing a hybrid table with configurable options and settings.
16
+
17
+ The `HybridTable` class allows for the creation and querying of OLTP Snowflake Tables .
18
+
19
+ While it does not support reflection at this time, it provides a flexible
20
+ interface for creating hybrid tables and management.
21
+
22
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-hybrid-table
23
+
24
+ Example usage:
25
+ HybridTable(
26
+ table_name,
27
+ metadata,
28
+ Column("id", Integer, primary_key=True),
29
+ Column("name", String)
30
+ )
31
+ """
32
+
33
+ __table_prefixes__ = [CustomTablePrefix.HYBRID]
34
+ _enforce_primary_keys: bool = True
35
+ _support_structured_types = True
36
+
37
+ def __init__(
38
+ self,
39
+ name: str,
40
+ metadata: MetaData,
41
+ *args: SchemaItem,
42
+ **kw: Any,
43
+ ) -> None:
44
+ if kw.get("_no_init", True):
45
+ return
46
+ super().__init__(name, metadata, *args, **kw)
47
+
48
+ def _init(
49
+ self,
50
+ name: str,
51
+ metadata: MetaData,
52
+ *args: SchemaItem,
53
+ **kw: Any,
54
+ ) -> None:
55
+ self.__init__(name, metadata, *args, _no_init=False, **kw)
56
+
57
+ def __repr__(self) -> str:
58
+ return "HybridTable(%s)" % ", ".join(
59
+ [repr(self.name)]
60
+ + [repr(self.metadata)]
61
+ + [repr(x) for x in self.columns]
62
+ + [f"{k}={repr(getattr(self, k))}" for k in ["schema"]]
63
+ )
@@ -0,0 +1,102 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ import typing
6
+ from typing import Any
7
+
8
+ from sqlalchemy.sql.schema import MetaData, SchemaItem
9
+
10
+ from .custom_table_prefix import CustomTablePrefix
11
+ from .options import LiteralOption, LiteralOptionType, TableOptionKey
12
+ from .table_from_query import TableFromQueryBase
13
+
14
+
15
+ class IcebergTable(TableFromQueryBase):
16
+ """
17
+ A class representing an iceberg table with configurable options and settings.
18
+
19
+ While it does not support reflection at this time, it provides a flexible
20
+ interface for creating iceberg tables and management.
21
+
22
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table
23
+
24
+ Example using option values:
25
+
26
+ IcebergTable(
27
+ "dynamic_test_table_1",
28
+ metadata,
29
+ Column("id", Integer),
30
+ Column("name", String),
31
+ external_volume='my_external_volume',
32
+ base_location='my_iceberg_table'"
33
+ )
34
+
35
+ Example using explicit options:
36
+ DynamicTable(
37
+ "dynamic_test_table_1",
38
+ metadata,
39
+ Column("id", Integer),
40
+ Column("name", String),
41
+ external_volume=LiteralOption('my_external_volume')
42
+ base_location=LiteralOption('my_iceberg_table')
43
+ )
44
+ """
45
+
46
+ __table_prefixes__ = [CustomTablePrefix.ICEBERG]
47
+ _support_structured_types = True
48
+
49
+ @property
50
+ def external_volume(self) -> typing.Optional[LiteralOption]:
51
+ return self._get_dialect_option(TableOptionKey.EXTERNAL_VOLUME)
52
+
53
+ @property
54
+ def base_location(self) -> typing.Optional[LiteralOption]:
55
+ return self._get_dialect_option(TableOptionKey.BASE_LOCATION)
56
+
57
+ @property
58
+ def catalog(self) -> typing.Optional[LiteralOption]:
59
+ return self._get_dialect_option(TableOptionKey.CATALOG)
60
+
61
+ def __init__(
62
+ self,
63
+ name: str,
64
+ metadata: MetaData,
65
+ *args: SchemaItem,
66
+ external_volume: LiteralOptionType = None,
67
+ base_location: LiteralOptionType = None,
68
+ **kw: Any,
69
+ ) -> None:
70
+ if kw.get("_no_init", True):
71
+ return
72
+
73
+ options = [
74
+ LiteralOption.create(TableOptionKey.EXTERNAL_VOLUME, external_volume),
75
+ LiteralOption.create(TableOptionKey.BASE_LOCATION, base_location),
76
+ LiteralOption.create(TableOptionKey.CATALOG, "SNOWFLAKE"),
77
+ ]
78
+
79
+ kw.update(self._as_dialect_options(options))
80
+ super().__init__(name, metadata, *args, **kw)
81
+
82
+ def _init(
83
+ self,
84
+ name: str,
85
+ metadata: MetaData,
86
+ *args: SchemaItem,
87
+ **kw: Any,
88
+ ) -> None:
89
+ self.__init__(name, metadata, *args, _no_init=False, **kw)
90
+
91
+ def __repr__(self) -> str:
92
+ return "IcebergTable(%s)" % ", ".join(
93
+ [repr(self.name)]
94
+ + [repr(self.metadata)]
95
+ + [repr(x) for x in self.columns]
96
+ + [repr(self.external_volume)]
97
+ + [repr(self.base_location)]
98
+ + [repr(self.catalog)]
99
+ + [repr(self.cluster_by)]
100
+ + [repr(self.as_query)]
101
+ + [f"{k}={repr(getattr(self, k))}" for k in ["schema"]]
102
+ )
@@ -0,0 +1,33 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ from .as_query_option import AsQueryOption, AsQueryOptionType
6
+ from .cluster_by_option import ClusterByOption, ClusterByOptionType
7
+ from .identifier_option import IdentifierOption, IdentifierOptionType
8
+ from .keyword_option import KeywordOption, KeywordOptionType
9
+ from .keywords import SnowflakeKeyword
10
+ from .literal_option import LiteralOption, LiteralOptionType
11
+ from .table_option import TableOptionKey
12
+ from .target_lag_option import TargetLagOption, TargetLagOptionType, TimeUnit
13
+
14
+ __all__ = [
15
+ # Options
16
+ "IdentifierOption",
17
+ "LiteralOption",
18
+ "KeywordOption",
19
+ "AsQueryOption",
20
+ "TargetLagOption",
21
+ "ClusterByOption",
22
+ # Enums
23
+ "TimeUnit",
24
+ "SnowflakeKeyword",
25
+ "TableOptionKey",
26
+ # Types
27
+ "IdentifierOptionType",
28
+ "LiteralOptionType",
29
+ "AsQueryOptionType",
30
+ "TargetLagOptionType",
31
+ "KeywordOptionType",
32
+ "ClusterByOptionType",
33
+ ]
@@ -0,0 +1,63 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from typing import Optional, Union
5
+
6
+ from sqlalchemy.sql import Selectable
7
+
8
+ from snowflake.sqlalchemy.custom_commands import NoneType
9
+
10
+ from .table_option import Priority, TableOption, TableOptionKey
11
+
12
+
13
+ class AsQueryOption(TableOption):
14
+ """Class to represent an AS clause in tables.
15
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/sql-reference/sql/create-table#create-table-as-select-also-referred-to-as-ctas
16
+
17
+ Example:
18
+ as_query=AsQueryOption('select name, address from existing_table where name = "test"')
19
+
20
+ is equivalent to:
21
+
22
+ as select name, address from existing_table where name = "test"
23
+ """
24
+
25
+ def __init__(self, query: Union[str, Selectable]) -> None:
26
+ super().__init__()
27
+ self._name: TableOptionKey = TableOptionKey.AS_QUERY
28
+ self.query = query
29
+
30
+ @staticmethod
31
+ def create(
32
+ value: Optional[Union["AsQueryOption", str, Selectable]]
33
+ ) -> "TableOption":
34
+ if isinstance(value, (NoneType, AsQueryOption)):
35
+ return value
36
+ if isinstance(value, (str, Selectable)):
37
+ return AsQueryOption(value)
38
+ return TableOption._get_invalid_table_option(
39
+ TableOptionKey.AS_QUERY,
40
+ str(type(value).__name__),
41
+ [AsQueryOption.__name__, str.__name__, Selectable.__name__],
42
+ )
43
+
44
+ def template(self) -> str:
45
+ return "AS %s"
46
+
47
+ @property
48
+ def priority(self) -> Priority:
49
+ return Priority.LOWEST
50
+
51
+ def __get_expression(self):
52
+ if isinstance(self.query, Selectable):
53
+ return self.query.compile(compile_kwargs={"literal_binds": True})
54
+ return self.query
55
+
56
+ def _render(self, compiler) -> str:
57
+ return self.template() % (self.__get_expression())
58
+
59
+ def __repr__(self) -> str:
60
+ return "AsQueryOption(%s)" % self.__get_expression()
61
+
62
+
63
+ AsQueryOptionType = Union[AsQueryOption, str, Selectable]
@@ -0,0 +1,58 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+ from typing import List, Union
5
+
6
+ from sqlalchemy.sql.expression import TextClause
7
+
8
+ from snowflake.sqlalchemy.custom_commands import NoneType
9
+
10
+ from .table_option import Priority, TableOption, TableOptionKey
11
+
12
+
13
+ class ClusterByOption(TableOption):
14
+ """Class to represent the cluster by clause in tables.
15
+ For further information on this clause, please refer to: https://docs.snowflake.com/en/user-guide/tables-clustering-keys
16
+ Example:
17
+ cluster_by=ClusterByOption('name', text('id > 0'))
18
+
19
+ is equivalent to:
20
+
21
+ cluster by (name, id > 0)
22
+ """
23
+
24
+ def __init__(self, *expressions: Union[str, TextClause]) -> None:
25
+ super().__init__()
26
+ self._name: TableOptionKey = TableOptionKey.CLUSTER_BY
27
+ self.expressions = expressions
28
+
29
+ @staticmethod
30
+ def create(value: "ClusterByOptionType") -> "TableOption":
31
+ if isinstance(value, (NoneType, ClusterByOption)):
32
+ return value
33
+ if isinstance(value, List):
34
+ return ClusterByOption(*value)
35
+ return TableOption._get_invalid_table_option(
36
+ TableOptionKey.CLUSTER_BY,
37
+ str(type(value).__name__),
38
+ [ClusterByOption.__name__, list.__name__],
39
+ )
40
+
41
+ def template(self) -> str:
42
+ return f"{self.option_name.upper()} (%s)"
43
+
44
+ @property
45
+ def priority(self) -> Priority:
46
+ return Priority.HIGH
47
+
48
+ def __get_expression(self):
49
+ return ", ".join([str(expression) for expression in self.expressions])
50
+
51
+ def _render(self, compiler) -> str:
52
+ return self.template() % (self.__get_expression())
53
+
54
+ def __repr__(self) -> str:
55
+ return "ClusterByOption(%s)" % self.__get_expression()
56
+
57
+
58
+ ClusterByOptionType = Union[ClusterByOption, List[Union[str, TextClause]]]
@@ -0,0 +1,63 @@
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 .table_option import Priority, TableOption, TableOptionKey
9
+
10
+
11
+ class IdentifierOption(TableOption):
12
+ """Class to represent an identifier option in Snowflake Tables.
13
+
14
+ Example:
15
+ warehouse = IdentifierOption('my_warehouse')
16
+
17
+ is equivalent to:
18
+
19
+ WAREHOUSE = my_warehouse
20
+ """
21
+
22
+ def __init__(self, value: Union[str]) -> None:
23
+ super().__init__()
24
+ self.value: str = 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, "IdentifierOption"]]
33
+ ) -> Optional[TableOption]:
34
+ if isinstance(value, NoneType):
35
+ return None
36
+
37
+ if isinstance(value, str):
38
+ value = IdentifierOption(value)
39
+
40
+ if isinstance(value, IdentifierOption):
41
+ value._set_option_name(name)
42
+ return value
43
+
44
+ return TableOption._get_invalid_table_option(
45
+ name, str(type(value).__name__), [IdentifierOption.__name__, str.__name__]
46
+ )
47
+
48
+ def template(self) -> str:
49
+ return f"{self.option_name.upper()} = %s"
50
+
51
+ def _render(self, compiler) -> str:
52
+ return self.template() % self.value
53
+
54
+ def __repr__(self) -> str:
55
+ option_name = (
56
+ f", table_option_key={self.option_name}"
57
+ if not isinstance(self.option_name, NoneType)
58
+ else ""
59
+ )
60
+ return f"IdentifierOption(value='{self.value}'{option_name})"
61
+
62
+
63
+ IdentifierOptionType = Union[IdentifierOption, str, int]