Skip to content
This repository was archived by the owner on Nov 6, 2023. It is now read-only.

Commit 3f63c0a

Browse files
committed
fix(mysql): add schema as params to columns query
1 parent 4d5e064 commit 3f63c0a

File tree

2 files changed

+158
-88
lines changed

2 files changed

+158
-88
lines changed

odd_collector/adapters/mysql/repository.py

+35-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import asdict, dataclass
2+
from typing import Optional
23

34
import mysql.connector
45
from odd_collector_sdk.errors import DataSourceConnectionError
@@ -17,7 +18,7 @@ class ConnectionParams:
1718
port: int
1819
database: str
1920
user: str
20-
password: str
21+
password: Optional[str]
2122
ssl_disabled: bool
2223

2324
@classmethod
@@ -27,7 +28,7 @@ def from_config(cls, config: MySQLPlugin):
2728
port=config.port,
2829
database=config.database,
2930
user=config.user,
30-
password=config.password.get_secret_value(),
31+
password=config.password.get_secret_value() if config.password else None,
3132
ssl_disabled=config.ssl_disabled,
3233
)
3334

@@ -83,7 +84,7 @@ def get_tables(self, database: str) -> list[Table]:
8384

8485
def get_columns(self) -> list[Column]:
8586
with self.conn.cursor(dictionary=True) as cursor:
86-
cursor.execute(self.columns_query)
87+
cursor.execute(self.columns_query, (self.conn_params.database,))
8788
columns = []
8889

8990
for raw in cursor.fetchall():
@@ -105,36 +106,36 @@ def get_columns(self) -> list[Column]:
105106

106107
@property
107108
def tables_query(self):
108-
return f"""
109-
select t.table_catalog,
110-
t.table_schema,
111-
t.table_name,
112-
t.table_type,
113-
t.engine,
114-
t.version,
115-
t.row_format,
116-
t.table_rows,
117-
t.avg_row_length,
118-
t.data_length,
119-
t.max_data_length,
120-
t.index_length,
121-
t.data_free,
122-
t.auto_increment,
123-
t.create_time,
124-
t.update_time,
125-
t.check_time,
126-
t.table_collation,
127-
t.checksum,
128-
t.create_options,
129-
t.table_comment,
130-
v.view_definition
131-
from information_schema.tables t
132-
left join information_schema.views v
133-
on t.TABLE_CATALOG = v.TABLE_CATALOG and
134-
t.TABLE_SCHEMA = v.TABLE_SCHEMA and
135-
t.TABLE_NAME = v.TABLE_NAME
136-
where t.table_schema = %s
137-
order by t.table_catalog, t.table_schema, t.table_name
109+
return """
110+
select t.table_catalog,
111+
t.table_schema,
112+
t.table_name,
113+
t.table_type,
114+
t.engine,
115+
t.version,
116+
t.row_format,
117+
t.table_rows,
118+
t.avg_row_length,
119+
t.data_length,
120+
t.max_data_length,
121+
t.index_length,
122+
t.data_free,
123+
t.auto_increment,
124+
t.create_time,
125+
t.update_time,
126+
t.check_time,
127+
t.table_collation,
128+
t.checksum,
129+
t.create_options,
130+
t.table_comment,
131+
v.view_definition
132+
from information_schema.tables t
133+
left join information_schema.views v
134+
on t.TABLE_CATALOG = v.TABLE_CATALOG and
135+
t.TABLE_SCHEMA = v.TABLE_SCHEMA and
136+
t.TABLE_NAME = v.TABLE_NAME
137+
where t.table_schema = %s
138+
order by t.table_catalog, t.table_schema, t.table_name
138139
"""
139140

140141
@property
@@ -164,5 +165,6 @@ def columns_query(self):
164165
generation_expression
165166
from information_schema.columns
166167
where table_schema not in ('information_schema', 'mysql', 'performance_schema', 'sys')
168+
and table_schema = %s
167169
order by table_catalog, table_schema, table_name, ordinal_position;
168170
"""

tests/integration/test_mysql.py

+123-55
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,150 @@
1+
import odd_models
12
import pytest
23
import sqlalchemy
3-
from odd_models import DataEntity
4-
from odd_models.models import DataEntityType
54
from pydantic import SecretStr
65
from testcontainers.mysql import MySqlContainer
76

7+
from odd_collector.adapters.mysql.adapter import Adapter
8+
from odd_collector.domain.plugin import MySQLPlugin
89
from tests.integration.helpers import find_by_name, find_by_type
910

10-
create_tables = """
11-
CREATE TABLE Persons (
12-
PersonID int,
13-
LastName varchar(255),
14-
FirstName varchar(255),
15-
Address varchar(255),
16-
City varchar(255)
17-
);"""
18-
19-
create_view = """
20-
CREATE VIEW persons_names AS
21-
SELECT LastName, FirstName
22-
FROM Persons
23-
WHERE City = 'Sandnes';
24-
"""
25-
26-
create_view_from_view = """
27-
CREATE VIEW persons_last_names AS
28-
SELECT LastName
29-
FROM persons_names;
30-
"""
3111

32-
from odd_collector.adapters.mysql.adapter import Adapter
33-
from odd_collector.domain.plugin import MySQLPlugin
12+
def create_primary_schema(connection: sqlalchemy.engine.Connection):
13+
create_tables = """
14+
CREATE TABLE Persons (
15+
PersonID int,
16+
LastName varchar(255),
17+
FirstName varchar(255),
18+
Address varchar(255),
19+
City varchar(255)
20+
);"""
21+
22+
create_view = """
23+
CREATE VIEW persons_names AS
24+
SELECT LastName, FirstName
25+
FROM Persons;
26+
"""
27+
28+
create_view_from_view = """
29+
CREATE VIEW persons_last_names AS
30+
SELECT LastName
31+
FROM persons_names;
32+
"""
33+
34+
connection.exec_driver_sql(create_tables)
35+
connection.exec_driver_sql(create_view)
36+
connection.exec_driver_sql(create_view_from_view)
37+
38+
39+
def create_other_schema(connection: sqlalchemy.engine.Connection):
40+
create_other_schema = """
41+
CREATE DATABASE `other_schema`;
42+
"""
43+
44+
create_tables = """
45+
CREATE TABLE `other_schema`.`Persons` (
46+
PersonID int,
47+
LastName varchar(255),
48+
FirstName varchar(255),
49+
Address varchar(255),
50+
City varchar(255)
51+
);"""
52+
53+
create_view = """
54+
CREATE VIEW `other_schema`.`persons_names` AS
55+
SELECT LastName, FirstName
56+
FROM `other_schema`.`Persons`;
57+
"""
58+
59+
create_view_from_view = """
60+
CREATE VIEW `other_schema`.`persons_last_names` AS
61+
SELECT LastName
62+
FROM `other_schema`.`persons_names`;
63+
"""
64+
65+
connection.exec_driver_sql(create_other_schema)
66+
connection.exec_driver_sql(create_tables)
67+
connection.exec_driver_sql(create_view)
68+
connection.exec_driver_sql(create_view_from_view)
69+
70+
71+
def entities_are_unique(entities: list[odd_models.DataEntity]):
72+
return len(entities) == len({e.oddrn for e in entities})
3473

3574

36-
@pytest.mark.integration
37-
def test_mysql():
38-
with MySqlContainer() as mysql:
75+
@pytest.fixture(scope="module")
76+
def data_entities() -> odd_models.DataEntityList:
77+
with MySqlContainer(MYSQL_USER="root") as mysql:
3978
engine = sqlalchemy.create_engine(mysql.get_connection_url())
4079

4180
with engine.connect() as connection:
42-
connection.exec_driver_sql(create_tables)
43-
connection.exec_driver_sql(create_view)
44-
connection.exec_driver_sql(create_view_from_view)
81+
create_primary_schema(connection)
82+
create_other_schema(connection)
4583

4684
config = MySQLPlugin(
4785
type="mysql",
4886
name="test_mysql",
4987
database="test",
5088
password=SecretStr("test"),
51-
user="test",
89+
user="root",
5290
host=mysql.get_container_host_ip(),
53-
port=mysql.get_exposed_port(3306),
91+
port=int(mysql.get_exposed_port(3306)),
5492
)
5593

56-
data_entities = Adapter(config).get_data_entity_list()
57-
database_services: list[DataEntity] = find_by_type(
58-
data_entities, DataEntityType.DATABASE_SERVICE
59-
)
60-
assert len(database_services) == 1
61-
database_service = database_services[0]
62-
assert len(database_service.data_entity_group.entities_list) == 3
94+
return Adapter(config).get_data_entity_list()
95+
96+
97+
def test_entities_are_unique(data_entities: odd_models.DataEntityList):
98+
assert entities_are_unique(data_entities.items)
99+
100+
101+
def test_fetch_one_database_from_config(data_entities: odd_models.DataEntityList):
102+
databases: list[odd_models.DataEntity] = find_by_type(
103+
data_entities, odd_models.DataEntityType.DATABASE_SERVICE
104+
)
105+
assert len(databases) == 1
106+
database = databases[0]
107+
assert database.data_entity_group is not None
108+
assert len(database.data_entity_group.entities_list) == 3
109+
110+
entities = database.data_entity_group.entities_list
111+
assert len(entities) == len(set(entities))
112+
113+
114+
def test_fetch_only_one_table(data_entities: odd_models.DataEntityList):
115+
tables = find_by_type(data_entities, odd_models.DataEntityType.TABLE)
116+
117+
assert entities_are_unique(tables)
118+
119+
table = tables[0]
120+
121+
assert len(tables) == 1
122+
assert table.dataset is not None
123+
assert len(table.dataset.field_list) == 5
124+
assert entities_are_unique(table.dataset.field_list)
125+
126+
127+
def test_fetch_two_views(data_entities: odd_models.DataEntityList):
128+
views = find_by_type(data_entities, odd_models.DataEntityType.VIEW)
129+
assert len(views) == 2
130+
assert entities_are_unique(views)
131+
132+
133+
def test_view_depends_on_table(data_entities: odd_models.DataEntityList):
134+
table_entity = find_by_name(data_entities, "Persons")
135+
entity = find_by_name(data_entities, "persons_names")
63136

64-
tables = find_by_type(data_entities, DataEntityType.TABLE)
65-
assert len(tables) == 1
66-
table = tables[0]
67-
assert len(table.dataset.field_list) == 5
137+
assert len(entity.dataset.field_list) == 2
138+
assert len(entity.data_transformer.inputs) == 1
139+
assert entity.data_transformer.inputs[0] == table_entity.oddrn
68140

69-
views = find_by_type(data_entities, DataEntityType.VIEW)
70-
assert len(views) == 2
71141

72-
persons_view = find_by_name(data_entities, "persons_names")
73-
assert len(persons_view.dataset.field_list) == 2
74-
assert len(persons_view.data_transformer.inputs) == 1
75-
assert persons_view.data_transformer.inputs[0] == table.oddrn
142+
def test_view_depends_on_view(data_entities: odd_models.DataEntityList):
143+
view_entity = find_by_name(data_entities, "persons_names")
144+
entity = find_by_name(data_entities, "persons_last_names")
145+
assert len(entity.data_transformer.inputs) == 1
146+
assert entity.data_transformer.inputs[0] == view_entity.oddrn
76147

77-
last_names_view = find_by_name(data_entities, "persons_last_names")
78-
assert len(last_names_view.dataset.field_list) == 1
79-
assert len(last_names_view.data_transformer.inputs) == 1
80-
assert last_names_view.data_transformer.inputs[0] == persons_view.oddrn
81148

82-
assert data_entities.json()
149+
def test_decoding_data_entities(data_entities: odd_models.DataEntityList):
150+
assert data_entities.json()

0 commit comments

Comments
 (0)