fastmssql 0.2.6__cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 0.2.8__cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.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 fastmssql might be problematic. Click here for more details.
- fastmssql/__init__.py +575 -4
- fastmssql/fastmssql.cpython-38-aarch64-linux-gnu.so +0 -0
- {fastmssql-0.2.6.dist-info → fastmssql-0.2.8.dist-info}/METADATA +81 -185
- fastmssql-0.2.8.dist-info/RECORD +6 -0
- fastmssql-0.2.6.dist-info/RECORD +0 -6
- {fastmssql-0.2.6.dist-info → fastmssql-0.2.8.dist-info}/WHEEL +0 -0
- {fastmssql-0.2.6.dist-info → fastmssql-0.2.8.dist-info}/licenses/LICENSE +0 -0
fastmssql/__init__.py
CHANGED
|
@@ -1,5 +1,576 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
FastMSSQL - High-Performance Microsoft SQL Server Driver for Python
|
|
3
|
+
===================================================================
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
This library provides direct access to high-performance Rust implementations
|
|
6
|
+
with minimal Python overhead for maximum performance. Built on top of the
|
|
7
|
+
tiberius crate, it offers both synchronous and asynchronous database operations
|
|
8
|
+
with advanced features like connection pooling, SSL/TLS configuration, and
|
|
9
|
+
efficient parameter handling.
|
|
10
|
+
|
|
11
|
+
Key Features:
|
|
12
|
+
- High-performance Rust backend with Python bindings
|
|
13
|
+
- Async/await support for non-blocking operations
|
|
14
|
+
- Connection pooling with configurable parameters
|
|
15
|
+
- SSL/TLS encryption with certificate validation
|
|
16
|
+
- Parameterized queries with automatic type conversion
|
|
17
|
+
- Memory-efficient result iteration
|
|
18
|
+
- Comprehensive error handling and logging
|
|
19
|
+
|
|
20
|
+
Basic Usage (Async):
|
|
21
|
+
>>> import asyncio
|
|
22
|
+
>>> from fastmssql import Connection
|
|
23
|
+
>>>
|
|
24
|
+
>>> async def main():
|
|
25
|
+
... async with Connection("Server=localhost;Database=test;Trusted_Connection=yes") as conn:
|
|
26
|
+
... # Simple query
|
|
27
|
+
... result = await conn.execute("SELECT * FROM users")
|
|
28
|
+
... async for row in result:
|
|
29
|
+
... print(f"User: {row['name']}, Age: {row['age']}")
|
|
30
|
+
...
|
|
31
|
+
... # Parameterized query
|
|
32
|
+
... result = await conn.execute(
|
|
33
|
+
... "SELECT * FROM users WHERE age > @P1 AND city = @P2",
|
|
34
|
+
... [18, "New York"]
|
|
35
|
+
... )
|
|
36
|
+
... rows = await result.fetchall()
|
|
37
|
+
... print(f"Found {len(rows)} users")
|
|
38
|
+
>>>
|
|
39
|
+
>>> asyncio.run(main())
|
|
40
|
+
|
|
41
|
+
Basic Usage (Sync):
|
|
42
|
+
>>> from fastmssql import Connection
|
|
43
|
+
>>>
|
|
44
|
+
>>> with Connection("Server=localhost;Database=test;Trusted_Connection=yes") as conn:
|
|
45
|
+
... result = conn.execute_with_python_params(
|
|
46
|
+
... "SELECT COUNT(*) as count FROM users WHERE active = ?",
|
|
47
|
+
... [True]
|
|
48
|
+
... )
|
|
49
|
+
... print(f"Active users: {result[0]['count']}")
|
|
50
|
+
|
|
51
|
+
Advanced Configuration:
|
|
52
|
+
>>> from fastmssql import Connection, PoolConfig, SslConfig, EncryptionLevel
|
|
53
|
+
>>>
|
|
54
|
+
>>> # Configure connection pool
|
|
55
|
+
>>> pool_config = PoolConfig(
|
|
56
|
+
... max_connections=20,
|
|
57
|
+
... min_connections=2,
|
|
58
|
+
... acquire_timeout_seconds=30,
|
|
59
|
+
... idle_timeout_seconds=600
|
|
60
|
+
... )
|
|
61
|
+
>>>
|
|
62
|
+
>>> # Configure SSL/TLS
|
|
63
|
+
>>> ssl_config = SslConfig(
|
|
64
|
+
... encryption_level=EncryptionLevel.Required,
|
|
65
|
+
... trust_server_certificate=False,
|
|
66
|
+
... certificate_path="/path/to/cert.pem"
|
|
67
|
+
... )
|
|
68
|
+
>>>
|
|
69
|
+
>>> conn = Connection(
|
|
70
|
+
... server="myserver.database.windows.net",
|
|
71
|
+
... database="mydatabase",
|
|
72
|
+
... username="myuser",
|
|
73
|
+
... password="mypassword",
|
|
74
|
+
... pool_config=pool_config,
|
|
75
|
+
... ssl_config=ssl_config
|
|
76
|
+
... )
|
|
77
|
+
|
|
78
|
+
Performance Considerations:
|
|
79
|
+
- Use parameterized queries to prevent SQL injection and improve performance
|
|
80
|
+
- Leverage connection pooling for applications with multiple concurrent operations
|
|
81
|
+
- Use async methods for I/O-bound applications to improve throughput
|
|
82
|
+
- Consider batch operations for bulk data manipulation
|
|
83
|
+
- Monitor connection pool statistics to optimize pool configuration
|
|
84
|
+
|
|
85
|
+
Thread Safety:
|
|
86
|
+
This library is thread-safe and can be used in multi-threaded applications.
|
|
87
|
+
Each Connection instance maintains its own connection pool and can be safely
|
|
88
|
+
shared across threads when using async methods.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
# Import from the maturin-generated module
|
|
92
|
+
from .fastmssql import Connection as _RustConnection
|
|
93
|
+
from .fastmssql import PoolConfig
|
|
94
|
+
from .fastmssql import SslConfig
|
|
95
|
+
from .fastmssql import FastExecutionResult
|
|
96
|
+
from .fastmssql import version, EncryptionLevel, Parameter, Parameters
|
|
97
|
+
|
|
98
|
+
# Wrapper class to handle async execution result conversion
|
|
99
|
+
class Connection:
|
|
100
|
+
"""
|
|
101
|
+
High-performance connection to Microsoft SQL Server.
|
|
102
|
+
|
|
103
|
+
This class provides a Python wrapper around the Rust-based connection implementation,
|
|
104
|
+
offering both synchronous and asynchronous database operations with advanced features
|
|
105
|
+
like connection pooling, SSL/TLS configuration, and efficient parameter handling.
|
|
106
|
+
|
|
107
|
+
The Connection class supports multiple initialization patterns:
|
|
108
|
+
1. Connection string-based initialization
|
|
109
|
+
2. Individual parameter initialization
|
|
110
|
+
3. Advanced configuration with pool and SSL settings
|
|
111
|
+
|
|
112
|
+
Connection Patterns:
|
|
113
|
+
# Using connection string
|
|
114
|
+
conn = Connection("Server=localhost;Database=test;Trusted_Connection=yes")
|
|
115
|
+
|
|
116
|
+
# Using individual parameters
|
|
117
|
+
conn = Connection(
|
|
118
|
+
server="localhost",
|
|
119
|
+
database="test",
|
|
120
|
+
trusted_connection=True
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Using username/password authentication
|
|
124
|
+
conn = Connection(
|
|
125
|
+
server="myserver.database.windows.net",
|
|
126
|
+
database="mydatabase",
|
|
127
|
+
username="myuser",
|
|
128
|
+
password="mypassword"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
Thread Safety:
|
|
132
|
+
This class is thread-safe and maintains an internal connection pool that can
|
|
133
|
+
be safely accessed from multiple threads when using async methods.
|
|
134
|
+
|
|
135
|
+
Performance Notes:
|
|
136
|
+
- Async methods are recommended for I/O-bound applications
|
|
137
|
+
- Connection pooling is automatically managed for optimal resource usage
|
|
138
|
+
- Parameterized queries provide better performance and security
|
|
139
|
+
- Results are streamed efficiently to minimize memory usage
|
|
140
|
+
|
|
141
|
+
Attributes:
|
|
142
|
+
_conn: The underlying Rust connection implementation
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
def __init__(
|
|
146
|
+
self,
|
|
147
|
+
connection_string=None,
|
|
148
|
+
pool_config=None,
|
|
149
|
+
ssl_config=None,
|
|
150
|
+
server=None,
|
|
151
|
+
database=None,
|
|
152
|
+
username=None,
|
|
153
|
+
password=None,
|
|
154
|
+
trusted_connection=None
|
|
155
|
+
):
|
|
156
|
+
"""
|
|
157
|
+
Initialize a new SQL Server connection.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
connection_string (str, optional): Complete ADO.NET-style connection string.
|
|
161
|
+
Takes precedence over individual parameters if provided.
|
|
162
|
+
Example: "Server=localhost;Database=test;Trusted_Connection=yes"
|
|
163
|
+
|
|
164
|
+
pool_config (PoolConfig, optional): Configuration for the connection pool.
|
|
165
|
+
Allows customization of pool size, timeouts, and behavior.
|
|
166
|
+
|
|
167
|
+
ssl_config (SslConfig, optional): SSL/TLS configuration for secure connections.
|
|
168
|
+
Required for encrypted connections to Azure SQL Database and other
|
|
169
|
+
secure SQL Server instances.
|
|
170
|
+
|
|
171
|
+
server (str, optional): SQL Server hostname or IP address.
|
|
172
|
+
Can include instance name (e.g., "localhost\\SQLEXPRESS") or port
|
|
173
|
+
(e.g., "localhost:1433").
|
|
174
|
+
|
|
175
|
+
database (str, optional): Name of the database to connect to.
|
|
176
|
+
If not specified, connects to the default database for the user.
|
|
177
|
+
|
|
178
|
+
username (str, optional): Username for SQL Server authentication.
|
|
179
|
+
Required when not using Windows Authentication.
|
|
180
|
+
|
|
181
|
+
password (str, optional): Password for SQL Server authentication.
|
|
182
|
+
Required when username is provided.
|
|
183
|
+
|
|
184
|
+
trusted_connection (bool, optional): Whether to use Windows Authentication.
|
|
185
|
+
When True, uses the current Windows user's credentials.
|
|
186
|
+
Mutually exclusive with username/password.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
ValueError: If connection parameters are invalid or conflicting.
|
|
190
|
+
ConnectionError: If unable to establish initial connection pool.
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
# Connection string approach
|
|
194
|
+
>>> conn = Connection("Server=localhost;Database=AdventureWorks;Trusted_Connection=yes")
|
|
195
|
+
|
|
196
|
+
# Individual parameters
|
|
197
|
+
>>> conn = Connection(
|
|
198
|
+
... server="localhost",
|
|
199
|
+
... database="AdventureWorks",
|
|
200
|
+
... trusted_connection=True
|
|
201
|
+
... )
|
|
202
|
+
|
|
203
|
+
# SQL Server authentication
|
|
204
|
+
>>> conn = Connection(
|
|
205
|
+
... server="myserver.database.windows.net",
|
|
206
|
+
... database="mydatabase",
|
|
207
|
+
... username="myuser@mydomain.com",
|
|
208
|
+
... password="SecurePassword123!"
|
|
209
|
+
... )
|
|
210
|
+
|
|
211
|
+
# With advanced configuration
|
|
212
|
+
>>> from fastmssql import PoolConfig, SslConfig, EncryptionLevel
|
|
213
|
+
>>> pool_config = PoolConfig(max_connections=10, min_connections=2)
|
|
214
|
+
>>> ssl_config = SslConfig(encryption_level=EncryptionLevel.Required)
|
|
215
|
+
>>> conn = Connection(
|
|
216
|
+
... server="secure-server.example.com",
|
|
217
|
+
... database="production_db",
|
|
218
|
+
... username="app_user",
|
|
219
|
+
... password="app_password",
|
|
220
|
+
... pool_config=pool_config,
|
|
221
|
+
... ssl_config=ssl_config
|
|
222
|
+
... )
|
|
223
|
+
"""
|
|
224
|
+
self._conn = _RustConnection(
|
|
225
|
+
connection_string=connection_string,
|
|
226
|
+
pool_config=pool_config,
|
|
227
|
+
ssl_config=ssl_config,
|
|
228
|
+
server=server,
|
|
229
|
+
database=database,
|
|
230
|
+
username=username,
|
|
231
|
+
password=password,
|
|
232
|
+
trusted_connection=trusted_connection
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
async def execute(self, query, parameters=None):
|
|
236
|
+
"""
|
|
237
|
+
Execute a SQL query asynchronously and return a FastExecutionResult.
|
|
238
|
+
|
|
239
|
+
This method executes SQL queries with optional parameterization for security
|
|
240
|
+
and performance. It supports all types of SQL statements including SELECT,
|
|
241
|
+
INSERT, UPDATE, DELETE, and stored procedure calls.
|
|
242
|
+
|
|
243
|
+
Parameter Binding:
|
|
244
|
+
Parameters are bound using positional placeholders (@P1, @P2, etc.) in the
|
|
245
|
+
query string. The parameter values are provided as a list in the same order.
|
|
246
|
+
|
|
247
|
+
Supported Parameter Types:
|
|
248
|
+
- None (NULL)
|
|
249
|
+
- bool
|
|
250
|
+
- int (32-bit and 64-bit)
|
|
251
|
+
- float (32-bit and 64-bit)
|
|
252
|
+
- str (varchar, nvarchar, text)
|
|
253
|
+
- bytes (varbinary, image)
|
|
254
|
+
- datetime.datetime (datetime, datetime2)
|
|
255
|
+
- datetime.date (date)
|
|
256
|
+
- datetime.time (time)
|
|
257
|
+
- decimal.Decimal (decimal, money)
|
|
258
|
+
- uuid.UUID (uniqueidentifier)
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
query (str): SQL query to execute. Use @P1, @P2, etc. for parameters.
|
|
262
|
+
Example: "SELECT * FROM users WHERE age > @P1 AND city = @P2"
|
|
263
|
+
|
|
264
|
+
parameters (list, optional): List of parameter values in order.
|
|
265
|
+
Values are automatically converted to appropriate SQL types.
|
|
266
|
+
Example: [18, "New York"]
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
FastExecutionResult: An async iterable result object that provides:
|
|
270
|
+
- Async iteration over result rows
|
|
271
|
+
- fetchone(), fetchmany(), fetchall() methods
|
|
272
|
+
- Row count and column metadata
|
|
273
|
+
- Efficient memory usage for large result sets
|
|
274
|
+
|
|
275
|
+
Raises:
|
|
276
|
+
SqlError: If the SQL query contains syntax errors or constraint violations.
|
|
277
|
+
ConnectionError: If the database connection is lost during execution.
|
|
278
|
+
TimeoutError: If the query execution exceeds configured timeouts.
|
|
279
|
+
ParameterError: If parameter types cannot be converted or are invalid.
|
|
280
|
+
|
|
281
|
+
Examples:
|
|
282
|
+
# Simple SELECT query
|
|
283
|
+
>>> result = await conn.execute("SELECT * FROM users")
|
|
284
|
+
>>> async for row in result:
|
|
285
|
+
... print(f"User ID: {row['id']}, Name: {row['name']}")
|
|
286
|
+
|
|
287
|
+
# Parameterized query
|
|
288
|
+
>>> result = await conn.execute(
|
|
289
|
+
... "SELECT * FROM orders WHERE created_date > @P1 AND amount > @P2",
|
|
290
|
+
... [datetime(2023, 1, 1), 100.0]
|
|
291
|
+
... )
|
|
292
|
+
>>> rows = await result.fetchall()
|
|
293
|
+
>>> print(f"Found {len(rows)} orders")
|
|
294
|
+
|
|
295
|
+
# INSERT with parameters
|
|
296
|
+
>>> result = await conn.execute(
|
|
297
|
+
... "INSERT INTO users (name, email, age) VALUES (@P1, @P2, @P3)",
|
|
298
|
+
... ["John Doe", "john@example.com", 30]
|
|
299
|
+
... )
|
|
300
|
+
>>> print(f"Inserted {result.rowcount} row(s)")
|
|
301
|
+
|
|
302
|
+
# Stored procedure call
|
|
303
|
+
>>> result = await conn.execute(
|
|
304
|
+
... "EXEC GetUsersByDepartment @P1, @P2",
|
|
305
|
+
... ["Engineering", True] # department, active_only
|
|
306
|
+
... )
|
|
307
|
+
>>> users = await result.fetchall()
|
|
308
|
+
|
|
309
|
+
# Complex query with multiple data types
|
|
310
|
+
>>> from decimal import Decimal
|
|
311
|
+
>>> from datetime import datetime
|
|
312
|
+
>>> import uuid
|
|
313
|
+
>>>
|
|
314
|
+
>>> result = await conn.execute(
|
|
315
|
+
... \"\"\"UPDATE products
|
|
316
|
+
... SET price = @P1, updated_date = @P2, active = @P3
|
|
317
|
+
... WHERE product_id = @P4\"\"\",
|
|
318
|
+
... [Decimal('29.99'), datetime.now(), True, uuid.uuid4()]
|
|
319
|
+
... )
|
|
320
|
+
|
|
321
|
+
Performance Tips:
|
|
322
|
+
- Use parameterized queries instead of string formatting for better performance
|
|
323
|
+
- For large result sets, iterate asynchronously rather than calling fetchall()
|
|
324
|
+
- Reuse Connection instances to benefit from connection pooling
|
|
325
|
+
- Consider batch operations for bulk data manipulation
|
|
326
|
+
|
|
327
|
+
Security Notes:
|
|
328
|
+
- Always use parameterized queries to prevent SQL injection attacks
|
|
329
|
+
- Parameter values are automatically escaped and type-checked
|
|
330
|
+
- Never concatenate user input directly into SQL query strings
|
|
331
|
+
"""
|
|
332
|
+
# The Rust implementation now returns results directly
|
|
333
|
+
return await self._conn.execute(query, parameters)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
async def is_connected(self):
|
|
337
|
+
"""
|
|
338
|
+
Check if the connection is active and available for queries.
|
|
339
|
+
|
|
340
|
+
This method performs a lightweight check to determine if the underlying
|
|
341
|
+
connection pool has active connections and can accept new queries.
|
|
342
|
+
It's useful for health checks and connection validation in long-running
|
|
343
|
+
applications.
|
|
344
|
+
|
|
345
|
+
The check verifies:
|
|
346
|
+
- Connection pool is initialized and operational
|
|
347
|
+
- At least one connection in the pool is active
|
|
348
|
+
- Network connectivity to the SQL Server instance
|
|
349
|
+
- Authentication credentials are still valid
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
bool: True if the connection is active and ready for queries,
|
|
353
|
+
False if the connection is closed, failed, or unavailable.
|
|
354
|
+
|
|
355
|
+
Raises:
|
|
356
|
+
ConnectionError: If there's an unexpected error checking connection status.
|
|
357
|
+
|
|
358
|
+
Examples:
|
|
359
|
+
# Basic connection check
|
|
360
|
+
>>> if await conn.is_connected():
|
|
361
|
+
... result = await conn.execute("SELECT COUNT(*) FROM users")
|
|
362
|
+
... else:
|
|
363
|
+
... await conn.connect() # Reconnect if needed
|
|
364
|
+
|
|
365
|
+
# Health check in a web application
|
|
366
|
+
>>> async def health_check():
|
|
367
|
+
... try:
|
|
368
|
+
... if await conn.is_connected():
|
|
369
|
+
... return {"database": "healthy", "status": "connected"}
|
|
370
|
+
... else:
|
|
371
|
+
... return {"database": "unhealthy", "status": "disconnected"}
|
|
372
|
+
... except Exception as e:
|
|
373
|
+
... return {"database": "error", "status": str(e)}
|
|
374
|
+
|
|
375
|
+
# Periodic connection monitoring
|
|
376
|
+
>>> import asyncio
|
|
377
|
+
>>>
|
|
378
|
+
>>> async def monitor_connection():
|
|
379
|
+
... while True:
|
|
380
|
+
... if await conn.is_connected():
|
|
381
|
+
... print(f"{datetime.now()}: Database connection is healthy")
|
|
382
|
+
... else:
|
|
383
|
+
... print(f"{datetime.now()}: Database connection is down!")
|
|
384
|
+
... # Attempt to reconnect
|
|
385
|
+
... try:
|
|
386
|
+
... await conn.connect()
|
|
387
|
+
... print("Reconnection successful")
|
|
388
|
+
... except Exception as e:
|
|
389
|
+
... print(f"Reconnection failed: {e}")
|
|
390
|
+
...
|
|
391
|
+
... await asyncio.sleep(60) # Check every minute
|
|
392
|
+
|
|
393
|
+
Performance Notes:
|
|
394
|
+
- This is a lightweight operation that doesn't execute actual SQL
|
|
395
|
+
- The check uses connection pool metadata and cached connection state
|
|
396
|
+
- Suitable for frequent health checks without performance impact
|
|
397
|
+
- Does not count against connection pool limits
|
|
398
|
+
|
|
399
|
+
Use Cases:
|
|
400
|
+
- Application startup validation
|
|
401
|
+
- Periodic health monitoring
|
|
402
|
+
- Circuit breaker pattern implementation
|
|
403
|
+
- Load balancer health checks
|
|
404
|
+
- Graceful degradation in microservices
|
|
405
|
+
"""
|
|
406
|
+
return await self._conn.is_connected()
|
|
407
|
+
|
|
408
|
+
async def pool_stats(self):
|
|
409
|
+
"""
|
|
410
|
+
Get comprehensive connection pool statistics and health metrics.
|
|
411
|
+
|
|
412
|
+
This method provides detailed information about the current state of the
|
|
413
|
+
connection pool, including active connections, idle connections, and
|
|
414
|
+
configuration parameters. It's essential for monitoring, debugging, and
|
|
415
|
+
optimizing connection pool performance in production environments.
|
|
416
|
+
|
|
417
|
+
The statistics help identify:
|
|
418
|
+
- Connection pool utilization patterns
|
|
419
|
+
- Potential connection leaks
|
|
420
|
+
- Optimal pool sizing configuration
|
|
421
|
+
- Performance bottlenecks
|
|
422
|
+
- Resource contention issues
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
dict: A dictionary containing pool statistics with the following keys:
|
|
426
|
+
|
|
427
|
+
When connected:
|
|
428
|
+
- 'connections' (int): Total number of connections in the pool
|
|
429
|
+
- 'idle_connections' (int): Number of idle connections available
|
|
430
|
+
- 'active_connections' (int): Number of connections currently in use
|
|
431
|
+
- 'max_size' (int): Maximum allowed connections in the pool
|
|
432
|
+
- 'min_idle' (int): Minimum idle connections maintained
|
|
433
|
+
|
|
434
|
+
When disconnected:
|
|
435
|
+
- 'connected' (bool): False, indicating no active pool
|
|
436
|
+
|
|
437
|
+
Raises:
|
|
438
|
+
ConnectionError: If unable to retrieve pool statistics due to connection issues.
|
|
439
|
+
|
|
440
|
+
Examples:
|
|
441
|
+
# Basic pool monitoring
|
|
442
|
+
>>> stats = await conn.pool_stats()
|
|
443
|
+
>>> if stats.get('connected', True): # Handle disconnected case
|
|
444
|
+
... print(f"Active connections: {stats['active_connections']}")
|
|
445
|
+
... print(f"Idle connections: {stats['idle_connections']}")
|
|
446
|
+
... print(f"Pool utilization: {stats['active_connections']/stats['max_size']*100:.1f}%")
|
|
447
|
+
|
|
448
|
+
# Comprehensive pool monitoring
|
|
449
|
+
>>> async def monitor_pool():
|
|
450
|
+
... stats = await conn.pool_stats()
|
|
451
|
+
...
|
|
452
|
+
... if not stats.get('connected', True):
|
|
453
|
+
... print("❌ Connection pool is not active")
|
|
454
|
+
... return
|
|
455
|
+
...
|
|
456
|
+
... total = stats['connections']
|
|
457
|
+
... active = stats['active_connections']
|
|
458
|
+
... idle = stats['idle_connections']
|
|
459
|
+
... max_size = stats['max_size']
|
|
460
|
+
... min_idle = stats['min_idle']
|
|
461
|
+
...
|
|
462
|
+
... utilization = (active / max_size) * 100
|
|
463
|
+
...
|
|
464
|
+
... print(f"📊 Connection Pool Statistics:")
|
|
465
|
+
... print(f" Total connections: {total}")
|
|
466
|
+
... print(f" Active connections: {active}")
|
|
467
|
+
... print(f" Idle connections: {idle}")
|
|
468
|
+
... print(f" Max pool size: {max_size}")
|
|
469
|
+
... print(f" Min idle: {min_idle}")
|
|
470
|
+
... print(f" Utilization: {utilization:.1f}%")
|
|
471
|
+
...
|
|
472
|
+
... # Health assessment
|
|
473
|
+
... if utilization > 90:
|
|
474
|
+
... print("⚠️ High pool utilization - consider increasing max_size")
|
|
475
|
+
... elif idle < min_idle:
|
|
476
|
+
... print("⚠️ Low idle connections - pool may be under pressure")
|
|
477
|
+
... elif utilization < 10 and total > min_idle * 2:
|
|
478
|
+
... print("ℹ️ Low utilization - consider reducing max_size")
|
|
479
|
+
... else:
|
|
480
|
+
... print("✅ Pool appears healthy")
|
|
481
|
+
|
|
482
|
+
# Pool statistics for alerting
|
|
483
|
+
>>> async def check_pool_health():
|
|
484
|
+
... stats = await conn.pool_stats()
|
|
485
|
+
...
|
|
486
|
+
... if not stats.get('connected', True):
|
|
487
|
+
... return {"status": "critical", "message": "Pool disconnected"}
|
|
488
|
+
...
|
|
489
|
+
... utilization = stats['active_connections'] / stats['max_size']
|
|
490
|
+
... idle_ratio = stats['idle_connections'] / stats['max_size']
|
|
491
|
+
...
|
|
492
|
+
... if utilization > 0.9:
|
|
493
|
+
... return {
|
|
494
|
+
... "status": "warning",
|
|
495
|
+
... "message": f"High utilization: {utilization:.1%}",
|
|
496
|
+
... "stats": stats
|
|
497
|
+
... }
|
|
498
|
+
... elif idle_ratio < 0.1:
|
|
499
|
+
... return {
|
|
500
|
+
... "status": "warning",
|
|
501
|
+
... "message": f"Low idle connections: {stats['idle_connections']}",
|
|
502
|
+
... "stats": stats
|
|
503
|
+
... }
|
|
504
|
+
... else:
|
|
505
|
+
... return {"status": "healthy", "stats": stats}
|
|
506
|
+
|
|
507
|
+
# Logging pool metrics
|
|
508
|
+
>>> import logging
|
|
509
|
+
>>>
|
|
510
|
+
>>> async def log_pool_metrics():
|
|
511
|
+
... stats = await conn.pool_stats()
|
|
512
|
+
... if stats.get('connected', True):
|
|
513
|
+
... logging.info(
|
|
514
|
+
... "Pool metrics: active=%d, idle=%d, total=%d, utilization=%.1f%%",
|
|
515
|
+
... stats['active_connections'],
|
|
516
|
+
... stats['idle_connections'],
|
|
517
|
+
... stats['connections'],
|
|
518
|
+
... (stats['active_connections'] / stats['max_size']) * 100
|
|
519
|
+
... )
|
|
520
|
+
|
|
521
|
+
Monitoring Best Practices:
|
|
522
|
+
- Monitor pool utilization during peak load periods
|
|
523
|
+
- Set up alerts for utilization > 80% or idle connections < min_idle
|
|
524
|
+
- Track connection acquisition times and pool exhaustion events
|
|
525
|
+
- Use metrics for capacity planning and performance optimization
|
|
526
|
+
- Log pool statistics periodically for historical analysis
|
|
527
|
+
|
|
528
|
+
Performance Impact:
|
|
529
|
+
- This operation has minimal performance overhead
|
|
530
|
+
- Safe to call frequently for monitoring purposes
|
|
531
|
+
- Does not affect active connections or pool operation
|
|
532
|
+
- Recommended for inclusion in health check endpoints
|
|
533
|
+
"""
|
|
534
|
+
result_tuple = await self._conn.pool_stats()
|
|
535
|
+
|
|
536
|
+
# Convert tuple to dictionary
|
|
537
|
+
connected, connections, idle_connections, max_size, min_idle = result_tuple
|
|
538
|
+
|
|
539
|
+
if connected:
|
|
540
|
+
return {
|
|
541
|
+
'connections': connections,
|
|
542
|
+
'idle_connections': idle_connections,
|
|
543
|
+
'max_size': max_size,
|
|
544
|
+
'min_idle': min_idle,
|
|
545
|
+
'active_connections': connections - idle_connections,
|
|
546
|
+
}
|
|
547
|
+
else:
|
|
548
|
+
return {'connected': False}
|
|
549
|
+
|
|
550
|
+
async def connect(self):
|
|
551
|
+
"""Explicitly connect to the database."""
|
|
552
|
+
return await self._conn.connect()
|
|
553
|
+
|
|
554
|
+
async def disconnect(self):
|
|
555
|
+
"""Explicitly disconnect from the database."""
|
|
556
|
+
return await self._conn.disconnect()
|
|
557
|
+
|
|
558
|
+
async def __aenter__(self):
|
|
559
|
+
await self._conn.__aenter__()
|
|
560
|
+
return self
|
|
561
|
+
|
|
562
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
563
|
+
await self._conn.__aexit__(exc_type, exc_val, exc_tb)
|
|
564
|
+
return None
|
|
565
|
+
|
|
566
|
+
def __enter__(self):
|
|
567
|
+
return self._conn.__enter__()
|
|
568
|
+
|
|
569
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
570
|
+
return self._conn.__exit__(exc_type, exc_val, exc_tb)
|
|
571
|
+
|
|
572
|
+
# Preserve module documentation
|
|
573
|
+
if hasattr(_RustConnection, "__doc__"):
|
|
574
|
+
__doc__ = _RustConnection.__doc__
|
|
575
|
+
|
|
576
|
+
__all__ = ["Connection", "PoolConfig", "SslConfig", "FastExecutionResult", "version", "EncryptionLevel", "Parameter", "Parameters"]
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastmssql
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: Other/Proprietary License
|
|
@@ -33,12 +33,14 @@ A Python library for Microsoft SQL Server built with Rust using the [Tiberius](h
|
|
|
33
33
|
|
|
34
34
|
[](https://github.com/Rivendael/pymssql-rs)
|
|
35
35
|
[](https://github.com/Rivendael/pymssql-rs)
|
|
36
|
+
[](https://github.com/Rivendael/pymssql-rs)
|
|
36
37
|
|
|
37
38
|
## Features
|
|
38
39
|
|
|
40
|
+
- **High Performance**: Exceptional throughput with 17,800+ RPS capability
|
|
39
41
|
- **Rust-Powered Backend**: Built with Rust for memory safety and reliability
|
|
40
42
|
- **No ODBC Required**: Direct native SQL Server connection without ODBC drivers
|
|
41
|
-
- **Connection Pooling**: bb8-based connection
|
|
43
|
+
- **Connection Pooling**: Intelligent bb8-based connection pooling (default: 75 max, 25 min idle)
|
|
42
44
|
- **Async-Only Design**: Built on Tokio with clean async/await API
|
|
43
45
|
- **Context Managers**: Automatic resource management with `async with`
|
|
44
46
|
- **Type Safety**: Strong typing with automatic Python type conversion
|
|
@@ -46,15 +48,39 @@ A Python library for Microsoft SQL Server built with Rust using the [Tiberius](h
|
|
|
46
48
|
- **Cross-Platform**: Works on Windows, macOS, and Linux
|
|
47
49
|
- **Simple API**: Clean, intuitive async-only interface
|
|
48
50
|
|
|
49
|
-
##
|
|
51
|
+
## Performance Highlights
|
|
50
52
|
|
|
51
|
-
Fastmssql
|
|
53
|
+
Fastmssql delivers exceptional database performance through Rust-powered architecture:
|
|
52
54
|
|
|
55
|
+
- **Outstanding Throughput**: Up to **17,800+ RPS** with multiple connection pattern
|
|
56
|
+
- **High Performance**: **5,000+ RPS** with single connection (default usage)
|
|
57
|
+
- **Low Latency**: ~2ms average query latency under high load
|
|
58
|
+
- **Scalable Architecture**: Linear scaling with multiple connection objects
|
|
53
59
|
- **Production Ready**: Stable API with comprehensive error handling
|
|
54
60
|
- **Connection Pooling**: Efficient resource management with configurable pools
|
|
55
61
|
- **Type Conversion**: Automatic conversion between SQL Server and Python types
|
|
56
62
|
- **SSL/TLS Support**: Secure connections with flexible encryption options
|
|
57
63
|
|
|
64
|
+
### Performance Benchmarks
|
|
65
|
+
|
|
66
|
+
| Pattern | RPS | Configuration | Use Case |
|
|
67
|
+
|---------|-----|---------------|----------|
|
|
68
|
+
| Single Connection (Default) | **5,000+** | Default pool (75/25) | Standard applications |
|
|
69
|
+
| Multiple Connections | **17,800+** | 50 workers, high_throughput() | High-concurrency scenarios |
|
|
70
|
+
| Conservative Load | 3,500+ | Shared connection | Traditional pooling |
|
|
71
|
+
|
|
72
|
+
**Benchmark Environment:**
|
|
73
|
+
- Database: SQL Server (local instance)
|
|
74
|
+
- Query: `SELECT 1` (minimal overhead)
|
|
75
|
+
- Test Duration: 15-30 seconds per test
|
|
76
|
+
- Hardware: Modern development machine
|
|
77
|
+
|
|
78
|
+
**Key Performance Insights:**
|
|
79
|
+
1. **Multiple Connection Objects**: Creating separate `Connection()` objects eliminates serialization bottlenecks
|
|
80
|
+
2. **Pool Configuration**: Use `PoolConfig.high_throughput()` for demanding workloads
|
|
81
|
+
3. **Optimal Worker Count**: 30-50 concurrent workers provides best throughput
|
|
82
|
+
4. **Tokio Optimization**: Aggressive threading configuration maximizes async performance
|
|
83
|
+
|
|
58
84
|
## Installation
|
|
59
85
|
|
|
60
86
|
### From PyPI (Recommended)
|
|
@@ -87,8 +113,6 @@ cd mssql-python-rust
|
|
|
87
113
|
|
|
88
114
|
## Quick Start
|
|
89
115
|
|
|
90
|
-
> **💡 Performance Tip**: Always reuse `Connection` objects! Each `Connection` manages a connection pool, so creating multiple `Connection` instances defeats the purpose of pooling and hurts performance. Create one `Connection` per database and reuse it throughout your application.
|
|
91
|
-
|
|
92
116
|
### Basic Async Usage (Recommended)
|
|
93
117
|
|
|
94
118
|
```python
|
|
@@ -157,6 +181,40 @@ async def main():
|
|
|
157
181
|
asyncio.run(main())
|
|
158
182
|
```
|
|
159
183
|
|
|
184
|
+
### Performance Patterns
|
|
185
|
+
|
|
186
|
+
For maximum performance in high-concurrency scenarios, create multiple Connection objects:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
import asyncio
|
|
190
|
+
from fastmssql import Connection, PoolConfig
|
|
191
|
+
|
|
192
|
+
async def high_performance_pattern():
|
|
193
|
+
"""Optimal pattern for maximum throughput."""
|
|
194
|
+
connection_string = "Server=localhost;Database=master;User Id=myuser;Password=mypass"
|
|
195
|
+
config = PoolConfig.high_throughput() # Optimized settings
|
|
196
|
+
|
|
197
|
+
async def worker():
|
|
198
|
+
# Each worker gets its own Connection object for maximum throughput
|
|
199
|
+
async with Connection(connection_string, pool_config=config) as conn:
|
|
200
|
+
for _ in range(1000):
|
|
201
|
+
# Use proper SQL Server parameterization with @param syntax
|
|
202
|
+
result = await conn.execute("SELECT data FROM my_table WHERE id = @id", {"id": 123})
|
|
203
|
+
# Process results...
|
|
204
|
+
|
|
205
|
+
# Launch multiple workers - each with their own connection
|
|
206
|
+
workers = [asyncio.create_task(worker()) for _ in range(50)]
|
|
207
|
+
await asyncio.gather(*workers)
|
|
208
|
+
|
|
209
|
+
# This pattern can achieve 17,800+ RPS
|
|
210
|
+
|
|
211
|
+
asyncio.run(high_performance_pattern())
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Key Performance Insight**: While a single Connection object provides excellent performance (5,000+ RPS),
|
|
215
|
+
creating multiple Connection objects eliminates serialization bottlenecks and can achieve 17,800+ RPS
|
|
216
|
+
for maximum throughput scenarios.
|
|
217
|
+
|
|
160
218
|
### Connection Pool Configuration
|
|
161
219
|
|
|
162
220
|
Configure the connection pool for your specific needs:
|
|
@@ -178,95 +236,29 @@ async def main():
|
|
|
178
236
|
async with Connection(connection_string, pool_config) as conn:
|
|
179
237
|
result = await conn.execute("SELECT * FROM users")
|
|
180
238
|
|
|
181
|
-
# Predefined configurations for
|
|
182
|
-
high_throughput_config = PoolConfig.high_throughput()
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
### Connection Pool Configuration
|
|
190
|
-
|
|
191
|
-
For high-throughput applications, you can configure the connection pool:
|
|
192
|
-
|
|
193
|
-
```python
|
|
194
|
-
import asyncio
|
|
195
|
-
from fastmssql import Connection, PoolConfig
|
|
196
|
-
|
|
197
|
-
async def main():
|
|
198
|
-
# High-throughput pool setup
|
|
199
|
-
config = PoolConfig.high_throughput() # Optimized for concurrent access
|
|
239
|
+
# Predefined configurations for different scenarios
|
|
240
|
+
high_throughput_config = PoolConfig.high_throughput() # 100 connections, optimized for high RPS
|
|
241
|
+
maximum_performance = PoolConfig.maximum_performance() # 150 connections, optimized for peak load
|
|
242
|
+
low_resource_config = PoolConfig.low_resource() # 3 connections, minimal resources
|
|
243
|
+
dev_config = PoolConfig.development() # 5 connections, shorter timeouts
|
|
244
|
+
|
|
245
|
+
# Default configuration is optimized for high performance
|
|
246
|
+
# Default: max_size=75, min_idle=25 - ready for production workloads
|
|
200
247
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
async def worker():
|
|
206
|
-
result = await conn.execute("SELECT @@VERSION")
|
|
248
|
+
# For maximum throughput, use multiple Connection objects:
|
|
249
|
+
async def high_perf_worker():
|
|
250
|
+
async with Connection(connection_string, maximum_performance) as conn:
|
|
251
|
+
result = await conn.execute("SELECT * FROM fast_table")
|
|
207
252
|
return result.rows()
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
# Pool monitoring
|
|
214
|
-
stats = await conn.pool_stats()
|
|
215
|
-
print(f"Pool utilization: {stats['active_connections']}/{stats['max_size']}")
|
|
253
|
+
|
|
254
|
+
# Each worker gets its own connection for optimal performance
|
|
255
|
+
tasks = [asyncio.create_task(high_perf_worker()) for _ in range(50)]
|
|
256
|
+
results = await asyncio.gather(*tasks)
|
|
216
257
|
|
|
217
258
|
asyncio.run(main())
|
|
218
259
|
```
|
|
219
260
|
|
|
220
|
-
**Performance Tips:**
|
|
221
|
-
- **Reuse Connection objects**: Create one `Connection` per database and reuse it across your application
|
|
222
|
-
- Use `PoolConfig.high_throughput()` for high-throughput applications
|
|
223
|
-
- Leverage `asyncio.gather()` for concurrent operations
|
|
224
|
-
- Monitor pool stats to optimize connection count
|
|
225
|
-
- Consider connection lifetime for long-running applications
|
|
226
261
|
|
|
227
|
-
**⚠️ Performance Anti-Pattern:**
|
|
228
|
-
```python
|
|
229
|
-
# DON'T DO THIS - Creates new pool for each operation
|
|
230
|
-
async def bad_example():
|
|
231
|
-
async with Connection(conn_str) as conn: # New pool created
|
|
232
|
-
return await conn.execute("SELECT 1")
|
|
233
|
-
|
|
234
|
-
async with Connection(conn_str) as conn: # Another new pool created
|
|
235
|
-
return await conn.execute("SELECT 2")
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
**✅ Correct Pattern:**
|
|
239
|
-
```python
|
|
240
|
-
# DO THIS - Reuse the same connection pool
|
|
241
|
-
async def good_example():
|
|
242
|
-
async with Connection(conn_str) as conn: # Single pool created
|
|
243
|
-
result1 = await conn.execute("SELECT 1")
|
|
244
|
-
result2 = await conn.execute("SELECT 2") # Reuses same pool
|
|
245
|
-
return result1, result2
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### Connection Pool Benefits
|
|
249
|
-
|
|
250
|
-
The bb8 connection pool provides significant performance improvements over traditional Python libraries:
|
|
251
|
-
|
|
252
|
-
| Metric | Traditional Python | fastmssql | Improvement |
|
|
253
|
-
|--------|-------------------|-----------|-------------|
|
|
254
|
-
| **Connection Setup** | 50ms | 0.1ms | **500x faster** |
|
|
255
|
-
| **Memory per Query** | 50-200 KB | 0.5 KB | **100-400x less** |
|
|
256
|
-
| **10 Concurrent Queries** | 500ms | 150ms | **3.3x faster** |
|
|
257
|
-
| **100 Concurrent Queries** | 5000ms | 400ms | **12.5x faster** |
|
|
258
|
-
| **1000 Concurrent Queries** | Timeouts/Errors | 2.9s | **Stable** |
|
|
259
|
-
| **Memory Leaks** | Common | None | **Zero leaks** |
|
|
260
|
-
|
|
261
|
-
**Key Benefits:**
|
|
262
|
-
- **Connection Reuse**: Eliminates connection establishment overhead (500x improvement)
|
|
263
|
-
- **Memory Efficiency**: Uses 100-400x less memory per operation (0.5 KB vs 50-200 KB)
|
|
264
|
-
- **Zero Memory Leaks**: Automatic cleanup with decreasing memory usage over time
|
|
265
|
-
- **Concurrency**: Safe multi-threaded access with automatic pooling
|
|
266
|
-
- **Resource Management**: Intelligent memory and connection lifecycle management
|
|
267
|
-
- **Load Balancing**: Intelligent connection distribution across threads
|
|
268
|
-
- **Fault Tolerance**: Built-in retry logic and connection health checks
|
|
269
|
-
- **Timeouts**: Configurable timeouts prevent hanging connections
|
|
270
262
|
|
|
271
263
|
### Connection Strings
|
|
272
264
|
|
|
@@ -362,89 +354,6 @@ async def main():
|
|
|
362
354
|
asyncio.run(main())
|
|
363
355
|
```
|
|
364
356
|
|
|
365
|
-
### Performance Comparison: bb8 Connection Pool
|
|
366
|
-
|
|
367
|
-
The bb8 connection pool dramatically improves performance, especially under load:
|
|
368
|
-
|
|
369
|
-
```python
|
|
370
|
-
import asyncio
|
|
371
|
-
import time
|
|
372
|
-
from fastmssql import Connection
|
|
373
|
-
|
|
374
|
-
async def performance_comparison():
|
|
375
|
-
connection_string = "Server=localhost;Database=test;User Id=myuser;Password=mypass"
|
|
376
|
-
|
|
377
|
-
# Sequential async operations (still efficient with pool reuse)
|
|
378
|
-
start = time.time()
|
|
379
|
-
async with Connection(connection_string) as conn:
|
|
380
|
-
for i in range(10):
|
|
381
|
-
result = await conn.execute("SELECT COUNT(*) FROM users")
|
|
382
|
-
sequential_time = time.time() - start
|
|
383
|
-
|
|
384
|
-
# Concurrent async operations (much faster with bb8 pool)
|
|
385
|
-
start = time.time()
|
|
386
|
-
async def concurrent_queries():
|
|
387
|
-
tasks = []
|
|
388
|
-
for i in range(10):
|
|
389
|
-
async def query():
|
|
390
|
-
async with Connection(connection_string) as conn: # Pool reuse
|
|
391
|
-
return await conn.execute("SELECT COUNT(*) FROM users")
|
|
392
|
-
tasks.append(query())
|
|
393
|
-
return await asyncio.gather(*tasks)
|
|
394
|
-
|
|
395
|
-
await concurrent_queries()
|
|
396
|
-
concurrent_time = time.time() - start
|
|
397
|
-
|
|
398
|
-
print(f"Sequential: {sequential_time:.3f}s")
|
|
399
|
-
print(f"Concurrent: {concurrent_time:.3f}s")
|
|
400
|
-
print(f"Improvement: {sequential_time/concurrent_time:.1f}x faster")
|
|
401
|
-
|
|
402
|
-
asyncio.run(performance_comparison())
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
**Real-world Performance Benefits:**
|
|
406
|
-
- **Web Applications**: Handle 100+ concurrent requests without connection exhaustion
|
|
407
|
-
- **Batch Processing**: Process large datasets with optimal resource usage
|
|
408
|
-
- **Microservices**: Reliable database connections across service boundaries
|
|
409
|
-
- **Data Analytics**: Concurrent query execution for faster insights
|
|
410
|
-
|
|
411
|
-
## Examples
|
|
412
|
-
|
|
413
|
-
Run the provided examples to see async patterns and features:
|
|
414
|
-
|
|
415
|
-
```bash
|
|
416
|
-
# Basic asynchronous usage
|
|
417
|
-
python examples/basic_usage.py
|
|
418
|
-
|
|
419
|
-
# Advanced asynchronous features
|
|
420
|
-
python examples/advanced_usage.py
|
|
421
|
-
|
|
422
|
-
# Asynchronous usage patterns
|
|
423
|
-
python examples/async_usage.py
|
|
424
|
-
|
|
425
|
-
# Advanced pool configuration
|
|
426
|
-
python examples/advanced_pool_config.py
|
|
427
|
-
|
|
428
|
-
# Connection parameters demonstration
|
|
429
|
-
python examples/connection_parameters_demo.py
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
### Key API Improvements
|
|
433
|
-
|
|
434
|
-
Our async-only design provides a clean, intuitive interface:
|
|
435
|
-
|
|
436
|
-
```python
|
|
437
|
-
# ✅ Clean async API (New Design)
|
|
438
|
-
async with Connection(connection_string) as conn:
|
|
439
|
-
result = await conn.execute(sql) # Intuitive!
|
|
440
|
-
rows_affected = await conn.execute_non_query(sql)
|
|
441
|
-
|
|
442
|
-
# ❌ Old confusing API (Removed)
|
|
443
|
-
# async with Connection(connection_string) as conn:
|
|
444
|
-
# result = await conn.execute_async(sql) # Confusing suffixes
|
|
445
|
-
# rows_affected = await conn.execute_non_query_async(sql)
|
|
446
|
-
```
|
|
447
|
-
|
|
448
357
|
## Development
|
|
449
358
|
|
|
450
359
|
### Building from Source
|
|
@@ -627,19 +536,6 @@ async with mssql.connect_async(conn_str) as conn:
|
|
|
627
536
|
print(f"Pool utilization: {stats['active_connections']}/{stats['connections']}")
|
|
628
537
|
```
|
|
629
538
|
|
|
630
|
-
**Features:**
|
|
631
|
-
- Async-only operations for optimal concurrency
|
|
632
|
-
- Automatic connection pooling with bb8
|
|
633
|
-
- Configurable pool settings via `PoolConfig`
|
|
634
|
-
- Pool statistics and monitoring
|
|
635
|
-
- Improved concurrent performance
|
|
636
|
-
- Better resource management
|
|
637
|
-
|
|
638
|
-
**Breaking Changes:**
|
|
639
|
-
- None - the API is fully backward compatible
|
|
640
|
-
- All existing code continues to work without modification
|
|
641
|
-
- Performance improvements are automatic
|
|
642
|
-
|
|
643
539
|
## Advanced Usage Patterns
|
|
644
540
|
|
|
645
541
|
### Custom Pool Configuration for Different Scenarios
|
|
@@ -728,7 +624,7 @@ async def olap_operations():
|
|
|
728
624
|
return quarterly_report
|
|
729
625
|
```
|
|
730
626
|
|
|
731
|
-
##
|
|
627
|
+
## API Reference
|
|
732
628
|
|
|
733
629
|
### Common Issues
|
|
734
630
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
fastmssql-0.2.8.dist-info/METADATA,sha256=pbN2t4Cuhy1rzvxweu537UUq6vrP42RZNmoOyg_Y94o,24664
|
|
2
|
+
fastmssql-0.2.8.dist-info/RECORD,,
|
|
3
|
+
fastmssql-0.2.8.dist-info/WHEEL,sha256=iJHK_0aL678LL8YWdI9IqLRjj3Upr4xV3uFUSgIuoYw,145
|
|
4
|
+
fastmssql-0.2.8.dist-info/licenses/LICENSE,sha256=NwufX3BNj7RvVtnrshWhkpFOLvWc_YVpGpr3UZGFz_E,4765
|
|
5
|
+
fastmssql/__init__.py,sha256=5D3MpxBm8I33nKOml8wlHJbfH1-9bak68md3xj1Rds0,24923
|
|
6
|
+
fastmssql/fastmssql.cpython-38-aarch64-linux-gnu.so,sha256=foMO4JuiCYggPa4Eu1G53Ed40UUTQenuNlLfR1BNgbQ,2182976
|
fastmssql-0.2.6.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
fastmssql-0.2.6.dist-info/METADATA,sha256=skDVnYNSe4ny1dYxsHrlehGnRLo8ij-pQFsBGNaJvwg,27674
|
|
2
|
-
fastmssql-0.2.6.dist-info/RECORD,,
|
|
3
|
-
fastmssql-0.2.6.dist-info/WHEEL,sha256=iJHK_0aL678LL8YWdI9IqLRjj3Upr4xV3uFUSgIuoYw,145
|
|
4
|
-
fastmssql-0.2.6.dist-info/licenses/LICENSE,sha256=NwufX3BNj7RvVtnrshWhkpFOLvWc_YVpGpr3UZGFz_E,4765
|
|
5
|
-
fastmssql/__init__.py,sha256=2I8gj3Ump7-VdoqnLpp4iiZjFrFZeyeG7iwOFJEz544,119
|
|
6
|
-
fastmssql/fastmssql.cpython-38-aarch64-linux-gnu.so,sha256=LLdgJbFU42nsAz4ond6OyZQuk6YIpJoVzKRDjaJtaMg,4780328
|
|
File without changes
|
|
File without changes
|