al-data-utils 0.3.1__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.
- al_data_utils/__init__.py +10 -0
- al_data_utils/airflow_utils/__init__.py +13 -0
- al_data_utils/airflow_utils/airflow_utils.py +83 -0
- al_data_utils/azure_as_utils/__init__.py +31 -0
- al_data_utils/azure_as_utils/auth.py +82 -0
- al_data_utils/azure_as_utils/azure_as_utils.py +223 -0
- al_data_utils/azure_as_utils/client.py +158 -0
- al_data_utils/azure_as_utils/errors.py +78 -0
- al_data_utils/azure_as_utils/models.py +265 -0
- al_data_utils/azure_as_utils/poller.py +121 -0
- al_data_utils/backup_utils/__init__.py +9 -0
- al_data_utils/backup_utils/mssql/dacpac/__init__.py +1 -0
- al_data_utils/backup_utils/mssql/dacpac/sqlpackage.py +207 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/__init__.py +8 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/config.py +150 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/emit/__init__.py +11 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/emit/foreign_keys.py +38 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/emit/indexes.py +37 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/emit/sql_objects.py +17 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/emit/tables.py +139 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/__init__.py +19 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/foreign_keys.py +173 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/functions.py +133 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/indexes.py +190 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/sequences.py +180 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/stored_procedures.py +313 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/synonyms.py +135 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/tables.py +251 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/introspect/views.py +244 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/io/__init__.py +10 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/io/layout.py +28 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/io/writer.py +40 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/models/__init__.py +14 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/models/foreign_key_def.py +19 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/models/index_def.py +19 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/models/sql_object_def.py +11 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/models/table_column_def.py +27 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/models/table_def.py +22 -0
- al_data_utils/backup_utils/mssql/schema_snapshot/snapshot_processor.py +754 -0
- al_data_utils/backup_utils/mssql/scripter/__init__.py +1 -0
- al_data_utils/config_utils/__init__.py +6 -0
- al_data_utils/config_utils/config_utils.py +31 -0
- al_data_utils/files_sync_utils/__init__.py +15 -0
- al_data_utils/files_sync_utils/files_sync_utils.py +567 -0
- al_data_utils/geo_utils/__init__.py +0 -0
- al_data_utils/git_utils/__init__.py +27 -0
- al_data_utils/git_utils/git_utils.py +142 -0
- al_data_utils/logging_utils/__init__.py +3 -0
- al_data_utils/logging_utils/logging_utils.py +39 -0
- al_data_utils/ones_api_utils/__init__.py +3 -0
- al_data_utils/ones_api_utils/ones_api_utils.py +61 -0
- al_data_utils/sql_utils/__init__.py +3 -0
- al_data_utils/sql_utils/sql_utils.py +158 -0
- al_data_utils/tg_utils/__init__.py +3 -0
- al_data_utils/tg_utils/tg_utils.py +45 -0
- al_data_utils-0.3.1.dist-info/METADATA +157 -0
- al_data_utils-0.3.1.dist-info/RECORD +60 -0
- al_data_utils-0.3.1.dist-info/WHEEL +5 -0
- al_data_utils-0.3.1.dist-info/licenses/LICENSE +21 -0
- al_data_utils-0.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from al_data_utils.sql_utils.sql_utils import UnifiedSQLClient
|
|
7
|
+
from ..models import SqlObjectDef
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_SQL_VIEWS = r"""
|
|
11
|
+
select
|
|
12
|
+
s.name as schema_name,
|
|
13
|
+
v.name as object_name,
|
|
14
|
+
m.definition as definition
|
|
15
|
+
from sys.views v
|
|
16
|
+
inner join sys.schemas s
|
|
17
|
+
on s.schema_id = v.schema_id
|
|
18
|
+
inner join sys.sql_modules m
|
|
19
|
+
on m.object_id = v.object_id
|
|
20
|
+
where v.is_ms_shipped = 0
|
|
21
|
+
order by s.name, v.name;
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_SQL_VIEW_PERMISSIONS = r"""
|
|
26
|
+
select
|
|
27
|
+
s.name as schema_name,
|
|
28
|
+
v.name as object_name,
|
|
29
|
+
dp.permission_name,
|
|
30
|
+
dp.state,
|
|
31
|
+
grantee.name as grantee_name,
|
|
32
|
+
c.name as column_name,
|
|
33
|
+
cast(case when dp.minor_id > 0 then 1 else 0 end as bit) as is_column_level
|
|
34
|
+
from sys.database_permissions dp
|
|
35
|
+
inner join sys.views v
|
|
36
|
+
on v.object_id = dp.major_id
|
|
37
|
+
inner join sys.schemas s
|
|
38
|
+
on s.schema_id = v.schema_id
|
|
39
|
+
inner join sys.database_principals grantee
|
|
40
|
+
on grantee.principal_id = dp.grantee_principal_id
|
|
41
|
+
left outer join sys.columns c
|
|
42
|
+
on c.object_id = dp.major_id
|
|
43
|
+
and c.column_id = dp.minor_id
|
|
44
|
+
where dp.class = 1
|
|
45
|
+
and v.is_ms_shipped = 0
|
|
46
|
+
and dp.state in ('G', 'W', 'D')
|
|
47
|
+
order by s.name, v.name, dp.permission_name, dp.state, grantee.name, c.column_id;
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _normalized_set(values: set[str] | None) -> set[str]:
|
|
52
|
+
if not values:
|
|
53
|
+
return set()
|
|
54
|
+
return {str(v).strip().lower() for v in values if str(v).strip()}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _split_qualified(values: set[str]) -> list[tuple[str, str]]:
|
|
58
|
+
pairs: list[tuple[str, str]] = []
|
|
59
|
+
for v in sorted(values):
|
|
60
|
+
if '.' not in v:
|
|
61
|
+
continue
|
|
62
|
+
schema, name = v.split('.', 1)
|
|
63
|
+
schema = schema.strip().lower()
|
|
64
|
+
name = name.strip().lower()
|
|
65
|
+
if schema and name:
|
|
66
|
+
pairs.append((schema, name))
|
|
67
|
+
return pairs
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _build_view_filter_sql_and_params(
|
|
71
|
+
*,
|
|
72
|
+
include_schemas: set[str] | None = None,
|
|
73
|
+
exclude_schemas: set[str] | None = None,
|
|
74
|
+
include_views: set[str] | None = None,
|
|
75
|
+
exclude_views: set[str] | None = None,
|
|
76
|
+
) -> tuple[str, dict[str, str]]:
|
|
77
|
+
inc_schemas = sorted(_normalized_set(include_schemas))
|
|
78
|
+
exc_schemas = sorted(_normalized_set(exclude_schemas))
|
|
79
|
+
inc_views = _split_qualified(_normalized_set(include_views))
|
|
80
|
+
exc_views = _split_qualified(_normalized_set(exclude_views))
|
|
81
|
+
|
|
82
|
+
params: dict[str, str] = {}
|
|
83
|
+
clauses: list[str] = []
|
|
84
|
+
|
|
85
|
+
if inc_schemas:
|
|
86
|
+
names: list[str] = []
|
|
87
|
+
for i, schema in enumerate(inc_schemas):
|
|
88
|
+
key = f'inc_schema_{i}'
|
|
89
|
+
params[key] = schema
|
|
90
|
+
names.append(f':{key}')
|
|
91
|
+
clauses.append(f'lower(s.name) in ({", ".join(names)})')
|
|
92
|
+
|
|
93
|
+
if exc_schemas:
|
|
94
|
+
names = []
|
|
95
|
+
for i, schema in enumerate(exc_schemas):
|
|
96
|
+
key = f'exc_schema_{i}'
|
|
97
|
+
params[key] = schema
|
|
98
|
+
names.append(f':{key}')
|
|
99
|
+
clauses.append(f'lower(s.name) not in ({", ".join(names)})')
|
|
100
|
+
|
|
101
|
+
if inc_views:
|
|
102
|
+
parts: list[str] = []
|
|
103
|
+
for i, (schema, name) in enumerate(inc_views):
|
|
104
|
+
sk = f'inc_view_schema_{i}'
|
|
105
|
+
nk = f'inc_view_name_{i}'
|
|
106
|
+
params[sk] = schema
|
|
107
|
+
params[nk] = name
|
|
108
|
+
parts.append(f'(lower(s.name) = :{sk} and lower(v.name) = :{nk})')
|
|
109
|
+
clauses.append(f'({" or ".join(parts)})')
|
|
110
|
+
|
|
111
|
+
if exc_views:
|
|
112
|
+
parts = []
|
|
113
|
+
for i, (schema, name) in enumerate(exc_views):
|
|
114
|
+
sk = f'exc_view_schema_{i}'
|
|
115
|
+
nk = f'exc_view_name_{i}'
|
|
116
|
+
params[sk] = schema
|
|
117
|
+
params[nk] = name
|
|
118
|
+
parts.append(f'(lower(s.name) = :{sk} and lower(v.name) = :{nk})')
|
|
119
|
+
clauses.append(f'not ({" or ".join(parts)})')
|
|
120
|
+
|
|
121
|
+
if not clauses:
|
|
122
|
+
return '', {}
|
|
123
|
+
|
|
124
|
+
return '\n and ' + '\n and '.join(clauses), params
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _inject_view_filters(sql: str, filters_sql: str) -> str:
|
|
128
|
+
if not filters_sql:
|
|
129
|
+
return sql
|
|
130
|
+
return sql.replace(
|
|
131
|
+
'where v.is_ms_shipped = 0', f'where v.is_ms_shipped = 0{filters_sql}'
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _q(name: str) -> str:
|
|
136
|
+
n = name.strip()
|
|
137
|
+
if re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', n):
|
|
138
|
+
return n
|
|
139
|
+
return '[' + n.replace(']', ']]') + ']'
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _build_permission_statements(
|
|
143
|
+
sql_client: UnifiedSQLClient,
|
|
144
|
+
*,
|
|
145
|
+
filters_sql: str = '',
|
|
146
|
+
params: dict[str, str] | None = None,
|
|
147
|
+
) -> dict[tuple[str, str], list[str]]:
|
|
148
|
+
rows = sql_client.execute_command_with_result_to_dicts(
|
|
149
|
+
_inject_view_filters(_SQL_VIEW_PERMISSIONS, filters_sql), params or {}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
object_perms: dict[tuple[str, str], list[str]] = defaultdict(list)
|
|
153
|
+
column_perms: dict[tuple[str, str], list[str]] = defaultdict(list)
|
|
154
|
+
|
|
155
|
+
# key: (schema, object, state, perm, grantee)
|
|
156
|
+
col_group: dict[tuple[str, str, str, str, str], list[str]] = defaultdict(list)
|
|
157
|
+
|
|
158
|
+
for r in rows:
|
|
159
|
+
schema_name = str(r['schema_name'])
|
|
160
|
+
object_name = str(r['object_name'])
|
|
161
|
+
permission_name = str(r['permission_name']).lower()
|
|
162
|
+
state = str(r['state']).upper()
|
|
163
|
+
grantee = str(r['grantee_name'])
|
|
164
|
+
is_column_level = bool(r['is_column_level'])
|
|
165
|
+
column_name = (
|
|
166
|
+
None if r.get('column_name') is None else str(r.get('column_name'))
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
target = f'{_q(schema_name)}.{_q(object_name)}'
|
|
170
|
+
grantee_sql = _q(grantee)
|
|
171
|
+
|
|
172
|
+
verb = 'grant' if state in {'G', 'W'} else 'deny'
|
|
173
|
+
with_grant = ' with grant option' if state == 'W' else ''
|
|
174
|
+
|
|
175
|
+
if is_column_level and column_name:
|
|
176
|
+
group_key = (schema_name, object_name, state, permission_name, grantee)
|
|
177
|
+
col_group[group_key].append(column_name)
|
|
178
|
+
else:
|
|
179
|
+
object_perms[(schema_name, object_name)].append(
|
|
180
|
+
f'{verb} {permission_name} on {target} to {grantee_sql}{with_grant}'
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
for (schema_name, object_name, state, permission_name, grantee), cols in sorted(
|
|
184
|
+
col_group.items()
|
|
185
|
+
):
|
|
186
|
+
target = f'{_q(schema_name)}.{_q(object_name)}'
|
|
187
|
+
grantee_sql = _q(grantee)
|
|
188
|
+
verb = 'grant' if state in {'G', 'W'} else 'deny'
|
|
189
|
+
with_grant = ' with grant option' if state == 'W' else ''
|
|
190
|
+
seen_cols: set[str] = set()
|
|
191
|
+
ordered_cols: list[str] = []
|
|
192
|
+
for c in cols:
|
|
193
|
+
lc = c.lower()
|
|
194
|
+
if lc in seen_cols:
|
|
195
|
+
continue
|
|
196
|
+
seen_cols.add(lc)
|
|
197
|
+
ordered_cols.append(c)
|
|
198
|
+
|
|
199
|
+
col_list = ', '.join(_q(c) for c in ordered_cols)
|
|
200
|
+
column_perms[(schema_name, object_name)].append(
|
|
201
|
+
f'{verb} {permission_name} ({col_list}) on {target} to {grantee_sql}{with_grant}'
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
merged: dict[tuple[str, str], list[str]] = defaultdict(list)
|
|
205
|
+
keys = set(object_perms.keys()) | set(column_perms.keys())
|
|
206
|
+
for k in keys:
|
|
207
|
+
merged[k].extend(object_perms.get(k, []))
|
|
208
|
+
merged[k].extend(column_perms.get(k, []))
|
|
209
|
+
|
|
210
|
+
return merged
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_views(
|
|
214
|
+
sql_client: UnifiedSQLClient,
|
|
215
|
+
*,
|
|
216
|
+
include_schemas: set[str] | None = None,
|
|
217
|
+
exclude_schemas: set[str] | None = None,
|
|
218
|
+
include_views: set[str] | None = None,
|
|
219
|
+
exclude_views: set[str] | None = None,
|
|
220
|
+
) -> list[SqlObjectDef]:
|
|
221
|
+
filters_sql, params = _build_view_filter_sql_and_params(
|
|
222
|
+
include_schemas=include_schemas,
|
|
223
|
+
exclude_schemas=exclude_schemas,
|
|
224
|
+
include_views=include_views,
|
|
225
|
+
exclude_views=exclude_views,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
rows = sql_client.execute_command_with_result_to_dicts(
|
|
229
|
+
_inject_view_filters(_SQL_VIEWS, filters_sql), params
|
|
230
|
+
)
|
|
231
|
+
perms_by_view = _build_permission_statements(
|
|
232
|
+
sql_client, filters_sql=filters_sql, params=params
|
|
233
|
+
)
|
|
234
|
+
return [
|
|
235
|
+
SqlObjectDef(
|
|
236
|
+
schema_name=str(r['schema_name']),
|
|
237
|
+
object_name=str(r['object_name']),
|
|
238
|
+
definition=str(r['definition'] or ''),
|
|
239
|
+
permissions_sql=perms_by_view.get(
|
|
240
|
+
(str(r['schema_name']), str(r['object_name'])), []
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
for r in rows
|
|
244
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .layout import table_file_path, object_file_path
|
|
2
|
+
from .writer import write_text_atomic, set_mtime_like, read_text_if_exists
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
'table_file_path',
|
|
6
|
+
'object_file_path',
|
|
7
|
+
'write_text_atomic',
|
|
8
|
+
'set_mtime_like',
|
|
9
|
+
'read_text_if_exists',
|
|
10
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def object_file_path(
|
|
7
|
+
*,
|
|
8
|
+
root: Path,
|
|
9
|
+
root_folder_name: str,
|
|
10
|
+
schema: str,
|
|
11
|
+
object_folder: str,
|
|
12
|
+
object_name: str,
|
|
13
|
+
) -> Path:
|
|
14
|
+
return (
|
|
15
|
+
root / root_folder_name / schema / object_folder / f'{schema}.{object_name}.sql'
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def table_file_path(
|
|
20
|
+
*, root: Path, root_folder_name: str, schema: str, table: str
|
|
21
|
+
) -> Path:
|
|
22
|
+
return object_file_path(
|
|
23
|
+
root=root,
|
|
24
|
+
root_folder_name=root_folder_name,
|
|
25
|
+
schema=schema,
|
|
26
|
+
object_folder='tables',
|
|
27
|
+
object_name=table,
|
|
28
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _normalize_text(text: str, newline: str) -> str:
|
|
8
|
+
t = text.replace("\r\n", "\n").replace("\r", "\n")
|
|
9
|
+
if newline != "\n":
|
|
10
|
+
t = t.replace("\n", newline)
|
|
11
|
+
if not t.endswith(newline):
|
|
12
|
+
t += newline
|
|
13
|
+
return t
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def write_text_atomic(path: Path, text: str, *, encoding: str, newline: str) -> None:
|
|
17
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
19
|
+
data = _normalize_text(text, newline)
|
|
20
|
+
|
|
21
|
+
with open(tmp, "w", encoding=encoding, newline="") as f:
|
|
22
|
+
f.write(data)
|
|
23
|
+
|
|
24
|
+
os.replace(tmp, path)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def set_mtime_like(path: Path, like_path: Path) -> None:
|
|
28
|
+
try:
|
|
29
|
+
st = like_path.stat()
|
|
30
|
+
m = int(st.st_mtime)
|
|
31
|
+
os.utime(path, (m, m))
|
|
32
|
+
except FileNotFoundError:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def read_text_if_exists(path: Path, *, encoding: str) -> str | None:
|
|
37
|
+
try:
|
|
38
|
+
return path.read_text(encoding=encoding)
|
|
39
|
+
except FileNotFoundError:
|
|
40
|
+
return None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .index_def import IndexDef
|
|
2
|
+
from .foreign_key_def import ForeignKeyDef
|
|
3
|
+
from .table_column_def import TableColumnDef
|
|
4
|
+
from .table_def import TableDef, PrimaryKeyDef
|
|
5
|
+
from .sql_object_def import SqlObjectDef
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'IndexDef',
|
|
9
|
+
'ForeignKeyDef',
|
|
10
|
+
'TableColumnDef',
|
|
11
|
+
'TableDef',
|
|
12
|
+
'PrimaryKeyDef',
|
|
13
|
+
'SqlObjectDef',
|
|
14
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class ForeignKeyDef:
|
|
8
|
+
parent_schema: str
|
|
9
|
+
parent_table: str
|
|
10
|
+
fk_name: str
|
|
11
|
+
referenced_schema: str
|
|
12
|
+
referenced_table: str
|
|
13
|
+
on_delete: str
|
|
14
|
+
on_update: str
|
|
15
|
+
is_not_for_replication: bool
|
|
16
|
+
is_disabled: bool
|
|
17
|
+
is_not_trusted: bool
|
|
18
|
+
parent_columns: str # formatted: [col1], [col2]
|
|
19
|
+
referenced_columns: str # formatted: [col1], [col2]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class IndexDef:
|
|
9
|
+
schema_name: str
|
|
10
|
+
table_name: str
|
|
11
|
+
index_name: str
|
|
12
|
+
is_unique: bool
|
|
13
|
+
type_desc: str # CLUSTERED / NONCLUSTERED
|
|
14
|
+
has_filter: bool
|
|
15
|
+
filter_definition: Optional[str]
|
|
16
|
+
is_disabled: bool
|
|
17
|
+
is_hypothetical: bool
|
|
18
|
+
key_columns: str # formatted: [col] asc, [col2] desc
|
|
19
|
+
include_columns: str # formatted: [col], [col2]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class TableColumnDef:
|
|
8
|
+
schema_name: str
|
|
9
|
+
table_name: str
|
|
10
|
+
column_name: str
|
|
11
|
+
ordinal_position: int
|
|
12
|
+
|
|
13
|
+
data_type: str
|
|
14
|
+
max_length: int | None
|
|
15
|
+
precision: int | None
|
|
16
|
+
scale: int | None
|
|
17
|
+
|
|
18
|
+
is_nullable: bool
|
|
19
|
+
is_identity: bool
|
|
20
|
+
identity_seed: int | None
|
|
21
|
+
identity_increment: int | None
|
|
22
|
+
|
|
23
|
+
is_computed: bool
|
|
24
|
+
computed_definition: str | None
|
|
25
|
+
collation_name: str | None
|
|
26
|
+
default_constraint_name: str | None
|
|
27
|
+
default_definition: str | None
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from .table_column_def import TableColumnDef
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class PrimaryKeyDef:
|
|
10
|
+
schema_name: str
|
|
11
|
+
table_name: str
|
|
12
|
+
constraint_name: str
|
|
13
|
+
key_columns: str
|
|
14
|
+
type_desc: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class TableDef:
|
|
19
|
+
schema_name: str
|
|
20
|
+
table_name: str
|
|
21
|
+
columns: list[TableColumnDef]
|
|
22
|
+
primary_key: PrimaryKeyDef | None = None
|