dbt-cube-sync 0.1.0a8__py3-none-any.whl → 0.1.0a10__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.
Potentially problematic release.
This version of dbt-cube-sync might be problematic. Click here for more details.
- dbt_cube_sync/connectors/superset.py +9 -3
- dbt_cube_sync/core/db_inspector.py +76 -24
- {dbt_cube_sync-0.1.0a8.dist-info → dbt_cube_sync-0.1.0a10.dist-info}/METADATA +1 -1
- {dbt_cube_sync-0.1.0a8.dist-info → dbt_cube_sync-0.1.0a10.dist-info}/RECORD +6 -6
- {dbt_cube_sync-0.1.0a8.dist-info → dbt_cube_sync-0.1.0a10.dist-info}/WHEEL +0 -0
- {dbt_cube_sync-0.1.0a8.dist-info → dbt_cube_sync-0.1.0a10.dist-info}/entry_points.txt +0 -0
|
@@ -60,13 +60,19 @@ class SupersetConnector(BaseConnector):
|
|
|
60
60
|
"""Authenticate and get JWT token"""
|
|
61
61
|
login_url = f"{self.base_url}/api/v1/security/login"
|
|
62
62
|
payload = {
|
|
63
|
-
"username": self.config['username'],
|
|
64
63
|
"password": self.config['password'],
|
|
65
64
|
"provider": "db",
|
|
66
|
-
"refresh":
|
|
65
|
+
"refresh": "true",
|
|
66
|
+
"username": self.config['username']
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
response = self.session.post(login_url, json=payload)
|
|
70
|
+
if response.status_code == 401:
|
|
71
|
+
raise Exception(
|
|
72
|
+
f"Superset authentication failed (401). "
|
|
73
|
+
f"Check username/password and ensure provider='{payload['provider']}' is correct. "
|
|
74
|
+
f"Response: {response.text}"
|
|
75
|
+
)
|
|
70
76
|
response.raise_for_status()
|
|
71
77
|
|
|
72
78
|
data = response.json()
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Database inspector - fetches column types using SQLAlchemy
|
|
2
|
+
Database inspector - fetches column types using SQLAlchemy or direct SQL.
|
|
3
3
|
|
|
4
|
-
Uses
|
|
5
|
-
|
|
6
|
-
Snowflake, BigQuery, Redshift, and other databases.
|
|
4
|
+
Uses Redshift-specific queries for Redshift databases (which don't support
|
|
5
|
+
standard PostgreSQL reflection), and SQLAlchemy reflection for other databases.
|
|
7
6
|
"""
|
|
8
7
|
from typing import Dict, Optional
|
|
9
|
-
from sqlalchemy import create_engine, MetaData, Table
|
|
8
|
+
from sqlalchemy import create_engine, MetaData, Table, text
|
|
10
9
|
from sqlalchemy.engine import Engine
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class DatabaseInspector:
|
|
14
|
-
"""Inspects database schema to extract column type information
|
|
13
|
+
"""Inspects database schema to extract column type information."""
|
|
15
14
|
|
|
16
15
|
def __init__(self, sqlalchemy_uri: str):
|
|
17
16
|
"""
|
|
@@ -20,8 +19,9 @@ class DatabaseInspector:
|
|
|
20
19
|
Args:
|
|
21
20
|
sqlalchemy_uri: SQLAlchemy connection URI (e.g., postgresql://user:pass@host:port/db)
|
|
22
21
|
"""
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
self.is_redshift = 'redshift' in sqlalchemy_uri.lower()
|
|
23
|
+
|
|
24
|
+
if self.is_redshift:
|
|
25
25
|
self.engine: Engine = create_engine(
|
|
26
26
|
sqlalchemy_uri,
|
|
27
27
|
connect_args={'sslmode': 'prefer'}
|
|
@@ -30,13 +30,62 @@ class DatabaseInspector:
|
|
|
30
30
|
self.engine: Engine = create_engine(sqlalchemy_uri)
|
|
31
31
|
|
|
32
32
|
self.metadata = MetaData()
|
|
33
|
-
self._table_cache: Dict[str,
|
|
33
|
+
self._table_cache: Dict[str, Dict[str, str]] = {}
|
|
34
|
+
|
|
35
|
+
def _get_redshift_columns(self, schema: str, table_name: str) -> Dict[str, str]:
|
|
36
|
+
"""
|
|
37
|
+
Get column types from Redshift using LIMIT 0 query (fastest method).
|
|
38
|
+
|
|
39
|
+
Executes SELECT * FROM table LIMIT 0 and reads column types from cursor description.
|
|
40
|
+
This is very fast because it doesn't scan any data - just returns metadata.
|
|
41
|
+
"""
|
|
42
|
+
columns = {}
|
|
43
|
+
|
|
44
|
+
# LIMIT 0 query - returns no rows but gives us column metadata
|
|
45
|
+
query = text(f'SELECT * FROM "{schema}"."{table_name}" LIMIT 0')
|
|
46
|
+
|
|
47
|
+
# Redshift type OID to name mapping (common types)
|
|
48
|
+
redshift_type_map = {
|
|
49
|
+
16: 'boolean',
|
|
50
|
+
20: 'bigint',
|
|
51
|
+
21: 'smallint',
|
|
52
|
+
23: 'integer',
|
|
53
|
+
25: 'text',
|
|
54
|
+
700: 'real',
|
|
55
|
+
701: 'double precision',
|
|
56
|
+
1042: 'char',
|
|
57
|
+
1043: 'varchar',
|
|
58
|
+
1082: 'date',
|
|
59
|
+
1083: 'time',
|
|
60
|
+
1114: 'timestamp',
|
|
61
|
+
1184: 'timestamptz',
|
|
62
|
+
1700: 'numeric',
|
|
63
|
+
2950: 'uuid',
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
with self.engine.connect() as conn:
|
|
68
|
+
result = conn.execute(query)
|
|
69
|
+
# Get column info from cursor description
|
|
70
|
+
# Format: (name, type_code, display_size, internal_size, precision, scale, null_ok)
|
|
71
|
+
if result.cursor and result.cursor.description:
|
|
72
|
+
for col_desc in result.cursor.description:
|
|
73
|
+
col_name = col_desc[0]
|
|
74
|
+
type_code = col_desc[1]
|
|
75
|
+
# Map type code to type name, fallback to 'varchar' if unknown
|
|
76
|
+
col_type = redshift_type_map.get(type_code, 'varchar')
|
|
77
|
+
columns[col_name] = col_type
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
print(f"Warning: Could not inspect Redshift table {schema}.{table_name}: {e}")
|
|
81
|
+
|
|
82
|
+
return columns
|
|
34
83
|
|
|
35
84
|
def get_table_columns(self, schema: str, table_name: str) -> Dict[str, str]:
|
|
36
85
|
"""
|
|
37
86
|
Get column names and their data types for a specific table.
|
|
38
87
|
|
|
39
|
-
Uses SQLAlchemy
|
|
88
|
+
Uses Redshift-specific queries for Redshift, SQLAlchemy reflection for others.
|
|
40
89
|
|
|
41
90
|
Args:
|
|
42
91
|
schema: Database schema name
|
|
@@ -45,30 +94,33 @@ class DatabaseInspector:
|
|
|
45
94
|
Returns:
|
|
46
95
|
Dictionary mapping column names to data types
|
|
47
96
|
"""
|
|
48
|
-
columns = {}
|
|
49
97
|
cache_key = f"{schema}.{table_name}"
|
|
50
98
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
99
|
+
# Check cache first
|
|
100
|
+
if cache_key in self._table_cache:
|
|
101
|
+
return self._table_cache[cache_key]
|
|
102
|
+
|
|
103
|
+
columns = {}
|
|
104
|
+
|
|
105
|
+
if self.is_redshift:
|
|
106
|
+
# Use Redshift-specific query
|
|
107
|
+
columns = self._get_redshift_columns(schema, table_name)
|
|
108
|
+
else:
|
|
109
|
+
# Use standard SQLAlchemy reflection
|
|
110
|
+
try:
|
|
57
111
|
table = Table(
|
|
58
112
|
table_name,
|
|
59
113
|
self.metadata,
|
|
60
114
|
autoload_with=self.engine,
|
|
61
115
|
schema=schema
|
|
62
116
|
)
|
|
63
|
-
|
|
117
|
+
for column in table.columns:
|
|
118
|
+
columns[column.name] = str(column.type)
|
|
64
119
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
columns[column.name] = str(column.type)
|
|
68
|
-
|
|
69
|
-
except Exception as e:
|
|
70
|
-
print(f"Warning: Could not inspect table {schema}.{table_name}: {e}")
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print(f"Warning: Could not inspect table {schema}.{table_name}: {e}")
|
|
71
122
|
|
|
123
|
+
self._table_cache[cache_key] = columns
|
|
72
124
|
return columns
|
|
73
125
|
|
|
74
126
|
def reflect_multiple_tables(
|
|
@@ -4,15 +4,15 @@ dbt_cube_sync/config.py,sha256=qhGE7CxTmh0RhPizgd3x3Yj-3L2LoC00UQIDT0q9FlQ,3858
|
|
|
4
4
|
dbt_cube_sync/connectors/__init__.py,sha256=NG6tYZ3CYD5bG_MfNLZrUM8YoBEKArG8-AOmJ8pwvQI,52
|
|
5
5
|
dbt_cube_sync/connectors/base.py,sha256=JLzerxJdt34z0kWuyieL6UQhf5_dUYPGmwkiRWBuSPY,2802
|
|
6
6
|
dbt_cube_sync/connectors/powerbi.py,sha256=2Y8fTfh_6Q_Myma1ymipPh1U3HsfQKcktVequXXnIXI,1275
|
|
7
|
-
dbt_cube_sync/connectors/superset.py,sha256=
|
|
7
|
+
dbt_cube_sync/connectors/superset.py,sha256=D_pWTN0F84mUosnsm-NG_v9IlVE2dviIDh08WxHEOIA,21709
|
|
8
8
|
dbt_cube_sync/connectors/tableau.py,sha256=jKve1zErzTbgPOtmPB92ZwZl4I6uEySedM51JiwlGrE,1261
|
|
9
9
|
dbt_cube_sync/core/__init__.py,sha256=kgsawtU5dqEvnHz6dU8qwJbH3rtIV7QlK2MhtYVDCaY,46
|
|
10
10
|
dbt_cube_sync/core/cube_generator.py,sha256=DtmaA_dtWmBVJnSWHVoQi-3KEsRc0axHZpCUEcKeYAk,11061
|
|
11
|
-
dbt_cube_sync/core/db_inspector.py,sha256=
|
|
11
|
+
dbt_cube_sync/core/db_inspector.py,sha256=V_cd12FBXj-1gB2JZeYmkQluUO-UYufy_tvfYoJXCGI,5073
|
|
12
12
|
dbt_cube_sync/core/dbt_parser.py,sha256=KbhDoB0ULP6JDUPZPDVbm9yCtRKrW17ptGoJvVLtueY,12763
|
|
13
13
|
dbt_cube_sync/core/models.py,sha256=2s5iZ9MEBGfSzkB4HJB5vG0mZqNXNJSfAD3Byw1IVe4,3203
|
|
14
14
|
dbt_cube_sync/core/state_manager.py,sha256=7uXJtlZBIWj6s6XgAhNlP6UHdfhH0y461iyQlfidqGI,7233
|
|
15
|
-
dbt_cube_sync-0.1.
|
|
16
|
-
dbt_cube_sync-0.1.
|
|
17
|
-
dbt_cube_sync-0.1.
|
|
18
|
-
dbt_cube_sync-0.1.
|
|
15
|
+
dbt_cube_sync-0.1.0a10.dist-info/METADATA,sha256=Foy8KI7-ILdZwTvejfjukArtnGNJJg85vwIlQoMS31w,10681
|
|
16
|
+
dbt_cube_sync-0.1.0a10.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
17
|
+
dbt_cube_sync-0.1.0a10.dist-info/entry_points.txt,sha256=iEAB_nZ1AoSeFwSHPY2tr02xmTHLVFKp5CJeFh0AfCw,56
|
|
18
|
+
dbt_cube_sync-0.1.0a10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|