fastmssql 0.3.0__cp310-cp310-win_amd64.whl → 0.3.3__cp310-cp310-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 CHANGED
@@ -331,6 +331,267 @@ class Connection:
331
331
  """
332
332
  return await self._conn.query(query, parameters)
333
333
 
334
+ async def execute_batch(self, commands):
335
+ """
336
+ Execute multiple SQL commands in a single batch operation for optimal performance.
337
+
338
+ This method executes multiple INSERT, UPDATE, DELETE, or DDL commands in sequence
339
+ on a single connection, minimizing network round-trips and connection overhead.
340
+
341
+ Use this method for:
342
+ - Multiple INSERT/UPDATE/DELETE operations
343
+ - Batch DDL operations (CREATE TABLE, CREATE INDEX, etc.)
344
+ - Mixed command operations that don't need to return result sets
345
+ - Any sequence of commands that modify data
346
+
347
+ Performance Benefits:
348
+ - Single connection usage reduces pool contention
349
+ - Reduced network round-trips compared to individual execute() calls
350
+ - Parameter pre-processing optimization
351
+ - Efficient memory usage for large batch operations
352
+
353
+ Args:
354
+ commands (list): List of tuples, each containing (command, parameters).
355
+ Each tuple should be in the format: (sql_command, parameter_list)
356
+ Example: [
357
+ ("INSERT INTO users (name, age) VALUES (@P1, @P2)", ["Alice", 25]),
358
+ ("UPDATE products SET price = @P1 WHERE id = @P2", [99.99, 123]),
359
+ ("DELETE FROM logs WHERE created_date < @P1", [datetime(2023, 1, 1)]),
360
+ ]
361
+
362
+ Returns:
363
+ list: List of affected row counts for each command, in the same order as input.
364
+ Each element is an integer representing the number of rows affected by
365
+ the corresponding command.
366
+
367
+ Raises:
368
+ SqlError: If any SQL command contains syntax errors or constraint violations.
369
+ ConnectionError: If the database connection is lost during execution.
370
+ TimeoutError: If the batch execution exceeds configured timeouts.
371
+ ParameterError: If parameter types cannot be converted or are invalid.
372
+ ValueError: If the commands list format is incorrect.
373
+
374
+ Examples:
375
+ # Basic batch execution
376
+ >>> commands = [
377
+ ... ("INSERT INTO users (name, email) VALUES (@P1, @P2)", ["John", "john@example.com"]),
378
+ ... ("INSERT INTO users (name, email) VALUES (@P1, @P2)", ["Jane", "jane@example.com"]),
379
+ ... ("UPDATE settings SET value = @P1 WHERE key = @P2", ["enabled", "notifications"])
380
+ ... ]
381
+ >>> results = await conn.execute_batch(commands)
382
+ >>> print(f"Affected rows: {results}") # [1, 1, 1]
383
+
384
+ # Mixed operations batch
385
+ >>> operations = [
386
+ ... ("CREATE TABLE temp_data (id INT, value NVARCHAR(50))", None),
387
+ ... ("INSERT INTO temp_data VALUES (@P1, @P2)", [1, "test"]),
388
+ ... ("UPDATE temp_data SET value = @P1 WHERE id = @P2", ["updated", 1]),
389
+ ... ("DROP TABLE temp_data", None)
390
+ ... ]
391
+ >>> results = await conn.execute_batch(operations)
392
+
393
+ # Bulk data modification
394
+ >>> user_updates = [
395
+ ... ("UPDATE users SET last_login = @P1 WHERE id = @P2", [datetime.now(), user_id])
396
+ ... for user_id in [1, 2, 3, 4, 5]
397
+ ... ]
398
+ >>> results = await conn.execute_batch(user_updates)
399
+ >>> total_updated = sum(results)
400
+ """
401
+ return await self._conn.execute_batch(commands)
402
+
403
+ async def query_batch(self, queries):
404
+ """
405
+ Execute multiple SQL queries in a single batch operation for optimal performance.
406
+
407
+ This method executes multiple SELECT queries in sequence on a single connection,
408
+ minimizing network round-trips and connection overhead while returning all result sets.
409
+
410
+ Use this method for:
411
+ - Multiple related SELECT queries
412
+ - Data analysis operations requiring multiple result sets
413
+ - Report generation with multiple data sources
414
+ - Any sequence of queries that return tabular data
415
+
416
+ Performance Benefits:
417
+ - Single connection usage reduces pool contention
418
+ - Reduced network round-trips compared to individual query() calls
419
+ - Parameter pre-processing optimization
420
+ - Efficient memory usage for multiple result sets
421
+
422
+ Args:
423
+ queries (list): List of tuples, each containing (query, parameters).
424
+ Each tuple should be in the format: (sql_query, parameter_list)
425
+ Example: [
426
+ ("SELECT * FROM users WHERE age > @P1", [18]),
427
+ ("SELECT COUNT(*) as total FROM products", None),
428
+ ("SELECT * FROM orders WHERE created_date > @P1", [datetime(2023, 1, 1)]),
429
+ ]
430
+
431
+ Returns:
432
+ list: List of FastExecutionResult objects for each query, in the same order as input.
433
+ Each FastExecutionResult provides the same interface as individual query() results:
434
+ - Async iteration over rows
435
+ - fetchone(), fetchmany(), fetchall() methods
436
+ - Row count and column metadata
437
+
438
+ Raises:
439
+ SqlError: If any SQL query contains syntax errors or constraint violations.
440
+ ConnectionError: If the database connection is lost during execution.
441
+ TimeoutError: If the query execution exceeds configured timeouts.
442
+ ParameterError: If parameter types cannot be converted or are invalid.
443
+ ValueError: If the queries list format is incorrect.
444
+
445
+ Examples:
446
+ # Basic batch queries
447
+ >>> queries = [
448
+ ... ("SELECT COUNT(*) as user_count FROM users", None),
449
+ ... ("SELECT COUNT(*) as product_count FROM products", None),
450
+ ... ("SELECT * FROM users WHERE created_date > @P1", [datetime(2023, 1, 1)])
451
+ ... ]
452
+ >>> results = await conn.query_batch(queries)
453
+ >>>
454
+ >>> # Process each result
455
+ >>> user_count = (await results[0].fetchone())['user_count']
456
+ >>> product_count = (await results[1].fetchone())['product_count']
457
+ >>> recent_users = await results[2].fetchall()
458
+
459
+ # Analytics batch
460
+ >>> analytics_queries = [
461
+ ... ("SELECT DATE(created_date) as date, COUNT(*) as registrations FROM users GROUP BY DATE(created_date)", None),
462
+ ... ("SELECT category, AVG(price) as avg_price FROM products GROUP BY category", None),
463
+ ... ("SELECT status, COUNT(*) as order_count FROM orders GROUP BY status", None)
464
+ ... ]
465
+ >>> results = await conn.query_batch(analytics_queries)
466
+ >>>
467
+ >>> # Process analytics data
468
+ >>> for i, result in enumerate(results):
469
+ ... print(f"Query {i+1} results:")
470
+ ... async for row in result:
471
+ ... print(f" {dict(row)}")
472
+
473
+ # Related data batch
474
+ >>> user_id = 123
475
+ >>> related_queries = [
476
+ ... ("SELECT * FROM users WHERE id = @P1", [user_id]),
477
+ ... ("SELECT * FROM orders WHERE user_id = @P1 ORDER BY created_date DESC", [user_id]),
478
+ ... ("SELECT * FROM user_preferences WHERE user_id = @P1", [user_id])
479
+ ... ]
480
+ >>> results = await conn.query_batch(related_queries)
481
+ >>> user_data = await results[0].fetchone()
482
+ >>> user_orders = await results[1].fetchall()
483
+ >>> user_prefs = await results[2].fetchall()
484
+ """
485
+ return await self._conn.query_batch(queries)
486
+
487
+ async def bulk_insert(self, table_name, columns, data_rows):
488
+ """
489
+ Perform high-performance bulk insert operation for large datasets.
490
+
491
+ This method is optimized for inserting many rows into a single table with
492
+ maximum performance. It processes data in batches to optimize memory usage
493
+ and network efficiency while maintaining consistency.
494
+
495
+ Use this method for:
496
+ - Large data imports (CSV, JSON, API data)
497
+ - ETL operations and data migration
498
+ - Batch data processing pipelines
499
+ - Any scenario requiring insertion of many rows
500
+
501
+ Performance Benefits:
502
+ - Optimized batch processing with configurable batch sizes
503
+ - Minimal memory overhead through streaming processing
504
+ - Single connection usage reduces pool contention
505
+ - Pre-compiled parameter handling for maximum speed
506
+ - Automatic transaction batching for consistency
507
+
508
+ Args:
509
+ table_name (str): Name of the target table for insertion.
510
+ Can be schema-qualified (e.g., "dbo.my_table" or "my_schema.my_table").
511
+
512
+ columns (list): List of column names in the order they appear in data_rows.
513
+ Example: ["name", "email", "age", "created_date"]
514
+
515
+ data_rows (list): List of data rows, where each row is a list of values
516
+ corresponding to the columns. All rows must have the same number of
517
+ values as there are columns.
518
+ Example: [
519
+ ["Alice", "alice@example.com", 25, datetime(2023, 1, 1)],
520
+ ["Bob", "bob@example.com", 30, datetime(2023, 1, 2)],
521
+ ["Charlie", "charlie@example.com", 35, datetime(2023, 1, 3)]
522
+ ]
523
+
524
+ Returns:
525
+ int: Total number of rows successfully inserted.
526
+
527
+ Raises:
528
+ SqlError: If table doesn't exist, column names are invalid, or constraint violations occur.
529
+ ConnectionError: If the database connection is lost during execution.
530
+ TimeoutError: If the bulk insert exceeds configured timeouts.
531
+ ParameterError: If data types cannot be converted to appropriate SQL types.
532
+ ValueError: If columns and data_rows have mismatched sizes or invalid format.
533
+
534
+ Examples:
535
+ # Basic bulk insert
536
+ >>> columns = ["name", "email", "age"]
537
+ >>> data = [
538
+ ... ["Alice", "alice@example.com", 25],
539
+ ... ["Bob", "bob@example.com", 30],
540
+ ... ["Charlie", "charlie@example.com", 35]
541
+ ... ]
542
+ >>> rows_inserted = await conn.bulk_insert("users", columns, data)
543
+ >>> print(f"Inserted {rows_inserted} rows")
544
+
545
+ # Large dataset import
546
+ >>> import csv
547
+ >>> columns = ["product_name", "category", "price", "in_stock"]
548
+ >>> data_rows = []
549
+ >>>
550
+ >>> with open('products.csv', 'r') as file:
551
+ ... reader = csv.reader(file)
552
+ ... next(reader) # Skip header
553
+ ... for row in reader:
554
+ ... data_rows.append([row[0], row[1], float(row[2]), bool(int(row[3]))])
555
+ >>>
556
+ >>> total_inserted = await conn.bulk_insert("products", columns, data_rows)
557
+ >>> print(f"Imported {total_inserted} products from CSV")
558
+
559
+ # Generated data bulk insert
560
+ >>> from datetime import datetime, timedelta
561
+ >>> import random
562
+ >>>
563
+ >>> columns = ["user_id", "activity", "timestamp", "value"]
564
+ >>> activities = ["login", "logout", "view_page", "click_button", "purchase"]
565
+ >>>
566
+ >>> # Generate 10,000 activity records
567
+ >>> data_rows = []
568
+ >>> for i in range(10000):
569
+ ... user_id = random.randint(1, 1000)
570
+ ... activity = random.choice(activities)
571
+ ... timestamp = datetime.now() - timedelta(days=random.randint(0, 30))
572
+ ... value = random.randint(1, 100)
573
+ ... data_rows.append([user_id, activity, timestamp, value])
574
+ >>>
575
+ >>> rows_inserted = await conn.bulk_insert("user_activities", columns, data_rows)
576
+ >>> print(f"Inserted {rows_inserted} activity records")
577
+
578
+ # Data transformation during bulk insert
579
+ >>> raw_data = fetch_api_data() # Some external data source
580
+ >>> columns = ["name", "email", "normalized_phone", "registration_date"]
581
+ >>>
582
+ >>> processed_data = []
583
+ >>> for record in raw_data:
584
+ ... processed_data.append([
585
+ ... record['full_name'].strip().title(),
586
+ ... record['email'].lower(),
587
+ ... normalize_phone(record['phone']),
588
+ ... datetime.fromisoformat(record['reg_date'])
589
+ ... ])
590
+ >>>
591
+ >>> result = await conn.bulk_insert("customers", columns, processed_data)
592
+ """
593
+ return await self._conn.bulk_insert(table_name, columns, data_rows)
594
+
334
595
  async def execute(self, query, parameters=None):
335
596
  """
336
597
  Execute a SQL command that doesn't return rows (INSERT/UPDATE/DELETE/DDL) asynchronously.
Binary file
@@ -0,0 +1,393 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastmssql
3
+ Version: 0.3.3
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
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 async Python library for Microsoft SQL Server built on Rust for heavy workloads and low latency.
23
+ Author-email: Riveranda <riverb514@gmail.com>
24
+ License: GPL-3.0-or-later OR Commercial
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
+ # FastMSSQL ⚡
30
+
31
+ FastMSSQL is an async Python library for Microsoft SQL Server (MSSQL), built in Rust.
32
+ Unlike pyodbc or pymssql, it uses a native SQL Server client—no ODBC required—simplifying installation on Windows, macOS, and Linux.
33
+ Great for data ingestion, bulk inserts, and large-scale query workloads.
34
+
35
+ [![PyPI](https://img.shields.io/pypi/v/fastmssql)](https://pypi.org/project/fastmssql/)
36
+ [![Python Versions](https://img.shields.io/pypi/pyversions/fastmssql)](https://pypi.org/project/fastmssql/)
37
+ [![Rust Backend](https://img.shields.io/badge/backend-rust-orange)](https://github.com/Rivendael/pymssql-rs)
38
+ [![Async](https://img.shields.io/badge/async-tokio-blue)](https://github.com/Rivendael/pymssql-rs)
39
+ [![License](https://img.shields.io/badge/license-GPL--3.0%20or%20Commercial-green)](LICENSE)
40
+
41
+
42
+
43
+ ## Contents
44
+
45
+ - Features
46
+ - Installation
47
+ - Quick start
48
+ - Usage
49
+ - Connection options
50
+ - Working with data (query vs execute)
51
+ - Connection pooling
52
+ - SSL/TLS
53
+ - Performance tips
54
+ - Examples & benchmarks
55
+ - Troubleshooting
56
+ - Contributing
57
+ - License
58
+
59
+
60
+ ## Features
61
+
62
+ - High performance: optimized for very high RPS and low overhead
63
+ - Rust core: memory‑safe and reliable, tuned Tokio runtime
64
+ - No ODBC: native SQL Server client, no external drivers needed
65
+ - Connection pooling: bb8‑based, smart defaults (default max_size=10, min_idle=2)
66
+ - Async first: clean async/await API with `async with` context managers
67
+ - Strong typing: fast conversions for common SQL Server types
68
+ - Thread‑safe: safe to use in concurrent apps
69
+ - Cross‑platform: Windows, macOS, Linux
70
+ - Batch operations: high-performance bulk inserts and batch query execution
71
+
72
+
73
+ ## Key API methods
74
+
75
+ Core methods for individual operations:
76
+
77
+ - `query()` — SELECT statements that return rows
78
+ - `execute()` — INSERT/UPDATE/DELETE/DDL that return affected row count
79
+
80
+ ```python
81
+ # Use query() for SELECT statements
82
+ result = await conn.query("SELECT * FROM users WHERE age > @P1", [25])
83
+ rows = result.rows()
84
+
85
+ # Use execute() for data modification
86
+ affected = await conn.execute("INSERT INTO users (name) VALUES (@P1)", ["John"])
87
+ ```
88
+
89
+
90
+ ## Installation
91
+
92
+ ### From PyPI (recommended)
93
+
94
+ ```bash
95
+ pip install fastmssql
96
+ ```
97
+
98
+ ### Prerequisites
99
+
100
+ - Python 3.8 to 3.13
101
+ - Microsoft SQL Server (any recent version)
102
+
103
+ ### From source (development)
104
+
105
+ ```bash
106
+ git clone <your-repo-url>
107
+ cd pymssql-rs
108
+ ./setup.sh
109
+ ```
110
+
111
+
112
+ ## Quick start
113
+
114
+ ### Basic async usage
115
+
116
+ ```python
117
+ import asyncio
118
+ from fastmssql import Connection
119
+
120
+ async def main():
121
+ conn_str = "Server=localhost;Database=master;User Id=myuser;Password=mypass"
122
+ async with Connection(conn_str) as conn:
123
+ # SELECT: use query() -> rows()
124
+ result = await conn.query("SELECT @@VERSION as version")
125
+ for row in result.rows():
126
+ print(row['version'])
127
+
128
+ # Pool statistics (tuple: connected, connections, idle, max_size, min_idle)
129
+ connected, connections, idle, max_size, min_idle = await conn.pool_stats()
130
+ print(f"Pool: connected={connected}, size={connections}/{max_size}, idle={idle}, min_idle={min_idle}")
131
+
132
+ asyncio.run(main())
133
+ ```
134
+
135
+
136
+ ## Usage
137
+
138
+ ### Connection options
139
+
140
+ You can connect either with a connection string or individual parameters.
141
+
142
+ 1) Connection string
143
+
144
+ ```python
145
+ import asyncio
146
+ from fastmssql import Connection
147
+
148
+ async def main():
149
+ conn_str = "Server=localhost;Database=master;User Id=myuser;Password=mypass"
150
+ async with Connection(connection_string=conn_str) as conn:
151
+ rows = (await conn.query("SELECT DB_NAME() as db")).rows()
152
+ print(rows[0]['db'])
153
+
154
+ asyncio.run(main())
155
+ ```
156
+
157
+ 2) Individual parameters
158
+
159
+ ```python
160
+ import asyncio
161
+ from fastmssql import Connection
162
+
163
+ async def main():
164
+ async with Connection(
165
+ server="localhost",
166
+ database="master",
167
+ username="myuser",
168
+ password="mypassword"
169
+ ) as conn:
170
+ rows = (await conn.query("SELECT SUSER_SID() as sid")).rows()
171
+ print(rows[0]['sid'])
172
+
173
+ asyncio.run(main())
174
+ ```
175
+
176
+ Note: Windows authentication (Trusted Connection) is currently not supported. Use SQL authentication (username/password).
177
+
178
+
179
+ ### Working with data
180
+
181
+ ```python
182
+ import asyncio
183
+ from fastmssql import Connection
184
+
185
+ async def main():
186
+ async with Connection("Server=.;Database=MyDB;User Id=sa;Password=StrongPwd;") as conn:
187
+ # SELECT (returns rows)
188
+ users = (await conn.query(
189
+ "SELECT id, name, email FROM users WHERE active = 1"
190
+ )).rows()
191
+ for u in users:
192
+ print(f"User {u['id']}: {u['name']} ({u['email']})")
193
+
194
+ # INSERT / UPDATE / DELETE (returns affected row count)
195
+ inserted = await conn.execute(
196
+ "INSERT INTO users (name, email) VALUES (@P1, @P2)",
197
+ ["Jane", "jane@example.com"],
198
+ )
199
+ print(f"Inserted {inserted} row(s)")
200
+
201
+ updated = await conn.execute(
202
+ "UPDATE users SET last_login = GETDATE() WHERE id = @P1",
203
+ [123],
204
+ )
205
+ print(f"Updated {updated} row(s)")
206
+
207
+ asyncio.run(main())
208
+ ```
209
+
210
+ Parameters use positional placeholders: `@P1`, `@P2`, ... Provide values as a list in the same order.
211
+
212
+
213
+ ### Batch operations
214
+
215
+ For high-throughput scenarios, use batch methods to reduce network round-trips:
216
+
217
+ ```python
218
+ import asyncio
219
+ from fastmssql import Connection
220
+
221
+ async def main():
222
+ async with Connection("Server=.;Database=MyDB;User Id=sa;Password=StrongPwd;") as conn:
223
+ # Bulk insert for fast data loading
224
+ columns = ["name", "email", "age"]
225
+ data_rows = [
226
+ ["Alice Johnson", "alice@example.com", 28],
227
+ ["Bob Smith", "bob@example.com", 32],
228
+ ["Carol Davis", "carol@example.com", 25]
229
+ ]
230
+
231
+ rows_inserted = await conn.bulk_insert("users", columns, data_rows)
232
+ print(f"Bulk inserted {rows_inserted} rows")
233
+
234
+ # Batch queries for multiple SELECT operations
235
+ queries = [
236
+ ("SELECT COUNT(*) as total FROM users WHERE age > @P1", [25]),
237
+ ("SELECT AVG(age) as avg_age FROM users", None),
238
+ ("SELECT name FROM users WHERE email LIKE @P1", ["%@example.com"])
239
+ ]
240
+
241
+ results = await conn.query_batch(queries)
242
+ print(f"Total users over 25: {results[0].rows()[0]['total']}")
243
+ print(f"Average age: {results[1].rows()[0]['avg_age']:.1f}")
244
+ print(f"Example.com users: {len(results[2].rows())}")
245
+
246
+ # Batch commands for multiple operations
247
+ commands = [
248
+ ("UPDATE users SET last_login = GETDATE() WHERE name = @P1", ["Alice Johnson"]),
249
+ ("INSERT INTO user_logs (action, user_name) VALUES (@P1, @P2)", ["login", "Alice Johnson"])
250
+ ]
251
+
252
+ affected_counts = await conn.execute_batch(commands)
253
+ print(f"Updated {affected_counts[0]} users, inserted {affected_counts[1]} logs")
254
+
255
+ asyncio.run(main())
256
+ ```
257
+
258
+
259
+ ### Connection strings
260
+
261
+ ```python
262
+ # SQL Server Authentication
263
+ conn_str = "Server=localhost;Database=MyDB;User Id=sa;Password=MyPassword"
264
+
265
+ # With specific port
266
+ conn_str = "Server=localhost,1433;Database=MyDB;User Id=myuser;Password=mypass"
267
+
268
+ # Azure SQL Database (encryption recommended)
269
+ conn_str = "Server=tcp:myserver.database.windows.net,1433;Database=MyDB;User Id=myuser;Password=mypass;Encrypt=true"
270
+ ```
271
+
272
+
273
+ ### Connection pooling
274
+
275
+ Tune the pool to fit your workload. Constructor signature:
276
+
277
+ ```python
278
+ from fastmssql import PoolConfig
279
+
280
+ # PoolConfig(max_size=10, min_idle=2, max_lifetime_secs=None, idle_timeout_secs=None, connection_timeout_secs=30)
281
+ config = PoolConfig(
282
+ max_size=20, # max connections in pool
283
+ min_idle=5, # keep at least this many idle
284
+ max_lifetime_secs=3600, # recycle connections after 1h
285
+ idle_timeout_secs=600, # close idle connections after 10m
286
+ connection_timeout_secs=30
287
+ )
288
+ ```
289
+
290
+ Presets:
291
+
292
+ ```python
293
+ high = PoolConfig.high_throughput() # ~ max_size=50, min_idle=15
294
+ low = PoolConfig.low_resource() # ~ max_size=3, min_idle=1
295
+ dev = PoolConfig.development() # ~ max_size=5, min_idle=1
296
+ maxp = PoolConfig.maximum_performance() # ~ max_size=100, min_idle=30
297
+ ultra = PoolConfig.ultra_high_concurrency() # ~ max_size=200, min_idle=50
298
+ ```
299
+
300
+ Apply to a connection:
301
+
302
+ ```python
303
+ async with Connection(conn_str, pool_config=high) as conn:
304
+ rows = (await conn.query("SELECT 1 AS ok")).rows()
305
+ ```
306
+
307
+ Default pool (if omitted): `max_size=10`, `min_idle=2`.
308
+
309
+
310
+ ### SSL/TLS
311
+
312
+ ```python
313
+ from fastmssql import SslConfig, EncryptionLevel, Connection
314
+
315
+ ssl = SslConfig(
316
+ encryption_level=EncryptionLevel.REQUIRED, # or "Required"
317
+ trust_server_certificate=False,
318
+ )
319
+
320
+ async with Connection(conn_str, ssl_config=ssl) as conn:
321
+ ...
322
+ ```
323
+
324
+ Helpers:
325
+
326
+ - `SslConfig.development()` – encrypt, trust all (dev only)
327
+ - `SslConfig.with_ca_certificate(path)` – use custom CA
328
+ - `SslConfig.login_only()` / `SslConfig.disabled()` – legacy modes
329
+
330
+
331
+ ## Performance tips
332
+
333
+ For maximum throughput in highly concurrent scenarios, use multiple `Connection` instances (each with its own pool) and batch your work:
334
+
335
+ ```python
336
+ import asyncio
337
+ from fastmssql import Connection, PoolConfig
338
+
339
+ async def worker(conn_str, cfg):
340
+ async with Connection(conn_str, pool_config=cfg) as conn:
341
+ for _ in range(1000):
342
+ _ = (await conn.query("SELECT 1 as v")).rows()
343
+
344
+ async def main():
345
+ conn_str = "Server=.;Database=master;User Id=sa;Password=StrongPwd;"
346
+ cfg = PoolConfig.high_throughput()
347
+ await asyncio.gather(*[asyncio.create_task(worker(conn_str, cfg)) for _ in range(32)])
348
+
349
+ asyncio.run(main())
350
+ ```
351
+
352
+
353
+ ## Examples & benchmarks
354
+
355
+ - Examples: `examples/comprehensive_example.py`
356
+ - Benchmarks: `benchmarks/` (MIT licensed)
357
+
358
+
359
+ ## Troubleshooting
360
+
361
+ - Import/build: ensure Rust toolchain and `maturin` are installed if building from source
362
+ - Connection: verify connection string; Windows auth not supported
363
+ - Timeouts: increase pool size or tune `connection_timeout_secs`
364
+ - Parameters: use `@P1, @P2, ...` and pass a list of values
365
+
366
+
367
+ ## Contributing
368
+
369
+ Contributions are welcome. Please open an issue or PR.
370
+
371
+
372
+ ## License
373
+
374
+ FastMSSQL is dual‑licensed:
375
+
376
+ - GPL‑3.0 (for open source projects)
377
+ - Commercial (for proprietary use). Contact: riverb514@gmail.com
378
+
379
+ See the [LICENSE](LICENSE) file for details.
380
+
381
+ ### Examples and Benchmarks
382
+
383
+ - `examples/` and `benchmarks/` are under the MIT License. See files in `licenses/`.
384
+
385
+
386
+ ## Third‑party attributions
387
+
388
+ Built on excellent open source projects: Tiberius, PyO3, pyo3‑asyncio, bb8, tokio, serde, pytest, maturin, and more. See `licenses/NOTICE.txt` for the full list. The full texts of Apache‑2.0 and MIT are in `licenses/`.
389
+
390
+
391
+ ## Acknowledgments
392
+
393
+ Thanks to the maintainers of Tiberius, PyO3, pyo3‑asyncio, Tokio, pytest, maturin, and the broader open source community.
@@ -0,0 +1,6 @@
1
+ fastmssql-0.3.3.dist-info/METADATA,sha256=RfM65FAcE44vci5KSf5sJOhG359bNxU2rJq23VdsgU0,12350
2
+ fastmssql-0.3.3.dist-info/WHEEL,sha256=gTksDcUm4vEEt-WSZ1CnYcSbBNyUtm2mp9lzlf3Yd0w,96
3
+ fastmssql-0.3.3.dist-info/licenses/LICENSE,sha256=oCvGJWOz1z4-4sMRHL32UnC0BnRFXmJZYQ0QAsv0Cok,34533
4
+ fastmssql/__init__.py,sha256=xTvVL92qRiQd3BSe3k7sZ7ud9J9pmNxonpixx48G2Q0,44500
5
+ fastmssql/fastmssql.cp310-win_amd64.pyd,sha256=hmVKdNzn7osmCIPSYeOFPrkJxkRvSyB36UyqTzZ2IsE,2251264
6
+ fastmssql-0.3.3.dist-info/RECORD,,