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,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
|