qodalis-cli-server-plugin-data-explorer-mysql 1.0.0b18__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.
@@ -0,0 +1,5 @@
1
+ """Qodalis CLI Data Explorer MySQL plugin — MySQL provider."""
2
+
3
+ from .mysql_provider import MysqlDataExplorerProvider
4
+
5
+ __all__ = ["MysqlDataExplorerProvider"]
@@ -0,0 +1,146 @@
1
+ """MySQL-backed Data Explorer provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from urllib.parse import urlparse
7
+
8
+ import aiomysql
9
+
10
+ from qodalis_cli_server_abstractions import (
11
+ DataExplorerExecutionContext,
12
+ DataExplorerProviderOptions,
13
+ DataExplorerResult,
14
+ DataExplorerSchemaColumn,
15
+ DataExplorerSchemaResult,
16
+ DataExplorerSchemaTable,
17
+ IDataExplorerProvider,
18
+ )
19
+
20
+
21
+ class MysqlDataExplorerProvider(IDataExplorerProvider):
22
+ """Executes SQL queries against a MySQL database."""
23
+
24
+ def __init__(self, connection_string: str) -> None:
25
+ parsed = urlparse(connection_string)
26
+ self._host = parsed.hostname or "localhost"
27
+ self._port = parsed.port or 3306
28
+ self._user = parsed.username or "root"
29
+ self._password = parsed.password or ""
30
+ self._db = parsed.path.lstrip("/") if parsed.path else None
31
+
32
+ async def execute_async(
33
+ self, context: DataExplorerExecutionContext
34
+ ) -> DataExplorerResult:
35
+ """Execute a SQL query against the MySQL database."""
36
+ start = time.monotonic()
37
+ conn: aiomysql.Connection | None = None
38
+ try:
39
+ conn = await aiomysql.connect(
40
+ host=self._host,
41
+ port=self._port,
42
+ user=self._user,
43
+ password=self._password,
44
+ db=self._db,
45
+ )
46
+ async with conn.cursor() as cursor:
47
+ await cursor.execute(context.query, context.parameters or None)
48
+
49
+ if cursor.description:
50
+ columns = [desc[0] for desc in cursor.description]
51
+ rows_raw = await cursor.fetchall()
52
+ rows = [list(row) for row in rows_raw]
53
+ return DataExplorerResult(
54
+ success=True,
55
+ source=context.options.name,
56
+ language=context.options.language,
57
+ default_output_format=context.options.default_output_format,
58
+ execution_time=int((time.monotonic() - start) * 1000),
59
+ columns=columns,
60
+ rows=rows,
61
+ row_count=len(rows),
62
+ truncated=False,
63
+ error=None,
64
+ )
65
+ else:
66
+ await conn.commit()
67
+ return DataExplorerResult(
68
+ success=True,
69
+ source=context.options.name,
70
+ language=context.options.language,
71
+ default_output_format=context.options.default_output_format,
72
+ execution_time=int((time.monotonic() - start) * 1000),
73
+ columns=["changes"],
74
+ rows=[[cursor.rowcount]],
75
+ row_count=1,
76
+ truncated=False,
77
+ error=None,
78
+ )
79
+ except Exception as exc:
80
+ return DataExplorerResult(
81
+ success=False,
82
+ source=context.options.name,
83
+ language=context.options.language,
84
+ default_output_format=context.options.default_output_format,
85
+ execution_time=int((time.monotonic() - start) * 1000),
86
+ columns=None,
87
+ rows=[],
88
+ row_count=0,
89
+ truncated=False,
90
+ error=str(exc),
91
+ )
92
+ finally:
93
+ if conn is not None:
94
+ conn.close()
95
+
96
+ async def get_schema_async(
97
+ self, options: DataExplorerProviderOptions
98
+ ) -> DataExplorerSchemaResult | None:
99
+ """Return the database schema (tables, views, and their columns)."""
100
+ conn: aiomysql.Connection | None = None
101
+ try:
102
+ conn = await aiomysql.connect(
103
+ host=self._host,
104
+ port=self._port,
105
+ user=self._user,
106
+ password=self._password,
107
+ db=self._db,
108
+ )
109
+ async with conn.cursor() as cursor:
110
+ await cursor.execute(
111
+ "SELECT TABLE_NAME, TABLE_TYPE "
112
+ "FROM information_schema.tables "
113
+ "WHERE TABLE_SCHEMA = DATABASE() "
114
+ "ORDER BY TABLE_NAME"
115
+ )
116
+ table_rows = await cursor.fetchall()
117
+
118
+ tables: list[DataExplorerSchemaTable] = []
119
+ for table_name, table_type in table_rows:
120
+ await cursor.execute(
121
+ "SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY "
122
+ "FROM information_schema.columns "
123
+ "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
124
+ "ORDER BY ORDINAL_POSITION",
125
+ (table_name,),
126
+ )
127
+ col_rows = await cursor.fetchall()
128
+ columns = [
129
+ DataExplorerSchemaColumn(
130
+ name=col_name,
131
+ type=data_type or "TEXT",
132
+ nullable=is_nullable == "YES",
133
+ primary_key=col_key == "PRI",
134
+ )
135
+ for col_name, data_type, is_nullable, col_key in col_rows
136
+ ]
137
+ tables.append(DataExplorerSchemaTable(
138
+ name=table_name,
139
+ type=table_type,
140
+ columns=columns,
141
+ ))
142
+
143
+ return DataExplorerSchemaResult(source=options.name, tables=tables)
144
+ finally:
145
+ if conn is not None:
146
+ conn.close()
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.5
2
+ Name: qodalis-cli-server-plugin-data-explorer-mysql
3
+ Version: 1.0.0b18
4
+ Summary: MySQL Data Explorer provider plugin for Qodalis CLI servers
5
+ Project-URL: Homepage, https://github.com/qodalis-solutions/cli-server-python
6
+ Project-URL: Repository, https://github.com/qodalis-solutions/cli-server-python
7
+ Project-URL: Issues, https://github.com/qodalis-solutions/cli-server-python/issues
8
+ Author-email: Nicolae Lupei <nicolae.lupei@qodalis.com>
9
+ License-Expression: MIT
10
+ Keywords: cli,data-explorer,mysql,plugin,qodalis,sql
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: aiomysql>=0.2.0
21
+ Requires-Dist: qodalis-cli-server-abstractions
@@ -0,0 +1,5 @@
1
+ qodalis_cli_data_explorer_mysql/__init__.py,sha256=xNDWVqaUpfSljB5p1PCkviBuqYWk8gcWPToIQYb8NMM,161
2
+ qodalis_cli_data_explorer_mysql/mysql_provider.py,sha256=3XeX0HxL3LFGTG5WV7Dbrh6WH2biPcawF9Hn9M7EZio,5736
3
+ qodalis_cli_server_plugin_data_explorer_mysql-1.0.0b18.dist-info/METADATA,sha256=B-RXCqn_IvB0_e4V-FZ7RkRR73eKR02kw7KNj3uYktQ,1027
4
+ qodalis_cli_server_plugin_data_explorer_mysql-1.0.0b18.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
5
+ qodalis_cli_server_plugin_data_explorer_mysql-1.0.0b18.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any