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.
- snowflake/sqlalchemy/__init__.py +162 -0
- snowflake/sqlalchemy/_constants.py +14 -0
- snowflake/sqlalchemy/base.py +1188 -0
- snowflake/sqlalchemy/compat.py +36 -0
- snowflake/sqlalchemy/custom_commands.py +627 -0
- snowflake/sqlalchemy/custom_types.py +155 -0
- snowflake/sqlalchemy/exc.py +82 -0
- snowflake/sqlalchemy/functions.py +16 -0
- snowflake/sqlalchemy/parser/custom_type_parser.py +245 -0
- snowflake/sqlalchemy/provision.py +12 -0
- snowflake/sqlalchemy/requirements.py +313 -0
- snowflake/sqlalchemy/snowdialect.py +1029 -0
- snowflake/sqlalchemy/sql/__init__.py +3 -0
- snowflake/sqlalchemy/sql/custom_schema/__init__.py +9 -0
- snowflake/sqlalchemy/sql/custom_schema/clustered_table.py +37 -0
- snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +127 -0
- snowflake/sqlalchemy/sql/custom_schema/custom_table_prefix.py +13 -0
- snowflake/sqlalchemy/sql/custom_schema/dynamic_table.py +117 -0
- snowflake/sqlalchemy/sql/custom_schema/hybrid_table.py +63 -0
- snowflake/sqlalchemy/sql/custom_schema/iceberg_table.py +102 -0
- snowflake/sqlalchemy/sql/custom_schema/options/__init__.py +33 -0
- snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +63 -0
- snowflake/sqlalchemy/sql/custom_schema/options/cluster_by_option.py +58 -0
- snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +63 -0
- snowflake/sqlalchemy/sql/custom_schema/options/invalid_table_option.py +25 -0
- snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +65 -0
- snowflake/sqlalchemy/sql/custom_schema/options/keywords.py +14 -0
- snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +67 -0
- snowflake/sqlalchemy/sql/custom_schema/options/table_option.py +84 -0
- snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +94 -0
- snowflake/sqlalchemy/sql/custom_schema/snowflake_table.py +70 -0
- snowflake/sqlalchemy/sql/custom_schema/table_from_query.py +54 -0
- snowflake/sqlalchemy/util.py +344 -0
- snowflake/sqlalchemy/version.py +6 -0
- snowflake_sqlalchemy-1.7.3.dist-info/METADATA +737 -0
- snowflake_sqlalchemy-1.7.3.dist-info/RECORD +39 -0
- snowflake_sqlalchemy-1.7.3.dist-info/WHEEL +4 -0
- snowflake_sqlalchemy-1.7.3.dist-info/entry_points.txt +2 -0
- snowflake_sqlalchemy-1.7.3.dist-info/licenses/LICENSE.txt +202 -0
|
@@ -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,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]
|