fastmssql 0.2.7__cp310-cp310-win_amd64.whl → 0.3.2__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
@@ -23,18 +23,25 @@ Basic Usage (Async):
23
23
  >>>
24
24
  >>> async def main():
25
25
  ... async with Connection("Server=localhost;Database=test;Trusted_Connection=yes") as conn:
26
- ... # Simple query
27
- ... result = await conn.execute("SELECT * FROM users")
26
+ ... # SELECT queries - use query() method
27
+ ... result = await conn.query("SELECT * FROM users")
28
28
  ... async for row in result:
29
29
  ... print(f"User: {row['name']}, Age: {row['age']}")
30
30
  ...
31
- ... # Parameterized query
32
- ... result = await conn.execute(
31
+ ... # Parameterized SELECT query
32
+ ... result = await conn.query(
33
33
  ... "SELECT * FROM users WHERE age > @P1 AND city = @P2",
34
34
  ... [18, "New York"]
35
35
  ... )
36
36
  ... rows = await result.fetchall()
37
37
  ... print(f"Found {len(rows)} users")
38
+ ...
39
+ ... # INSERT/UPDATE/DELETE - use execute() method
40
+ ... affected = await conn.execute(
41
+ ... "INSERT INTO users (name, email, age) VALUES (@P1, @P2, @P3)",
42
+ ... ["John Doe", "john@example.com", 30]
43
+ ... )
44
+ ... print(f"Inserted {affected} rows")
38
45
  >>>
39
46
  >>> asyncio.run(main())
40
47
 
@@ -42,11 +49,9 @@ Basic Usage (Sync):
42
49
  >>> from fastmssql import Connection
43
50
  >>>
44
51
  >>> 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']}")
52
+ ... # For SELECT queries, you would typically use the async API
53
+ ... # Sync usage is primarily for simple operations
54
+ ... pass # Connection established and will be closed on exit
50
55
 
51
56
  Advanced Configuration:
52
57
  >>> from fastmssql import Connection, PoolConfig, SslConfig, EncryptionLevel
@@ -232,13 +237,19 @@ class Connection:
232
237
  trusted_connection=trusted_connection
233
238
  )
234
239
 
235
- async def execute(self, query, parameters=None):
240
+ async def query(self, query, parameters=None):
236
241
  """
237
- Execute a SQL query asynchronously and return a FastExecutionResult.
242
+ Execute a SQL query that returns rows (SELECT statements) asynchronously.
238
243
 
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.
244
+ This method is specifically designed for SELECT queries and other statements
245
+ that return result sets. It uses the optimized query() method internally
246
+ for maximum performance when fetching data.
247
+
248
+ Use this method for:
249
+ - SELECT statements
250
+ - Stored procedures that return result sets
251
+ - SHOW commands
252
+ - Any query that returns tabular data
242
253
 
243
254
  Parameter Binding:
244
255
  Parameters are bound using positional placeholders (@P1, @P2, etc.) in the
@@ -258,7 +269,7 @@ class Connection:
258
269
  - uuid.UUID (uniqueidentifier)
259
270
 
260
271
  Args:
261
- query (str): SQL query to execute. Use @P1, @P2, etc. for parameters.
272
+ query (str): SQL SELECT query to execute. Use @P1, @P2, etc. for parameters.
262
273
  Example: "SELECT * FROM users WHERE age > @P1 AND city = @P2"
263
274
 
264
275
  parameters (list, optional): List of parameter values in order.
@@ -280,143 +291,167 @@ class Connection:
280
291
 
281
292
  Examples:
282
293
  # Simple SELECT query
283
- >>> result = await conn.execute("SELECT * FROM users")
294
+ >>> result = await conn.query("SELECT * FROM users")
284
295
  >>> async for row in result:
285
296
  ... print(f"User ID: {row['id']}, Name: {row['name']}")
286
297
 
287
298
  # Parameterized query
288
- >>> result = await conn.execute(
299
+ >>> result = await conn.query(
289
300
  ... "SELECT * FROM orders WHERE created_date > @P1 AND amount > @P2",
290
301
  ... [datetime(2023, 1, 1), 100.0]
291
302
  ... )
292
303
  >>> rows = await result.fetchall()
293
304
  >>> print(f"Found {len(rows)} orders")
294
305
 
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]
306
+ # Complex SELECT with joins
307
+ >>> result = await conn.query(
308
+ ... \"\"\"SELECT u.name, u.email, COUNT(o.id) as order_count
309
+ ... FROM users u
310
+ ... LEFT JOIN orders o ON u.id = o.user_id
311
+ ... WHERE u.created_date > @P1
312
+ ... GROUP BY u.id, u.name, u.email
313
+ ... ORDER BY order_count DESC\"\"\",
314
+ ... [datetime(2023, 1, 1)]
299
315
  ... )
300
- >>> print(f"Inserted {result.rowcount} row(s)")
316
+ >>> async for row in result:
317
+ ... print(f"{row['name']}: {row['order_count']} orders")
301
318
 
302
- # Stored procedure call
303
- >>> result = await conn.execute(
319
+ # Stored procedure that returns data
320
+ >>> result = await conn.query(
304
321
  ... "EXEC GetUsersByDepartment @P1, @P2",
305
322
  ... ["Engineering", True] # department, active_only
306
323
  ... )
307
324
  >>> 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
325
 
321
326
  Performance Tips:
322
- - Use parameterized queries instead of string formatting for better performance
327
+ - Use this method instead of execute() for SELECT queries for better performance
323
328
  - For large result sets, iterate asynchronously rather than calling fetchall()
324
329
  - 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
330
+ - Use appropriate indexes on filtered columns
331
331
  """
332
- # The Rust implementation now returns results directly
333
- return await self._conn.execute(query, parameters)
332
+ return await self._conn.query(query, parameters)
334
333
 
335
- def execute_with_python_params(self, query, params):
334
+ async def execute(self, query, parameters=None):
336
335
  """
337
- Execute a SQL query synchronously with Python-style parameter substitution.
336
+ Execute a SQL command that doesn't return rows (INSERT/UPDATE/DELETE/DDL) asynchronously.
337
+
338
+ This method is specifically designed for SQL commands that modify data or database
339
+ structure but don't return result sets. It uses the optimized execute() method
340
+ internally for maximum performance when performing data modifications.
338
341
 
339
- This method provides a synchronous interface for executing SQL queries with
340
- parameter substitution using Python's DB-API 2.0 style placeholders (?).
341
- It's designed for compatibility with existing Python database code and
342
- simple synchronous operations.
342
+ Use this method for:
343
+ - INSERT statements
344
+ - UPDATE statements
345
+ - DELETE statements
346
+ - DDL commands (CREATE, ALTER, DROP)
347
+ - Stored procedures that don't return result sets
348
+ - MERGE statements
343
349
 
344
350
  Parameter Binding:
345
- Uses question mark (?) placeholders in the query string, with parameters
346
- provided as a list in the same order as the placeholders appear.
351
+ Parameters are bound using positional placeholders (@P1, @P2, etc.) in the
352
+ query string. The parameter values are provided as a list in the same order.
347
353
 
348
- Note:
349
- This is a synchronous method that blocks until the query completes.
350
- For better performance in async applications, prefer the async execute() method.
354
+ Supported Parameter Types:
355
+ - None (NULL)
356
+ - bool
357
+ - int (32-bit and 64-bit)
358
+ - float (32-bit and 64-bit)
359
+ - str (varchar, nvarchar, text)
360
+ - bytes (varbinary, image)
361
+ - datetime.datetime (datetime, datetime2)
362
+ - datetime.date (date)
363
+ - datetime.time (time)
364
+ - decimal.Decimal (decimal, money)
365
+ - uuid.UUID (uniqueidentifier)
351
366
 
352
367
  Args:
353
- query (str): SQL query with ? placeholders for parameters.
354
- Example: "SELECT * FROM users WHERE age > ? AND city = ?"
368
+ query (str): SQL command to execute. Use @P1, @P2, etc. for parameters.
369
+ Example: "INSERT INTO users (name, email, age) VALUES (@P1, @P2, @P3)"
355
370
 
356
- params (list): List of parameter values in order of appearance.
371
+ parameters (list, optional): List of parameter values in order.
357
372
  Values are automatically converted to appropriate SQL types.
358
- Example: [18, "New York"]
373
+ Example: ["John Doe", "john@example.com", 30]
359
374
 
360
375
  Returns:
361
- list: A list of dictionaries representing the result rows.
362
- Each dictionary maps column names to values.
363
- For non-SELECT queries, returns an empty list.
376
+ int: Number of rows affected by the command.
377
+ - For INSERT: Number of rows inserted
378
+ - For UPDATE: Number of rows updated
379
+ - For DELETE: Number of rows deleted
380
+ - For DDL: Usually 0 (structure changes don't affect rows)
364
381
 
365
382
  Raises:
366
- SqlError: If the SQL query contains syntax errors or constraint violations.
367
- ConnectionError: If the database connection is not available.
383
+ SqlError: If the SQL command contains syntax errors or constraint violations.
384
+ ConnectionError: If the database connection is lost during execution.
385
+ TimeoutError: If the command execution exceeds configured timeouts.
368
386
  ParameterError: If parameter types cannot be converted or are invalid.
369
387
 
370
388
  Examples:
371
- # Simple SELECT query
372
- >>> rows = conn.execute_with_python_params(
373
- ... "SELECT id, name, email FROM users WHERE active = ?",
374
- ... [True]
389
+ # INSERT with parameters
390
+ >>> affected = await conn.execute(
391
+ ... "INSERT INTO users (name, email, age) VALUES (@P1, @P2, @P3)",
392
+ ... ["John Doe", "john@example.com", 30]
375
393
  ... )
376
- >>> for row in rows:
377
- ... print(f"User: {row['name']} ({row['email']})")
394
+ >>> print(f"Inserted {affected} row(s)")
378
395
 
379
- # INSERT operation
380
- >>> conn.execute_with_python_params(
381
- ... "INSERT INTO logs (message, level, timestamp) VALUES (?, ?, ?)",
382
- ... ["Application started", "INFO", datetime.now()]
396
+ # UPDATE with conditions
397
+ >>> affected = await conn.execute(
398
+ ... "UPDATE users SET age = @P1, updated_date = @P2 WHERE id = @P3",
399
+ ... [31, datetime.now(), 123]
383
400
  ... )
401
+ >>> print(f"Updated {affected} user(s)")
384
402
 
385
- # UPDATE with multiple parameters
386
- >>> conn.execute_with_python_params(
387
- ... "UPDATE users SET last_login = ?, login_count = login_count + 1 WHERE id = ?",
388
- ... [datetime.now(), 12345]
403
+ # DELETE with parameters
404
+ >>> affected = await conn.execute(
405
+ ... "DELETE FROM users WHERE age < @P1 AND last_login < @P2",
406
+ ... [18, datetime(2020, 1, 1)]
389
407
  ... )
408
+ >>> print(f"Deleted {affected} inactive users")
390
409
 
391
- # Complex query with various data types
392
- >>> from decimal import Decimal
393
- >>> rows = conn.execute_with_python_params(
394
- ... \"\"\"SELECT p.name, p.price, c.name as category
395
- ... FROM products p
396
- ... JOIN categories c ON p.category_id = c.id
397
- ... WHERE p.price BETWEEN ? AND ? AND p.discontinued = ?\"\"\",
398
- ... [Decimal('10.00'), Decimal('100.00'), False]
410
+ # DDL commands
411
+ >>> affected = await conn.execute(
412
+ ... \"\"\"CREATE TABLE temp_data (
413
+ ... id INT IDENTITY(1,1) PRIMARY KEY,
414
+ ... name NVARCHAR(100) NOT NULL,
415
+ ... created_date DATETIME2 DEFAULT GETDATE()
416
+ ... )\"\"\"
399
417
  ... )
400
-
401
- Migration from DB-API 2.0:
402
- This method is designed to be compatible with Python's DB-API 2.0
403
- specification, making it easier to migrate from libraries like pyodbc
404
- or pymssql with minimal code changes.
418
+ >>> print(f"Table created (affected rows: {affected})")
405
419
 
406
- # Old pyodbc code:
407
- # cursor.execute("SELECT * FROM users WHERE id = ?", [user_id])
408
- # rows = cursor.fetchall()
420
+ # Stored procedure that modifies data
421
+ >>> affected = await conn.execute(
422
+ ... "EXEC UpdateUserPreferences @P1, @P2",
423
+ ... [user_id, json.dumps(preferences)]
424
+ ... )
425
+ >>> print(f"Updated preferences for {affected} user(s)")
409
426
 
410
- # New fastmssql code:
411
- # rows = conn.execute_with_python_params("SELECT * FROM users WHERE id = ?", [user_id])
412
-
413
- Performance Considerations:
414
- - This method is synchronous and will block the calling thread
415
- - For high-throughput applications, use the async execute() method instead
416
- - Connection pooling is still utilized for efficient resource management
417
- - Results are loaded entirely into memory, so be cautious with large result sets
427
+ # Batch operations
428
+ >>> users_to_insert = [
429
+ ... ["Alice Johnson", "alice@example.com", 28],
430
+ ... ["Bob Smith", "bob@example.com", 32],
431
+ ... ["Carol Davis", "carol@example.com", 25]
432
+ ... ]
433
+ >>> total_affected = 0
434
+ >>> for user_data in users_to_insert:
435
+ ... affected = await conn.execute(
436
+ ... "INSERT INTO users (name, email, age) VALUES (@P1, @P2, @P3)",
437
+ ... user_data
438
+ ... )
439
+ ... total_affected += affected
440
+ >>> print(f"Inserted {total_affected} users total")
441
+
442
+ Performance Tips:
443
+ - Use this method instead of query() for data modification commands
444
+ - For bulk operations, consider using batch processing or table-valued parameters
445
+ - Use transactions for multiple related operations
446
+ - Monitor the returned affected row count for validation
447
+
448
+ Security Notes:
449
+ - Always use parameterized queries to prevent SQL injection attacks
450
+ - Validate affected row counts match expectations
451
+ - Consider using transactions for data consistency
418
452
  """
419
- return self._conn.execute_with_python_params(query, params)
453
+ return await self._conn.execute(query, parameters)
454
+
420
455
 
421
456
  async def is_connected(self):
422
457
  """
Binary file