mcp-dbutils 0.23.0__py3-none-any.whl → 1.0.0__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.
@@ -154,6 +154,66 @@ class PostgreSQLHandler(ConnectionHandler):
154
154
  if conn:
155
155
  conn.close()
156
156
 
157
+ async def _execute_write_query(self, sql: str) -> str:
158
+ """Execute SQL write query
159
+
160
+ Args:
161
+ sql: SQL write query (INSERT, UPDATE, DELETE)
162
+
163
+ Returns:
164
+ str: Execution result
165
+
166
+ Raises:
167
+ ConnectionHandlerError: If query execution fails
168
+ """
169
+ conn = None
170
+ try:
171
+ # Check if the query is a write operation
172
+ sql_upper = sql.strip().upper()
173
+ is_insert = sql_upper.startswith("INSERT")
174
+ is_update = sql_upper.startswith("UPDATE")
175
+ is_delete = sql_upper.startswith("DELETE")
176
+ is_transaction = sql_upper.startswith(("BEGIN", "COMMIT", "ROLLBACK"))
177
+
178
+ if not (is_insert or is_update or is_delete or is_transaction):
179
+ raise ConnectionHandlerError("Only INSERT, UPDATE, DELETE, and transaction statements are allowed for write operations")
180
+
181
+ conn_params = self.config.get_connection_params()
182
+ conn = psycopg2.connect(**conn_params)
183
+ self.log("debug", f"Executing write operation: {sql}")
184
+
185
+ with conn.cursor() as cur:
186
+ try:
187
+ # Execute the write operation
188
+ cur.execute(sql)
189
+
190
+ # Get number of affected rows
191
+ affected_rows = cur.rowcount
192
+
193
+ # Commit the transaction if not in a transaction block
194
+ if not is_transaction:
195
+ conn.commit()
196
+
197
+ self.log("debug", f"Write operation executed successfully, affected {affected_rows} rows")
198
+
199
+ # Return result
200
+ if is_transaction:
201
+ return f"Transaction operation executed successfully"
202
+ else:
203
+ return f"Write operation executed successfully. {affected_rows} row{'s' if affected_rows != 1 else ''} affected."
204
+ except psycopg2.Error as e:
205
+ # Rollback on error
206
+ if not is_transaction:
207
+ conn.rollback()
208
+ self.log("error", f"Write operation error: [Code: {e.pgcode}] {e.pgerror or str(e)}")
209
+ raise ConnectionHandlerError(f"[Code: {e.pgcode}] {e.pgerror or str(e)}")
210
+ except psycopg2.Error as e:
211
+ error_msg = f"[{self.db_type}] Write operation failed: [Code: {e.pgcode}] {e.pgerror or str(e)}"
212
+ raise ConnectionHandlerError(error_msg)
213
+ finally:
214
+ if conn:
215
+ conn.close()
216
+
157
217
  async def get_table_description(self, table_name: str) -> str:
158
218
  """Get detailed table description"""
159
219
  conn = None
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from typing import Any, Dict, Literal, Optional
6
6
  from urllib.parse import parse_qs, urlparse
7
7
 
8
- from ..config import ConnectionConfig
8
+ from ..config import ConnectionConfig, WritePermissions
9
9
 
10
10
 
11
11
  def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
@@ -55,6 +55,8 @@ class SQLiteConfig(ConnectionConfig):
55
55
  password: Optional[str] = None
56
56
  uri: bool = True # Enable URI mode to support parameters like password
57
57
  type: Literal['sqlite'] = 'sqlite'
58
+ writable: bool = False # Whether write operations are allowed
59
+ write_permissions: Optional[WritePermissions] = None # Write permissions configuration
58
60
 
59
61
  @classmethod
60
62
  def from_jdbc_url(cls, jdbc_url: str, password: Optional[str] = None) -> 'SQLiteConfig':
@@ -147,5 +149,10 @@ class SQLiteConfig(ConnectionConfig):
147
149
  uri=True
148
150
  )
149
151
 
152
+ # Parse write permissions
153
+ config.writable = db_config.get('writable', False)
154
+ if config.writable and 'write_permissions' in db_config:
155
+ config.write_permissions = WritePermissions(db_config['write_permissions'])
156
+
150
157
  config.debug = cls.get_debug_mode()
151
158
  return config
@@ -128,6 +128,59 @@ class SQLiteHandler(ConnectionHandler):
128
128
  error_msg = f"[{self.db_type}] Query execution failed: {str(e)}"
129
129
  raise ConnectionHandlerError(error_msg)
130
130
 
131
+ async def _execute_write_query(self, sql: str) -> str:
132
+ """Execute SQL write query
133
+
134
+ Args:
135
+ sql: SQL write query (INSERT, UPDATE, DELETE)
136
+
137
+ Returns:
138
+ str: Execution result
139
+
140
+ Raises:
141
+ ConnectionHandlerError: If query execution fails
142
+ """
143
+ try:
144
+ # Check if the query is a write operation
145
+ sql_upper = sql.strip().upper()
146
+ is_insert = sql_upper.startswith("INSERT")
147
+ is_update = sql_upper.startswith("UPDATE")
148
+ is_delete = sql_upper.startswith("DELETE")
149
+ is_transaction = sql_upper.startswith(("BEGIN", "COMMIT", "ROLLBACK"))
150
+
151
+ if not (is_insert or is_update or is_delete or is_transaction):
152
+ raise ConnectionHandlerError("Only INSERT, UPDATE, DELETE, and transaction statements are allowed for write operations")
153
+
154
+ conn = sqlite3.connect(self.config.path)
155
+ cur = conn.cursor()
156
+
157
+ try:
158
+ start_time = time.time()
159
+ cur.execute(sql)
160
+ conn.commit()
161
+ end_time = time.time()
162
+ elapsed_ms = (end_time - start_time) * 1000
163
+
164
+ # Get number of affected rows
165
+ affected_rows = cur.rowcount
166
+
167
+ self.log("debug", f"Write operation executed in {elapsed_ms:.2f}ms, affected {affected_rows} rows")
168
+
169
+ # Return result
170
+ if is_transaction:
171
+ return f"Transaction operation executed successfully"
172
+ else:
173
+ return f"Write operation executed successfully. {affected_rows} row{'s' if affected_rows != 1 else ''} affected."
174
+ except sqlite3.Error as e:
175
+ self.log("error", f"Write operation error: {str(e)}")
176
+ raise ConnectionHandlerError(str(e))
177
+ finally:
178
+ cur.close()
179
+ conn.close()
180
+ except sqlite3.Error as e:
181
+ error_msg = f"[{self.db_type}] Write operation failed: {str(e)}"
182
+ raise ConnectionHandlerError(error_msg)
183
+
131
184
  async def get_table_description(self, table_name: str) -> str:
132
185
  """Get detailed table description"""
133
186
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-dbutils
3
- Version: 0.23.0
3
+ Version: 1.0.0
4
4
  Summary: MCP Database Utilities Service
5
5
  Author: Dong Hao
6
6
  License-Expression: MIT
@@ -0,0 +1,23 @@
1
+ mcp_dbutils/__init__.py,sha256=6LLccQv7je2L4IpY_I3OzSJZcK32VUDJv2IY31y6eYg,1900
2
+ mcp_dbutils/audit.py,sha256=U-Fd511HxOQH4BxfCXgd4KyaBLESEBnGsPkUNviSTwc,7294
3
+ mcp_dbutils/base.py,sha256=ao2md_bsbK5s_vsO--m-sZLPfiMp6f_MUVL9o0KzKIA,61720
4
+ mcp_dbutils/config.py,sha256=zwN9yPKv4WvEPG3WIRT6uBVZSRxFniSmN2kEog7KPcI,5921
5
+ mcp_dbutils/log.py,sha256=mqxi6I_IL-MF1F_pxBtnYZQKOHbGBJ74gsvZHVelr1w,823
6
+ mcp_dbutils/stats.py,sha256=wMqWPfGnEOg9v5YBtTsARV-1YsFUMM_pKdzitzSU9x4,7137
7
+ mcp_dbutils/mysql/__init__.py,sha256=gNhoHaxK1qhvMAH5AVl1vfV1rUpcbV9KZWUQb41aaQk,129
8
+ mcp_dbutils/mysql/config.py,sha256=ZOYnSRc-Cj9u494sk0P2Nn3zmQxMxaQaXGYArNJNo84,10338
9
+ mcp_dbutils/mysql/handler.py,sha256=B2T9TDwRGUtdvUz0m-iHtSk-ct0Q7a9UVFzscgV7NYo,25274
10
+ mcp_dbutils/mysql/server.py,sha256=4kWkrZby8fZq-Em65Fobs5KDDPJnrOxbsK1zLK1jzp0,9412
11
+ mcp_dbutils/postgres/__init__.py,sha256=-2zYuEJEQ2AMvmGhH5Z_umerSvt7S4xOa_XV4wgvGfI,154
12
+ mcp_dbutils/postgres/config.py,sha256=Z9g-VIZVfiik_u7nayFOc82EigainmPzwjO6zYPHpU8,8431
13
+ mcp_dbutils/postgres/handler.py,sha256=YLSPeS_D3BPLO7qN5SnYN95ioeTgCqUppIZ7Pt2ytQY,27733
14
+ mcp_dbutils/postgres/server.py,sha256=qden51sQu5Ht2ibl2jSDyxg7r_N9qDVhg83Pc-q98yc,8887
15
+ mcp_dbutils/sqlite/__init__.py,sha256=fK_3-WylCBYpBAzwuopi8hlwoIGJm2TPAlwcPWG46I0,134
16
+ mcp_dbutils/sqlite/config.py,sha256=rsfAE8yaCVZC39ziXssqsi0EXUOEWA-MtKHvrO-6jG4,4933
17
+ mcp_dbutils/sqlite/handler.py,sha256=vpkyCow26hpBqigNUNW0VGyWhsTz8uFflssM7K-FJi4,21882
18
+ mcp_dbutils/sqlite/server.py,sha256=EBKNKz_wTvChwg6BZlvZIBA1H5mmE2NiNEMOgu_CMy4,7373
19
+ mcp_dbutils-1.0.0.dist-info/METADATA,sha256=qwZMwl9JMQAydeUitTQ_zOhmzE2FplxliOYY03O39OQ,8440
20
+ mcp_dbutils-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ mcp_dbutils-1.0.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
22
+ mcp_dbutils-1.0.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
23
+ mcp_dbutils-1.0.0.dist-info/RECORD,,
@@ -1,22 +0,0 @@
1
- mcp_dbutils/__init__.py,sha256=6LLccQv7je2L4IpY_I3OzSJZcK32VUDJv2IY31y6eYg,1900
2
- mcp_dbutils/base.py,sha256=A9p79jqKxzCmOErFTwabQtUzIftkl1_qMFQ2rlnFB3g,42147
3
- mcp_dbutils/config.py,sha256=bmXpOd1fyYfoyUS75I035ChT6t3wP5AyEnJ06e2ZS2o,1848
4
- mcp_dbutils/log.py,sha256=mqxi6I_IL-MF1F_pxBtnYZQKOHbGBJ74gsvZHVelr1w,823
5
- mcp_dbutils/stats.py,sha256=wMqWPfGnEOg9v5YBtTsARV-1YsFUMM_pKdzitzSU9x4,7137
6
- mcp_dbutils/mysql/__init__.py,sha256=gNhoHaxK1qhvMAH5AVl1vfV1rUpcbV9KZWUQb41aaQk,129
7
- mcp_dbutils/mysql/config.py,sha256=BTPPFqlhoTp7EBFIeLJZh8x6bCn3q9NivHYz9yZHziw,9820
8
- mcp_dbutils/mysql/handler.py,sha256=CJl6Mcs8kloojtS7i-2yD_-InRSmtU09LfYnHDZDhn0,22783
9
- mcp_dbutils/mysql/server.py,sha256=4kWkrZby8fZq-Em65Fobs5KDDPJnrOxbsK1zLK1jzp0,9412
10
- mcp_dbutils/postgres/__init__.py,sha256=-2zYuEJEQ2AMvmGhH5Z_umerSvt7S4xOa_XV4wgvGfI,154
11
- mcp_dbutils/postgres/config.py,sha256=NyQOVhkXJ1S-JD0w-ePNjTKI1Ja-aZQkDUdHi6U7Vl4,7752
12
- mcp_dbutils/postgres/handler.py,sha256=SnzgXjcO3iuvpQ-dzggNOF0bewAKB5BwL3QS11a5qWw,25183
13
- mcp_dbutils/postgres/server.py,sha256=qden51sQu5Ht2ibl2jSDyxg7r_N9qDVhg83Pc-q98yc,8887
14
- mcp_dbutils/sqlite/__init__.py,sha256=fK_3-WylCBYpBAzwuopi8hlwoIGJm2TPAlwcPWG46I0,134
15
- mcp_dbutils/sqlite/config.py,sha256=j67TJ8mQJ2D886MthSa-zYMtvUUYyyxYLMlNxkYoqZE,4509
16
- mcp_dbutils/sqlite/handler.py,sha256=jH0xi_0PxiIBIVnXAGkfNoTVazTr7ol_P1XgBVDRkrU,19781
17
- mcp_dbutils/sqlite/server.py,sha256=EBKNKz_wTvChwg6BZlvZIBA1H5mmE2NiNEMOgu_CMy4,7373
18
- mcp_dbutils-0.23.0.dist-info/METADATA,sha256=8GKU0Z9Z-TQFAVJqjeouk5VRNrZaJfedBktVujVg3b4,8441
19
- mcp_dbutils-0.23.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- mcp_dbutils-0.23.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
21
- mcp_dbutils-0.23.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
22
- mcp_dbutils-0.23.0.dist-info/RECORD,,