fastmssql 0.2.2__cp313-cp313-win_amd64.whl → 0.3.2__cp313-cp313-win_amd64.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 +689 -11
- fastmssql/fastmssql.cp313-win_amd64.pyd +0 -0
- fastmssql-0.3.2.dist-info/METADATA +346 -0
- fastmssql-0.3.2.dist-info/RECORD +6 -0
- fastmssql-0.3.2.dist-info/licenses/LICENSE +675 -0
- fastmssql/fastmssql.py +0 -706
- fastmssql/fastmssql_core.cp313-win_amd64.pyd +0 -0
- fastmssql-0.2.2.dist-info/METADATA +0 -819
- fastmssql-0.2.2.dist-info/RECORD +0 -7
- fastmssql-0.2.2.dist-info/licenses/LICENSE +0 -139
- {fastmssql-0.2.2.dist-info → fastmssql-0.3.2.dist-info}/WHEEL +0 -0
|
@@ -1,819 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fastmssql
|
|
3
|
-
Version: 0.2.2
|
|
4
|
-
Classifier: Development Status :: 4 - Beta
|
|
5
|
-
Classifier: Intended Audience :: Developers
|
|
6
|
-
Classifier: License :: Other/Proprietary License
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Classifier: Programming Language :: Rust
|
|
15
|
-
Classifier: Topic :: Database
|
|
16
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
-
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
18
|
-
Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
|
|
19
|
-
Requires-Dist: psutil>=5.9.0 ; extra == 'dev'
|
|
20
|
-
Provides-Extra: dev
|
|
21
|
-
License-File: LICENSE
|
|
22
|
-
Summary: A high-performance Python library for Microsoft SQL Server using Rust and Tiberius
|
|
23
|
-
Author-email: Riveranda <riverb514@gmail.com>
|
|
24
|
-
License: PolyForm Noncommercial License 1.0.0
|
|
25
|
-
Requires-Python: >=3.8
|
|
26
|
-
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
27
|
-
Project-URL: Homepage, https://github.com/Rivendael/pymssql-rs
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# Fastmssql ⚡
|
|
31
|
-
|
|
32
|
-
A **blazingly fast** Python library for Microsoft SQL Server that delivers **significant performance improvements** over standard libraries. Built with Rust using the [Tiberius](https://github.com/prisma/tiberius) driver, [PyO3](https://github.com/PyO3/pyo3), and [bb8](https://github.com/djc/bb8) connection pooling.
|
|
33
|
-
|
|
34
|
-
> **🚀 Performance Highlight**: Achieves **3,493 requests/second** and **0.08 KB memory per query** in our benchmarks
|
|
35
|
-
|
|
36
|
-
[](https://github.com/Rivendael/pymssql-rs)
|
|
37
|
-
[](https://github.com/Rivendael/pymssql-rs)
|
|
38
|
-
[](https://github.com/Rivendael/pymssql-rs)
|
|
39
|
-
[](https://github.com/Rivendael/pymssql-rs)
|
|
40
|
-
[](https://github.com/Rivendael/pymssql-rs)
|
|
41
|
-
|
|
42
|
-
## Features
|
|
43
|
-
|
|
44
|
-
- **High Performance**: Built with Rust for memory safety and speed
|
|
45
|
-
- **No ODBC Required**: Direct native SQL Server connection without ODBC drivers
|
|
46
|
-
- **Connection Pooling**: Advanced bb8-based connection pool for optimal performance
|
|
47
|
-
- **Async-Only Design**: Built on Tokio for excellent concurrency with clean async/await API
|
|
48
|
-
- **Context Managers**: Automatic resource management with `async with`
|
|
49
|
-
- **Type Safety**: Strong typing with automatic Python type conversion
|
|
50
|
-
- **Thread Safety**: Full support for concurrent operations
|
|
51
|
-
- **Cross-Platform**: Works on Windows, macOS, and Linux
|
|
52
|
-
- **Simple API**: Clean, intuitive async-only interface
|
|
53
|
-
|
|
54
|
-
## ⚡ Performance
|
|
55
|
-
|
|
56
|
-
Fastmssql delivers **excellent performance** that outperforms standard Python SQL Server libraries in our testing:
|
|
57
|
-
|
|
58
|
-
### 🚀 Benchmark Results
|
|
59
|
-
|
|
60
|
-
| Library | RPS (Requests/Second) | Performance vs fastmssql |
|
|
61
|
-
|---------|----------------------|--------------------------|
|
|
62
|
-
| **fastmssql** | **3,493** | **Baseline** |
|
|
63
|
-
| Other Python libraries | ~300-600* | **5-11x slower** |
|
|
64
|
-
|
|
65
|
-
*Benchmarks performed with 20 concurrent workers executing `SELECT @@VERSION` queries on our test environment. Performance of other libraries may vary based on configuration, environment, and specific use cases. We recommend conducting your own benchmarks for your specific requirements.*
|
|
66
|
-
|
|
67
|
-
### 🧠 Memory Efficiency
|
|
68
|
-
|
|
69
|
-
Fastmssql delivers **exceptional memory efficiency** with minimal overhead:
|
|
70
|
-
|
|
71
|
-
| Metric | Value | Description |
|
|
72
|
-
|--------|-------|-------------|
|
|
73
|
-
| **Per Query Overhead** | **0.08 KB** | Memory used per database query |
|
|
74
|
-
| **Concurrent Overhead** | **3.52 KB** | Memory per concurrent operation |
|
|
75
|
-
| **Connection Pool** | **5.26 MB** | One-time pool initialization |
|
|
76
|
-
| **Memory Leaks** | **None** | Actually reduces memory over time |
|
|
77
|
-
|
|
78
|
-
**Memory Efficiency Highlights:**
|
|
79
|
-
- **12,800 queries per MB** of memory overhead
|
|
80
|
-
- **Zero memory leaks** - memory usage decreases over time
|
|
81
|
-
- **100-1000x more efficient** than libraries without connection pooling
|
|
82
|
-
- **Stable memory usage** under high concurrent load
|
|
83
|
-
|
|
84
|
-
*Traditional Python SQL Server libraries typically use 50-200 KB per operation due to connection overhead and lack of efficient pooling.*
|
|
85
|
-
|
|
86
|
-
## Installation
|
|
87
|
-
|
|
88
|
-
### From PyPI (Recommended)
|
|
89
|
-
|
|
90
|
-
Install fastmssql using pip:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
pip install fastmssql
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Prerequisites
|
|
97
|
-
|
|
98
|
-
- Python 3.8 to 3.13
|
|
99
|
-
- Microsoft SQL Server (any recent version)
|
|
100
|
-
|
|
101
|
-
### From Source (Development)
|
|
102
|
-
Ensure you have Docker, Rust, and Python installed.
|
|
103
|
-
If you want to build from source or contribute to development:
|
|
104
|
-
|
|
105
|
-
1. Clone the repository:
|
|
106
|
-
```bash
|
|
107
|
-
git clone <your-repo-url>
|
|
108
|
-
cd mssql-python-rust
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
2. Run the setup script
|
|
112
|
-
```
|
|
113
|
-
./setup.sh
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Quick Start
|
|
117
|
-
|
|
118
|
-
> **💡 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.
|
|
119
|
-
|
|
120
|
-
### Basic Async Usage (Recommended)
|
|
121
|
-
|
|
122
|
-
```python
|
|
123
|
-
import asyncio
|
|
124
|
-
from fastmssql import Connection
|
|
125
|
-
|
|
126
|
-
async def main():
|
|
127
|
-
# Connect to SQL Server using async context manager
|
|
128
|
-
connection_string = "Server=localhost;Database=master;User Id=myuser;Password=mypass"
|
|
129
|
-
|
|
130
|
-
# Automatic connection pool management
|
|
131
|
-
async with Connection(connection_string) as conn:
|
|
132
|
-
rows = await conn.execute("SELECT @@VERSION as version")
|
|
133
|
-
for row in rows:
|
|
134
|
-
print(row['version'])
|
|
135
|
-
|
|
136
|
-
# Pool statistics
|
|
137
|
-
stats = conn.pool_stats()
|
|
138
|
-
print(f"Pool: {stats['active_connections']}/{stats['connections']} connections active")
|
|
139
|
-
|
|
140
|
-
asyncio.run(main())
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Connection Methods
|
|
144
|
-
|
|
145
|
-
The library supports two ways to connect to SQL Server:
|
|
146
|
-
|
|
147
|
-
#### 1. Connection String (Traditional)
|
|
148
|
-
|
|
149
|
-
```python
|
|
150
|
-
import asyncio
|
|
151
|
-
from fastmssql import Connection
|
|
152
|
-
|
|
153
|
-
async def main():
|
|
154
|
-
# Traditional connection string approach
|
|
155
|
-
connection_string = "Server=localhost;Database=master;User Id=myuser;Password=mypass"
|
|
156
|
-
|
|
157
|
-
async with Connection(connection_string=connection_string) as conn:
|
|
158
|
-
result = await conn.execute("SELECT @@VERSION as version")
|
|
159
|
-
for row in result.rows():
|
|
160
|
-
print(row['version'])
|
|
161
|
-
|
|
162
|
-
asyncio.run(main())
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
#### 2. Individual Parameters
|
|
166
|
-
|
|
167
|
-
```python
|
|
168
|
-
import asyncio
|
|
169
|
-
from fastmssql import Connection
|
|
170
|
-
|
|
171
|
-
async def main():
|
|
172
|
-
# Using individual connection parameters
|
|
173
|
-
|
|
174
|
-
# SQL Server Authentication
|
|
175
|
-
async with Connection(
|
|
176
|
-
server="localhost",
|
|
177
|
-
database="master",
|
|
178
|
-
username="myuser",
|
|
179
|
-
password="mypassword"
|
|
180
|
-
) as conn:
|
|
181
|
-
result = await conn.execute("SELECT SUSER_NAME() as login")
|
|
182
|
-
for row in result.rows():
|
|
183
|
-
print(f"Logged in as: {row['login']}")
|
|
184
|
-
|
|
185
|
-
asyncio.run(main())
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Connection Pool Configuration
|
|
189
|
-
|
|
190
|
-
Configure the connection pool for your specific needs:
|
|
191
|
-
|
|
192
|
-
```python
|
|
193
|
-
import asyncio
|
|
194
|
-
from fastmssql import Connection, PoolConfig
|
|
195
|
-
|
|
196
|
-
async def main():
|
|
197
|
-
# Custom pool configuration
|
|
198
|
-
pool_config = PoolConfig(
|
|
199
|
-
max_size=20, # Maximum connections in pool
|
|
200
|
-
min_idle=5, # Minimum idle connections
|
|
201
|
-
max_lifetime_secs=3600, # Connection max lifetime (1 hour)
|
|
202
|
-
idle_timeout_secs=600, # Idle connection timeout (10 min)
|
|
203
|
-
connection_timeout_secs=30 # Max wait time for connection
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
async with Connection(connection_string, pool_config) as conn:
|
|
207
|
-
result = await conn.execute("SELECT * FROM users")
|
|
208
|
-
|
|
209
|
-
# Predefined configurations for common scenarios
|
|
210
|
-
high_throughput_config = PoolConfig.high_throughput() # 20 connections, optimized for load
|
|
211
|
-
low_resource_config = PoolConfig.low_resource() # 3 connections, minimal resources
|
|
212
|
-
dev_config = PoolConfig.development() # 5 connections, shorter timeouts
|
|
213
|
-
|
|
214
|
-
asyncio.run(main())
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### 🔥 Maximum Performance Configuration
|
|
218
|
-
|
|
219
|
-
For applications requiring **extreme performance**, use the high-throughput configuration:
|
|
220
|
-
|
|
221
|
-
```python
|
|
222
|
-
import asyncio
|
|
223
|
-
from fastmssql import Connection, PoolConfig
|
|
224
|
-
|
|
225
|
-
async def main():
|
|
226
|
-
# Maximum performance setup
|
|
227
|
-
extreme_config = PoolConfig.high_throughput() # Optimized for 3000+ RPS
|
|
228
|
-
|
|
229
|
-
async with Connection(connection_string, extreme_config) as conn:
|
|
230
|
-
# This setup achieves 3,493 RPS in benchmarks
|
|
231
|
-
|
|
232
|
-
# Concurrent workers for maximum throughput
|
|
233
|
-
async def worker():
|
|
234
|
-
result = await conn.execute("SELECT @@VERSION")
|
|
235
|
-
return result.rows()
|
|
236
|
-
|
|
237
|
-
# Run 20 concurrent workers
|
|
238
|
-
tasks = [worker() for _ in range(20)]
|
|
239
|
-
results = await asyncio.gather(*tasks)
|
|
240
|
-
|
|
241
|
-
# Pool monitoring
|
|
242
|
-
stats = await conn.pool_stats()
|
|
243
|
-
print(f"Pool utilization: {stats['active_connections']}/{stats['max_size']}")
|
|
244
|
-
|
|
245
|
-
asyncio.run(main())
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
**Performance Tips:**
|
|
249
|
-
- **Reuse Connection objects**: Create one `Connection` per database and reuse it across your application
|
|
250
|
-
- Use `PoolConfig.high_throughput()` for maximum RPS
|
|
251
|
-
- Leverage `asyncio.gather()` for concurrent operations
|
|
252
|
-
- Monitor pool stats to optimize connection count
|
|
253
|
-
- Consider connection lifetime for long-running applications
|
|
254
|
-
|
|
255
|
-
**⚠️ Performance Anti-Pattern:**
|
|
256
|
-
```python
|
|
257
|
-
# DON'T DO THIS - Creates new pool for each operation
|
|
258
|
-
async def bad_example():
|
|
259
|
-
async with Connection(conn_str) as conn: # New pool created
|
|
260
|
-
return await conn.execute("SELECT 1")
|
|
261
|
-
|
|
262
|
-
async with Connection(conn_str) as conn: # Another new pool created
|
|
263
|
-
return await conn.execute("SELECT 2")
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
**✅ Correct Pattern:**
|
|
267
|
-
```python
|
|
268
|
-
# DO THIS - Reuse the same connection pool
|
|
269
|
-
async def good_example():
|
|
270
|
-
async with Connection(conn_str) as conn: # Single pool created
|
|
271
|
-
result1 = await conn.execute("SELECT 1")
|
|
272
|
-
result2 = await conn.execute("SELECT 2") # Reuses same pool
|
|
273
|
-
return result1, result2
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
### Connection Pool Benefits
|
|
277
|
-
|
|
278
|
-
The bb8 connection pool provides significant performance improvements over traditional Python libraries:
|
|
279
|
-
|
|
280
|
-
| Metric | Traditional Python | fastmssql | Improvement |
|
|
281
|
-
|--------|-------------------|-----------|-------------|
|
|
282
|
-
| **Connection Setup** | 50ms | 0.1ms | **500x faster** |
|
|
283
|
-
| **Memory per Query** | 50-200 KB | 0.08 KB | **625-2500x less** |
|
|
284
|
-
| **10 Concurrent Queries** | 500ms | 150ms | **3.3x faster** |
|
|
285
|
-
| **100 Concurrent Queries** | 5000ms | 400ms | **12.5x faster** |
|
|
286
|
-
| **1000 Concurrent Queries** | Timeouts/Errors | 2.9s | **Stable** |
|
|
287
|
-
| **Memory Leaks** | Common | None | **Zero leaks** |
|
|
288
|
-
|
|
289
|
-
**Key Benefits:**
|
|
290
|
-
- **Connection Reuse**: Eliminates connection establishment overhead (500x improvement)
|
|
291
|
-
- **Memory Efficiency**: Uses 625-2500x less memory per operation (0.08 KB vs 50-200 KB)
|
|
292
|
-
- **Zero Memory Leaks**: Automatic cleanup with decreasing memory usage over time
|
|
293
|
-
- **Concurrency**: Safe multi-threaded access with automatic pooling
|
|
294
|
-
- **Resource Management**: Intelligent memory and connection lifecycle management
|
|
295
|
-
- **Load Balancing**: Intelligent connection distribution across threads
|
|
296
|
-
- **Fault Tolerance**: Built-in retry logic and connection health checks
|
|
297
|
-
- **Timeouts**: Configurable timeouts prevent hanging connections
|
|
298
|
-
|
|
299
|
-
### Connection Strings
|
|
300
|
-
|
|
301
|
-
The library supports standard SQL Server connection string formats:
|
|
302
|
-
|
|
303
|
-
```python
|
|
304
|
-
# SQL Server Authentication
|
|
305
|
-
conn_str = "Server=localhost;Database=MyDB;User Id=sa;Password=MyPassword"
|
|
306
|
-
|
|
307
|
-
# With specific port
|
|
308
|
-
conn_str = "Server=localhost,1433;Database=MyDB;User Id=myuser;Password=mypass"
|
|
309
|
-
|
|
310
|
-
# Azure SQL Database
|
|
311
|
-
conn_str = "Server=tcp:myserver.database.windows.net,1433;Database=MyDB;User Id=myuser;Password=mypass;Encrypt=true"
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Working with Data
|
|
315
|
-
|
|
316
|
-
```python
|
|
317
|
-
import asyncio
|
|
318
|
-
from fastmssql import Connection
|
|
319
|
-
|
|
320
|
-
async def main():
|
|
321
|
-
async with Connection(connection_string) as conn:
|
|
322
|
-
# Execute queries
|
|
323
|
-
users = await conn.execute("SELECT id, name, email FROM users WHERE active = 1")
|
|
324
|
-
|
|
325
|
-
# Iterate through results
|
|
326
|
-
for user in users:
|
|
327
|
-
print(f"User {user['id']}: {user['name']} ({user['email']})")
|
|
328
|
-
|
|
329
|
-
# Execute non-query operations
|
|
330
|
-
rows_affected = await conn.execute_non_query(
|
|
331
|
-
"UPDATE users SET last_login = GETDATE() WHERE id = 123"
|
|
332
|
-
)
|
|
333
|
-
print(f"Updated {rows_affected} rows")
|
|
334
|
-
|
|
335
|
-
# Work with different data types
|
|
336
|
-
data = await conn.execute("""
|
|
337
|
-
SELECT
|
|
338
|
-
42 as int_value,
|
|
339
|
-
3.14159 as float_value,
|
|
340
|
-
'Hello World' as string_value,
|
|
341
|
-
GETDATE() as datetime_value,
|
|
342
|
-
CAST(1 as BIT) as bool_value,
|
|
343
|
-
NULL as null_value
|
|
344
|
-
""")
|
|
345
|
-
|
|
346
|
-
row = data[0]
|
|
347
|
-
for column_name, value in row.items():
|
|
348
|
-
print(f"{column_name}: {value} (type: {type(value).__name__})")
|
|
349
|
-
|
|
350
|
-
asyncio.run(main())
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Usage
|
|
354
|
-
|
|
355
|
-
### Asynchronous Usage with Connection Pooling
|
|
356
|
-
|
|
357
|
-
Full async/await support with automatic connection pool management:
|
|
358
|
-
|
|
359
|
-
```python
|
|
360
|
-
import asyncio
|
|
361
|
-
from fastmssql import Connection
|
|
362
|
-
|
|
363
|
-
async def main():
|
|
364
|
-
connection_string = "Server=localhost;Database=test;Integrated Security=true"
|
|
365
|
-
|
|
366
|
-
# Async context manager with automatic pool management
|
|
367
|
-
async with Connection(connection_string) as conn:
|
|
368
|
-
rows = await conn.execute("SELECT name FROM sys.databases")
|
|
369
|
-
for row in rows:
|
|
370
|
-
print(row['name'])
|
|
371
|
-
|
|
372
|
-
# Pool statistics
|
|
373
|
-
stats = conn.pool_stats()
|
|
374
|
-
print(f"Pool: {stats['active_connections']}/{stats['connections']} connections active")
|
|
375
|
-
|
|
376
|
-
# High-performance concurrent operations
|
|
377
|
-
async def fetch_user_data(user_id):
|
|
378
|
-
async with Connection(connection_string) as conn:
|
|
379
|
-
return await conn.execute(f"SELECT * FROM users WHERE id = {user_id}")
|
|
380
|
-
|
|
381
|
-
# Execute multiple queries concurrently using the connection pool
|
|
382
|
-
user_ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
383
|
-
tasks = [fetch_user_data(uid) for uid in user_ids]
|
|
384
|
-
results = await asyncio.gather(*tasks) # bb8 pool handles concurrent connections efficiently
|
|
385
|
-
|
|
386
|
-
for user_data in results:
|
|
387
|
-
if user_data:
|
|
388
|
-
print(f"User: {user_data[0]['name']}")
|
|
389
|
-
|
|
390
|
-
asyncio.run(main())
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### Performance Comparison: bb8 Connection Pool
|
|
394
|
-
|
|
395
|
-
The bb8 connection pool dramatically improves performance, especially under load:
|
|
396
|
-
|
|
397
|
-
```python
|
|
398
|
-
import asyncio
|
|
399
|
-
import time
|
|
400
|
-
from fastmssql import Connection
|
|
401
|
-
|
|
402
|
-
async def performance_comparison():
|
|
403
|
-
connection_string = "Server=localhost;Database=test;User Id=myuser;Password=mypass"
|
|
404
|
-
|
|
405
|
-
# Sequential async operations (still efficient with pool reuse)
|
|
406
|
-
start = time.time()
|
|
407
|
-
async with Connection(connection_string) as conn:
|
|
408
|
-
for i in range(10):
|
|
409
|
-
result = await conn.execute("SELECT COUNT(*) FROM users")
|
|
410
|
-
sequential_time = time.time() - start
|
|
411
|
-
|
|
412
|
-
# Concurrent async operations (much faster with bb8 pool)
|
|
413
|
-
start = time.time()
|
|
414
|
-
async def concurrent_queries():
|
|
415
|
-
tasks = []
|
|
416
|
-
for i in range(10):
|
|
417
|
-
async def query():
|
|
418
|
-
async with Connection(connection_string) as conn: # Pool reuse
|
|
419
|
-
return await conn.execute("SELECT COUNT(*) FROM users")
|
|
420
|
-
tasks.append(query())
|
|
421
|
-
return await asyncio.gather(*tasks)
|
|
422
|
-
|
|
423
|
-
await concurrent_queries()
|
|
424
|
-
concurrent_time = time.time() - start
|
|
425
|
-
|
|
426
|
-
print(f"Sequential: {sequential_time:.3f}s")
|
|
427
|
-
print(f"Concurrent: {concurrent_time:.3f}s")
|
|
428
|
-
print(f"Improvement: {sequential_time/concurrent_time:.1f}x faster")
|
|
429
|
-
|
|
430
|
-
asyncio.run(performance_comparison())
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
**Real-world Performance Benefits:**
|
|
434
|
-
- **Web Applications**: Handle 100+ concurrent requests without connection exhaustion
|
|
435
|
-
- **Batch Processing**: Process large datasets with optimal resource usage
|
|
436
|
-
- **Microservices**: Reliable database connections across service boundaries
|
|
437
|
-
- **Data Analytics**: Concurrent query execution for faster insights
|
|
438
|
-
|
|
439
|
-
## Examples
|
|
440
|
-
|
|
441
|
-
Run the provided examples to see async patterns and features:
|
|
442
|
-
|
|
443
|
-
```bash
|
|
444
|
-
# Basic asynchronous usage
|
|
445
|
-
python examples/basic_usage.py
|
|
446
|
-
|
|
447
|
-
# Advanced asynchronous features
|
|
448
|
-
python examples/advanced_usage.py
|
|
449
|
-
|
|
450
|
-
# Asynchronous usage patterns
|
|
451
|
-
python examples/async_usage.py
|
|
452
|
-
|
|
453
|
-
# Advanced pool configuration
|
|
454
|
-
python examples/advanced_pool_config.py
|
|
455
|
-
|
|
456
|
-
# Connection parameters demonstration
|
|
457
|
-
python examples/connection_parameters_demo.py
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### Key API Improvements
|
|
461
|
-
|
|
462
|
-
Our async-only design provides a clean, intuitive interface:
|
|
463
|
-
|
|
464
|
-
```python
|
|
465
|
-
# ✅ Clean async API (New Design)
|
|
466
|
-
async with Connection(connection_string) as conn:
|
|
467
|
-
result = await conn.execute(sql) # Intuitive!
|
|
468
|
-
rows_affected = await conn.execute_non_query(sql)
|
|
469
|
-
|
|
470
|
-
# ❌ Old confusing API (Removed)
|
|
471
|
-
# async with Connection(connection_string) as conn:
|
|
472
|
-
# result = await conn.execute_async(sql) # Confusing suffixes
|
|
473
|
-
# rows_affected = await conn.execute_non_query_async(sql)
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## Development
|
|
477
|
-
|
|
478
|
-
### Building from Source
|
|
479
|
-
|
|
480
|
-
```bash
|
|
481
|
-
# Install development dependencies
|
|
482
|
-
pip install maturin pytest pytest-asyncio black ruff
|
|
483
|
-
|
|
484
|
-
# Build in development mode
|
|
485
|
-
maturin develop
|
|
486
|
-
|
|
487
|
-
# Run tests
|
|
488
|
-
python -m pytest tests/
|
|
489
|
-
|
|
490
|
-
# Format code
|
|
491
|
-
black python/
|
|
492
|
-
ruff check python/
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### Project Structure
|
|
496
|
-
|
|
497
|
-
```
|
|
498
|
-
mssql-python-rust/
|
|
499
|
-
├── src/ # Rust source code
|
|
500
|
-
│ ├── lib.rs # Main library entry point
|
|
501
|
-
│ ├── connection.rs # Connection handling
|
|
502
|
-
│ ├── query.rs # Query execution
|
|
503
|
-
│ └── types.rs # Type definitions
|
|
504
|
-
├── python/ # Python source code
|
|
505
|
-
│ ├── __init__.py # Main Python module
|
|
506
|
-
│ ├── mssql.py # High-level API
|
|
507
|
-
│ └── types.py # Python type definitions
|
|
508
|
-
├── examples/ # Usage examples
|
|
509
|
-
├── tests/ # Test files
|
|
510
|
-
├── Cargo.toml # Rust dependencies
|
|
511
|
-
├── pyproject.toml # Python project configuration
|
|
512
|
-
└── README.md # This file
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
### Testing
|
|
516
|
-
|
|
517
|
-
Run the examples to test your installation:
|
|
518
|
-
|
|
519
|
-
```bash
|
|
520
|
-
# Basic functionality
|
|
521
|
-
python examples/basic_usage.py
|
|
522
|
-
|
|
523
|
-
# Advanced features
|
|
524
|
-
python examples/advanced_usage.py
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
## API Reference
|
|
528
|
-
|
|
529
|
-
### Core Classes
|
|
530
|
-
|
|
531
|
-
#### `Connection`
|
|
532
|
-
Main connection class with bb8 connection pool management.
|
|
533
|
-
|
|
534
|
-
**Constructor:**
|
|
535
|
-
```python
|
|
536
|
-
Connection(connection_string: str, pool_config: Optional[PoolConfig] = None)
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
**Context Manager Support:**
|
|
540
|
-
```python
|
|
541
|
-
# Synchronous
|
|
542
|
-
with Connection(conn_str) as conn:
|
|
543
|
-
result = conn.execute("SELECT * FROM table")
|
|
544
|
-
|
|
545
|
-
# Asynchronous
|
|
546
|
-
async with Connection(conn_str) as conn:
|
|
547
|
-
result = await conn.execute_async("SELECT * FROM table")
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
**Methods:**
|
|
551
|
-
- `execute(sql: str) -> List[Row]` - Execute a query synchronously
|
|
552
|
-
- `pool_stats() -> dict` - Get connection pool statistics
|
|
553
|
-
- `disconnect()` - Close the connection pool
|
|
554
|
-
- `is_connected() -> bool` - Check if pool is active
|
|
555
|
-
|
|
556
|
-
**Pool Statistics:**
|
|
557
|
-
```python
|
|
558
|
-
stats = conn.pool_stats()
|
|
559
|
-
# Returns: {
|
|
560
|
-
# 'connections': 10, # Total connections in pool
|
|
561
|
-
# 'active_connections': 3, # Currently active connections
|
|
562
|
-
# 'idle_connections': 7 # Available idle connections
|
|
563
|
-
# }
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
#### `PoolConfig`
|
|
567
|
-
Configuration class for bb8 connection pool settings.
|
|
568
|
-
|
|
569
|
-
**Constructor:**
|
|
570
|
-
```python
|
|
571
|
-
PoolConfig(
|
|
572
|
-
max_size: int = 10, # Maximum connections in pool
|
|
573
|
-
min_idle: int = 0, # Minimum idle connections
|
|
574
|
-
max_lifetime_secs: Optional[int] = None, # Connection max lifetime
|
|
575
|
-
idle_timeout_secs: Optional[int] = None, # Idle connection timeout
|
|
576
|
-
connection_timeout_secs: int = 30 # Max wait time for connection
|
|
577
|
-
)
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
**Predefined Configurations:**
|
|
581
|
-
```python
|
|
582
|
-
# High throughput applications (web servers, APIs)
|
|
583
|
-
config = PoolConfig.high_throughput() # 20 connections, optimized settings
|
|
584
|
-
|
|
585
|
-
# Low resource environments (embedded, containers)
|
|
586
|
-
config = PoolConfig.low_resource() # 3 connections, minimal overhead
|
|
587
|
-
|
|
588
|
-
# Development environments
|
|
589
|
-
config = PoolConfig.development() # 5 connections, shorter timeouts
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
#### `Row`
|
|
593
|
-
Represents a database row with column access.
|
|
594
|
-
|
|
595
|
-
**Methods:**
|
|
596
|
-
- `get(column: str) -> Value` - Get value by column name
|
|
597
|
-
- `get_by_index(index: int) -> Value` - Get value by column index
|
|
598
|
-
- `columns() -> List[str]` - Get column names
|
|
599
|
-
- `values() -> List[Value]` - Get all values
|
|
600
|
-
- `to_dict() -> dict` - Convert to dictionary
|
|
601
|
-
|
|
602
|
-
### Module Functions
|
|
603
|
-
|
|
604
|
-
#### Connection Management
|
|
605
|
-
```python
|
|
606
|
-
# Create connection with default pool settings
|
|
607
|
-
connect(connection_string: str) -> Connection
|
|
608
|
-
|
|
609
|
-
# Create async connection with default pool settings
|
|
610
|
-
connect_async(connection_string: str) -> Connection
|
|
611
|
-
|
|
612
|
-
# One-liner query execution
|
|
613
|
-
execute(connection_string: str, sql: str) -> List[dict]
|
|
614
|
-
execute_async(connection_string: str, sql: str) -> List[dict]
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
#### Utility Functions
|
|
618
|
-
```python
|
|
619
|
-
version() -> str # Get library version
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
### Connection Pool Architecture
|
|
623
|
-
|
|
624
|
-
The library uses the bb8 connection pool for efficient resource management:
|
|
625
|
-
|
|
626
|
-
1. **Pool Initialization**: Creates a pool of reusable connections on first use
|
|
627
|
-
2. **Connection Reuse**: Automatically reuses idle connections for new requests
|
|
628
|
-
3. **Load Balancing**: Distributes connections across concurrent operations
|
|
629
|
-
4. **Automatic Cleanup**: Closes idle connections based on timeout settings
|
|
630
|
-
5. **Thread Safety**: Safe for use across multiple threads and async tasks
|
|
631
|
-
|
|
632
|
-
### Error Handling
|
|
633
|
-
|
|
634
|
-
```python
|
|
635
|
-
try:
|
|
636
|
-
async with mssql.connect_async(connection_string) as conn:
|
|
637
|
-
result = await conn.execute("SELECT * FROM invalid_table")
|
|
638
|
-
except Exception as e:
|
|
639
|
-
print(f"Database error: {e}")
|
|
640
|
-
# Connection automatically returned to pool even on error
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
## Migration to Async-Only Architecture
|
|
644
|
-
|
|
645
|
-
This library has been upgraded to use async-only operations with the bb8 connection pool for improved performance and reliability.
|
|
646
|
-
|
|
647
|
-
**Async-Only API:**
|
|
648
|
-
```python
|
|
649
|
-
# Async-only with automatic connection pooling
|
|
650
|
-
async with mssql.connect_async(conn_str) as conn:
|
|
651
|
-
result = await conn.execute("SELECT * FROM table")
|
|
652
|
-
|
|
653
|
-
# Pool statistics
|
|
654
|
-
stats = conn.pool_stats()
|
|
655
|
-
print(f"Pool utilization: {stats['active_connections']}/{stats['connections']}")
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
**Features:**
|
|
659
|
-
- Async-only operations for maximum performance
|
|
660
|
-
- Automatic connection pooling with bb8
|
|
661
|
-
- Configurable pool settings via `PoolConfig`
|
|
662
|
-
- Pool statistics and monitoring
|
|
663
|
-
- Improved concurrent performance
|
|
664
|
-
- Better resource management
|
|
665
|
-
|
|
666
|
-
**Breaking Changes:**
|
|
667
|
-
- None - the API is fully backward compatible
|
|
668
|
-
- All existing code continues to work without modification
|
|
669
|
-
- Performance improvements are automatic
|
|
670
|
-
|
|
671
|
-
## Advanced Usage Patterns
|
|
672
|
-
|
|
673
|
-
### Custom Pool Configuration for Different Scenarios
|
|
674
|
-
|
|
675
|
-
```python
|
|
676
|
-
from fastmssql import Connection, PoolConfig
|
|
677
|
-
|
|
678
|
-
# High-load web application
|
|
679
|
-
web_config = PoolConfig(
|
|
680
|
-
max_size=50, # Handle many concurrent requests
|
|
681
|
-
min_idle=10, # Keep connections ready
|
|
682
|
-
max_lifetime_secs=1800, # 30 min connection lifetime
|
|
683
|
-
idle_timeout_secs=300, # 5 min idle timeout
|
|
684
|
-
connection_timeout_secs=10 # Fast timeout for web responses
|
|
685
|
-
)
|
|
686
|
-
|
|
687
|
-
# Batch processing application
|
|
688
|
-
batch_config = PoolConfig(
|
|
689
|
-
max_size=5, # Fewer connections
|
|
690
|
-
min_idle=2, # Always keep some ready
|
|
691
|
-
max_lifetime_secs=7200, # 2 hour lifetime for long operations
|
|
692
|
-
idle_timeout_secs=1800, # 30 min idle timeout
|
|
693
|
-
connection_timeout_secs=60 # Longer timeout for batch work
|
|
694
|
-
)
|
|
695
|
-
|
|
696
|
-
# Microservice with limited resources
|
|
697
|
-
micro_config = PoolConfig(
|
|
698
|
-
max_size=3, # Minimal connections
|
|
699
|
-
min_idle=1, # One always ready
|
|
700
|
-
max_lifetime_secs=3600, # 1 hour lifetime
|
|
701
|
-
idle_timeout_secs=600, # 10 min idle timeout
|
|
702
|
-
connection_timeout_secs=15 # Quick timeout
|
|
703
|
-
)
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### Monitoring Pool Health
|
|
707
|
-
|
|
708
|
-
```python
|
|
709
|
-
async def monitor_database_pool():
|
|
710
|
-
"""Monitor connection pool health and performance"""
|
|
711
|
-
|
|
712
|
-
async with mssql.connect_async(connection_string) as conn:
|
|
713
|
-
while True:
|
|
714
|
-
stats = conn.pool_stats()
|
|
715
|
-
utilization = stats['active_connections'] / stats['connections'] * 100
|
|
716
|
-
|
|
717
|
-
print(f"Pool Utilization: {utilization:.1f}%")
|
|
718
|
-
print(f"Active: {stats['active_connections']}, Idle: {stats['idle_connections']}")
|
|
719
|
-
|
|
720
|
-
# Alert if pool utilization is too high
|
|
721
|
-
if utilization > 90:
|
|
722
|
-
print("WARNING: High pool utilization detected!")
|
|
723
|
-
|
|
724
|
-
await asyncio.sleep(30) # Check every 30 seconds
|
|
725
|
-
```
|
|
726
|
-
|
|
727
|
-
### Optimizing for Different Database Workloads
|
|
728
|
-
|
|
729
|
-
```python
|
|
730
|
-
# OLTP (Online Transaction Processing) - Many small, fast queries
|
|
731
|
-
oltp_config = PoolConfig.high_throughput()
|
|
732
|
-
async def oltp_operations():
|
|
733
|
-
async with mssql.connect_async(conn_str, oltp_config) as conn:
|
|
734
|
-
# Fast, concurrent transactions
|
|
735
|
-
tasks = [
|
|
736
|
-
conn.execute_async("SELECT * FROM users WHERE id = $1", [user_id])
|
|
737
|
-
for user_id in range(1, 101)
|
|
738
|
-
]
|
|
739
|
-
results = await asyncio.gather(*tasks)
|
|
740
|
-
|
|
741
|
-
# OLAP (Online Analytical Processing) - Fewer, longer-running queries
|
|
742
|
-
olap_config = PoolConfig.low_resource()
|
|
743
|
-
async def olap_operations():
|
|
744
|
-
async with mssql.connect_async(conn_str, olap_config) as conn:
|
|
745
|
-
# Long-running analytical queries
|
|
746
|
-
quarterly_report = await conn.execute_async("""
|
|
747
|
-
SELECT
|
|
748
|
-
DATE_TRUNC('quarter', order_date) as quarter,
|
|
749
|
-
SUM(total_amount) as total_revenue,
|
|
750
|
-
COUNT(*) as order_count
|
|
751
|
-
FROM orders
|
|
752
|
-
WHERE order_date >= '2024-01-01'
|
|
753
|
-
GROUP BY DATE_TRUNC('quarter', order_date)
|
|
754
|
-
ORDER BY quarter
|
|
755
|
-
""")
|
|
756
|
-
return quarterly_report
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
## Troubleshooting
|
|
760
|
-
|
|
761
|
-
### Common Issues
|
|
762
|
-
|
|
763
|
-
1. **Import Error**: Make sure you've built the extension with `maturin develop`
|
|
764
|
-
2. **Connection Fails**: Check your connection string and SQL Server configuration. Note that Windows authentication is not supported - use SQL Server authentication with username and password.
|
|
765
|
-
3. **Build Errors**: Ensure you have the Rust toolchain installed
|
|
766
|
-
4. **Build Issues**: Make sure you have the Microsoft Visual C++ Build Tools on Windows
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
## Contributing
|
|
770
|
-
|
|
771
|
-
Contributions are welcome! Please open an issue or submit a pull request for any enhancements or bug fixes.
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
## License
|
|
775
|
-
|
|
776
|
-
This project is licensed under the PolyForm Noncommercial License 1.0.0. See the LICENSE file for details.
|
|
777
|
-
|
|
778
|
-
## Third-Party Attributions
|
|
779
|
-
|
|
780
|
-
This project includes and depends on third-party libraries licensed under the Apache License 2.0 and MIT License, as well as other open source licenses.
|
|
781
|
-
|
|
782
|
-
**Note:** Additional third-party libraries and their license information are listed in `licenses/NOTICE.txt`.
|
|
783
|
-
|
|
784
|
-
- [Tiberius](https://github.com/prisma/tiberius) (Apache License 2.0)
|
|
785
|
-
- [PyO3](https://github.com/PyO3/pyo3) (Apache License 2.0)
|
|
786
|
-
- [pyo3-asyncio](https://github.com/PyO3/pyo3-asyncio) (Apache License 2.0)
|
|
787
|
-
- [bb8](https://github.com/djc/bb8) (MIT or Apache License 2.0)
|
|
788
|
-
- [bb8-tiberius](https://github.com/prisma/tiberius) (Apache License 2.0)
|
|
789
|
-
- [tokio](https://github.com/tokio-rs/tokio) (MIT or Apache License 2.0)
|
|
790
|
-
- [tokio-util](https://github.com/tokio-rs/tokio) (MIT or Apache License 2.0)
|
|
791
|
-
- [futures](https://github.com/rust-lang/futures-rs) (MIT or Apache License 2.0)
|
|
792
|
-
- [serde](https://github.com/serde-rs/serde) (MIT or Apache License 2.0)
|
|
793
|
-
- [serde_json](https://github.com/serde-rs/json) (MIT or Apache License 2.0)
|
|
794
|
-
- [anyhow](https://github.com/dtolnay/anyhow) (MIT or Apache License 2.0)
|
|
795
|
-
- [chrono](https://github.com/chronotope/chrono) (MIT or Apache License 2.0)
|
|
796
|
-
- [uuid](https://github.com/uuid-rs/uuid) (MIT or Apache License 2.0)
|
|
797
|
-
- [tempfile](https://github.com/Stebalien/tempfile) (MIT or Apache License 2.0)
|
|
798
|
-
- [pytest](https://github.com/pytest-dev/pytest) (MIT License)
|
|
799
|
-
- [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) (MIT License)
|
|
800
|
-
- [black](https://github.com/psf/black) (MIT License)
|
|
801
|
-
- [ruff](https://github.com/astral-sh/ruff) (MIT License)
|
|
802
|
-
- [maturin](https://github.com/PyO3/maturin) (Apache License 2.0 or MIT License)
|
|
803
|
-
- [Python](https://www.python.org/) and [asyncio](https://docs.python.org/3/library/asyncio.html) (Python Software Foundation License)
|
|
804
|
-
|
|
805
|
-
See the `licenses/NOTICE.txt` file for full attribution and copyright information.
|
|
806
|
-
The full text of the Apache License 2.0 is provided in the `licenses/APACHE_LICENSE_2.0.txt` file.
|
|
807
|
-
The full text of the MIT License is provided in the `licenses/MIT_LICENSE.txt` file.
|
|
808
|
-
|
|
809
|
-
## Acknowledgments
|
|
810
|
-
|
|
811
|
-
- [Tiberius](https://github.com/prisma/tiberius) - Rust SQL Server driver (Apache License 2.0)
|
|
812
|
-
- [PyO3](https://github.com/PyO3/pyo3) - Python bindings for Rust (Apache License 2.0)
|
|
813
|
-
- [pyo3-asyncio](https://github.com/PyO3/pyo3-asyncio) - Async bridge for PyO3 (Apache License 2.0)
|
|
814
|
-
- [pytest](https://github.com/pytest-dev/pytest) - Python testing framework (MIT License)
|
|
815
|
-
- [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) - Async test support for pytest (MIT License)
|
|
816
|
-
- [black](https://github.com/psf/black) - Python code formatter (MIT License)
|
|
817
|
-
- [ruff](https://github.com/astral-sh/ruff) - Python linter (MIT License)
|
|
818
|
-
- [Python](https://www.python.org/) and [asyncio](https://docs.python.org/3/library/asyncio.html) - Python standard library (Python Software Foundation License)
|
|
819
|
-
- [Maturin](https://github.com/PyO3/maturin) - Build tool for Python extensions in Rust
|