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/fastmssql.py DELETED
@@ -1,706 +0,0 @@
1
- """
2
- High-level Python API for mssql-python-rust
3
-
4
- This module provides convenient Python functions that wrap the Rust core functionality.
5
- Supports asynchronous operations only.
6
- """
7
-
8
- from typing import List, Dict, Any, Optional, Union, Iterable
9
-
10
- try:
11
- # Try to import the compiled Rust module from the package
12
- from . import fastmssql_core as _core
13
- except ImportError:
14
- # Fallback for development - try absolute import
15
- try:
16
- import fastmssql_core as _core
17
- except ImportError as e:
18
- import sys
19
- print(f"ERROR: fastmssql_core module not found: {e}")
20
- print("Solution: Build the extension with 'maturin develop' or 'maturin develop --release'")
21
- print("Make sure you're in the project root directory and have Rust/Maturin installed.")
22
- sys.exit(1)
23
-
24
- class Row:
25
- """Python wrapper around Row for better type hints and documentation."""
26
-
27
- def __init__(self, py_row: Any) -> None:
28
- """Initialize with a Row instance.
29
-
30
- Args:
31
- py_row: The underlying Rust Row instance
32
- """
33
- self._row = py_row
34
-
35
- def get(self, column: Union[str, int]) -> Any:
36
- """Get value by column name or index.
37
-
38
- Args:
39
- column: Column name (str) or index (int)
40
-
41
- Returns:
42
- The column value
43
- """
44
- return self._row.get(column)
45
-
46
- def to_dict(self) -> Dict[str, Any]:
47
- """Convert row to dictionary.
48
-
49
- Returns:
50
- Dictionary mapping column names to values
51
- """
52
- return self._row.to_dict()
53
-
54
- def to_tuple(self) -> tuple:
55
- """Convert row to tuple.
56
-
57
- Returns:
58
- Tuple of column values in order
59
- """
60
- return self._row.to_tuple()
61
-
62
- def __getitem__(self, key: Union[str, int]) -> Any:
63
- """Get value by column name or index."""
64
- return self._row[key]
65
-
66
- def __len__(self) -> int:
67
- """Get number of columns in the row."""
68
- return len(self._row)
69
-
70
- def __repr__(self) -> str:
71
- """String representation of the row."""
72
- return f"Row({self.to_dict()})"
73
-
74
-
75
- class ExecutionResult:
76
- """Python wrapper around ExecutionResult for better type hints."""
77
-
78
- def __init__(self, py_result):
79
- """Initialize with a ExecutionResult instance."""
80
- self._result = py_result
81
-
82
- def rows(self) -> List[Row]:
83
- """Query result rows (for SELECT queries).
84
-
85
- Returns:
86
- List of Row objects
87
- """
88
- if self._result.has_rows():
89
- # Get raw rows - could be property or method
90
- try:
91
- if callable(self._result.rows):
92
- raw_rows = self._result.rows()
93
- else:
94
- raw_rows = self._result.rows
95
- return [Row(py_row) for py_row in raw_rows]
96
- except Exception:
97
- return []
98
- return []
99
-
100
- @property
101
- def affected_rows(self) -> Optional[int]:
102
- """Number of affected rows (for INSERT/UPDATE/DELETE).
103
-
104
- Returns:
105
- Number of affected rows, or None if not applicable
106
- """
107
- return self._result.affected_rows
108
-
109
- def has_rows(self) -> bool:
110
- """Check if result contains rows.
111
-
112
- Returns:
113
- True if result has rows (SELECT query), False otherwise
114
- """
115
- return self._result.has_rows()
116
-
117
- def __len__(self) -> int:
118
- """Get number of rows in the result."""
119
- return len(self.rows())
120
-
121
- def __iter__(self):
122
- """Iterate over rows in the result."""
123
- return iter(self.rows())
124
-
125
- def __repr__(self) -> str:
126
- """String representation of the result."""
127
- if self.has_rows():
128
- return f"ExecutionResult(rows={len(self.rows())})"
129
- else:
130
- return f"ExecutionResult(affected_rows={self.affected_rows})"
131
-
132
- class PoolConfig:
133
- """Python wrapper around PoolConfig for better documentation."""
134
-
135
- def __init__(
136
- self,
137
- max_size: int = 10,
138
- min_idle: Optional[int] = None,
139
- max_lifetime_secs: Optional[int] = None,
140
- idle_timeout_secs: Optional[int] = None,
141
- connection_timeout_secs: Optional[int] = None
142
- ):
143
- """Initialize connection pool configuration.
144
-
145
- Args:
146
- max_size: Maximum number of connections in pool (default: 10)
147
- min_idle: Minimum number of idle connections to maintain
148
- max_lifetime_secs: Maximum lifetime of connections in seconds
149
- idle_timeout_secs: How long a connection can be idle before being closed (seconds)
150
- connection_timeout_secs: Timeout for establishing new connections (seconds)
151
- """
152
- self._config = _core.PoolConfig(
153
- max_size=max_size,
154
- min_idle=min_idle,
155
- max_lifetime_secs=max_lifetime_secs,
156
- idle_timeout_secs=idle_timeout_secs,
157
- connection_timeout_secs=connection_timeout_secs
158
- )
159
-
160
- @property
161
- def max_size(self) -> int:
162
- """Maximum number of connections in pool."""
163
- return self._config.max_size
164
-
165
- @property
166
- def min_idle(self) -> Optional[int]:
167
- """Minimum number of idle connections."""
168
- return self._config.min_idle
169
-
170
- @property
171
- def max_lifetime_secs(self) -> Optional[int]:
172
- """Maximum lifetime of connections in seconds."""
173
- return self._config.max_lifetime_secs
174
-
175
- @property
176
- def idle_timeout_secs(self) -> Optional[int]:
177
- """Idle timeout in seconds."""
178
- return self._config.idle_timeout_secs
179
-
180
- @property
181
- def connection_timeout_secs(self) -> Optional[int]:
182
- """Connection timeout in seconds."""
183
- return self._config.connection_timeout_secs
184
-
185
- @staticmethod
186
- def high_throughput() -> 'PoolConfig':
187
- """Create configuration for high-throughput scenarios."""
188
- config = PoolConfig.__new__(PoolConfig)
189
- config._config = _core.PoolConfig.high_throughput()
190
- return config
191
-
192
- @staticmethod
193
- def low_resource() -> 'PoolConfig':
194
- """Create configuration for low-resource scenarios."""
195
- config = PoolConfig.__new__(PoolConfig)
196
- config._config = _core.PoolConfig.low_resource()
197
- return config
198
-
199
- @staticmethod
200
- def development() -> 'PoolConfig':
201
- """Create configuration for development scenarios."""
202
- config = PoolConfig.__new__(PoolConfig)
203
- config._config = _core.PoolConfig.development()
204
- return config
205
-
206
-
207
- class Parameter:
208
- """Represents a SQL parameter with value and optional type information."""
209
-
210
- # Valid SQL parameter types (base types without parameters)
211
- VALID_SQL_TYPES = {
212
- 'VARCHAR', 'NVARCHAR', 'CHAR', 'NCHAR', 'TEXT', 'NTEXT',
213
- 'INT', 'BIGINT', 'SMALLINT', 'TINYINT', 'BIT',
214
- 'FLOAT', 'REAL', 'DECIMAL', 'NUMERIC', 'MONEY', 'SMALLMONEY',
215
- 'DATETIME', 'DATETIME2', 'SMALLDATETIME', 'DATE', 'TIME', 'DATETIMEOFFSET',
216
- 'BINARY', 'VARBINARY', 'IMAGE',
217
- 'UNIQUEIDENTIFIER', 'XML', 'JSON'
218
- }
219
-
220
- @staticmethod
221
- def _extract_base_type(sql_type: str) -> str:
222
- """Extract the base SQL type from a type specification.
223
-
224
- Examples:
225
- VARCHAR(50) -> VARCHAR
226
- NVARCHAR(MAX) -> NVARCHAR
227
- DECIMAL(10,2) -> DECIMAL
228
- INT -> INT
229
- """
230
- # Find the first opening parenthesis and extract everything before it
231
- paren_pos = sql_type.find('(')
232
- if paren_pos != -1:
233
- return sql_type[:paren_pos].strip().upper()
234
- return sql_type.strip().upper()
235
-
236
- def __init__(self, value: Any, sql_type: Optional[str] = None) -> None:
237
- """Initialize a parameter.
238
-
239
- Args:
240
- value: The parameter value (None, bool, int, float, str, bytes, or iterable for IN clauses)
241
- sql_type: Optional SQL type hint. Can include parameters:
242
- - 'VARCHAR(50)', 'NVARCHAR(MAX)', 'DECIMAL(10,2)', etc.
243
- - Base types: 'VARCHAR', 'INT', 'DATETIME', etc.
244
-
245
- Raises:
246
- ValueError: If sql_type is provided but the base type is not recognized
247
-
248
- Note:
249
- Lists, tuples, sets and other iterables (except strings/bytes) are automatically
250
- expanded for use in IN clauses. So Parameter([1, 2, 3]) will expand to
251
- placeholder values for "WHERE id IN (@P1, @P2, @P3)".
252
- """
253
- # Automatically detect iterables for IN clause expansion
254
- if self._is_iterable_value(value):
255
- self.value = list(value) # Convert to list for consistency
256
- self.is_expanded = True
257
- else:
258
- self.value = value
259
- self.is_expanded = False
260
-
261
- if sql_type is not None:
262
- # Extract base type and validate it
263
- base_type = self._extract_base_type(sql_type)
264
- if base_type not in self.VALID_SQL_TYPES:
265
- raise ValueError(
266
- f"Invalid sql_type '{sql_type}'. Base type '{base_type}' not recognized. "
267
- f"Valid base types: {', '.join(sorted(self.VALID_SQL_TYPES))}"
268
- )
269
- # Store the original type specification (including parameters)
270
- self.sql_type = sql_type.upper()
271
- else:
272
- self.sql_type = None
273
-
274
- @staticmethod
275
- def _is_iterable_value(value: Any) -> bool:
276
- """Check if a value is an iterable that can be expanded for IN clauses.
277
-
278
- Returns True for lists, tuples, sets, etc., but False for strings and bytes
279
- which should be treated as single values.
280
- """
281
- return (
282
- hasattr(value, '__iter__') and
283
- not isinstance(value, (str, bytes))
284
- )
285
-
286
- def __repr__(self) -> str:
287
- if self.is_expanded:
288
- type_info = f", type={self.sql_type}" if self.sql_type else ""
289
- return f"Parameter(IN_values={self.value!r}{type_info})"
290
- elif self.sql_type:
291
- return f"Parameter(value={self.value!r}, type={self.sql_type})"
292
- return f"Parameter(value={self.value!r})"
293
-
294
-
295
- class Parameters:
296
- """Container for SQL parameters that can be passed to execute()."""
297
-
298
- def __init__(self, *args, **kwargs):
299
- """Initialize parameters container.
300
-
301
- Args:
302
- *args: Positional parameter values. Can be individual values or iterables.
303
- Iterables (lists, tuples, etc.) will be expanded by Rust for performance.
304
- Strings and bytes are treated as single values, not expanded.
305
- **kwargs: Named parameter values (for @name placeholders, if supported)
306
-
307
- Examples:
308
- # Individual parameters
309
- params = Parameters(1, "hello", 3.14)
310
-
311
- # Mix of individual and iterable parameters (expansion handled in Rust)
312
- params = Parameters(1, [2, 3, 4], "hello") # Rust expands [2,3,4] automatically
313
-
314
- # All types of iterables work
315
- params = Parameters([1, 2], (3, 4), {5, 6}) # All expanded by Rust
316
- """
317
- self._positional = []
318
- self._named = {}
319
-
320
- # Handle positional parameters - let Rust handle expansion
321
- for arg in args:
322
- if isinstance(arg, Parameter):
323
- self._positional.append(arg)
324
- else:
325
- self._positional.append(Parameter(arg))
326
-
327
- # Handle named parameters
328
- for name, value in kwargs.items():
329
- if isinstance(value, Parameter):
330
- self._named[name] = value
331
- else:
332
- self._named[name] = Parameter(value)
333
-
334
- def add(self, value: Any, sql_type: Optional[str] = None) -> 'Parameters':
335
- """Add a positional parameter and return self for chaining.
336
-
337
- Args:
338
- value: Parameter value (can be an iterable for automatic expansion by Rust)
339
- sql_type: Optional SQL type hint
340
-
341
- Returns:
342
- Self for method chaining
343
-
344
- Examples:
345
- params = Parameters().add(42).add("hello")
346
- params = Parameters().add([1, 2, 3]) # Rust expands automatically
347
- """
348
- self._positional.append(Parameter(value, sql_type))
349
- return self
350
-
351
- def extend(self, other: Union['Parameters', Iterable[Any]]) -> 'Parameters':
352
- """Extend parameters with another Parameters object or iterable.
353
-
354
- Args:
355
- other: Another Parameters object or an iterable of values
356
-
357
- Returns:
358
- Self for method chaining
359
-
360
- Examples:
361
- params1 = Parameters(1, 2)
362
- params2 = Parameters(3, 4)
363
- params1.extend(params2) # params1 now has [1, 2, 3, 4]
364
-
365
- params = Parameters(1, 2)
366
- params.extend([3, 4, 5]) # params now has [1, 2, [3, 4, 5]] - Rust handles expansion
367
- """
368
- if isinstance(other, Parameters):
369
- self._positional.extend(other._positional)
370
- self._named.update(other._named)
371
- else:
372
- # Add as single parameter - Rust will expand if it's an iterable
373
- self._positional.append(Parameter(other))
374
- return self
375
-
376
- def set(self, name: str, value: Any, sql_type: Optional[str] = None) -> 'Parameters':
377
- """Set a named parameter and return self for chaining.
378
-
379
- Args:
380
- name: Parameter name
381
- value: Parameter value
382
- sql_type: Optional SQL type hint
383
-
384
- Returns:
385
- Self for method chaining
386
- """
387
- self._named[name] = Parameter(value, sql_type)
388
- return self
389
-
390
- @property
391
- def positional(self) -> List[Parameter]:
392
- """Get positional parameters."""
393
- return self._positional.copy()
394
-
395
- @property
396
- def named(self) -> Dict[str, Parameter]:
397
- """Get named parameters."""
398
- return self._named.copy()
399
-
400
- def to_list(self) -> List[Any]:
401
- """Convert to simple list of values for compatibility.
402
-
403
- Note: Iterable expansion is now handled by Rust for performance,
404
- so this returns the raw values as-is.
405
- """
406
- return [param.value for param in self._positional]
407
-
408
- def __len__(self) -> int:
409
- """Get total number of parameters."""
410
- return len(self._positional) + len(self._named)
411
-
412
- def __repr__(self) -> str:
413
- parts = []
414
- if self._positional:
415
- parts.append(f"positional={len(self._positional)}")
416
- if self._named:
417
- parts.append(f"named={len(self._named)}")
418
- return f"Parameters({', '.join(parts)})"
419
-
420
-
421
- class SslConfig:
422
- """SSL/TLS configuration for database connections."""
423
-
424
- def __init__(
425
- self,
426
- encryption_level: Optional[str] = None,
427
- trust_server_certificate: bool = False,
428
- ca_certificate_path: Optional[str] = None,
429
- enable_sni: bool = True,
430
- server_name: Optional[str] = None
431
- ):
432
- """Initialize SSL configuration.
433
-
434
- Args:
435
- encryption_level: Encryption level - "Required", "LoginOnly", or "Off" (default: "Required")
436
- trust_server_certificate: Trust server certificate without validation (dangerous in production)
437
- ca_certificate_path: Path to custom CA certificate file (.pem, .crt, or .der)
438
- enable_sni: Enable Server Name Indication (default: True)
439
- server_name: Custom server name for certificate validation
440
-
441
- Note:
442
- trust_server_certificate and ca_certificate_path are mutually exclusive.
443
- For production, either use a valid certificate in the system trust store,
444
- or provide a ca_certificate_path to a trusted CA certificate.
445
- """
446
- # Set default encryption level
447
- if encryption_level is None:
448
- encryption_level = "Required"
449
-
450
- # Validate encryption level
451
- valid_levels = ["Required", "LoginOnly", "Off"]
452
- if encryption_level not in valid_levels:
453
- raise ValueError(f"Invalid encryption_level. Must be one of: {valid_levels}")
454
-
455
- # Map string encryption levels to enum values
456
- encryption_level_map = {
457
- "Required": _core.EncryptionLevel.REQUIRED,
458
- "LoginOnly": _core.EncryptionLevel.LOGIN_ONLY,
459
- "Off": _core.EncryptionLevel.OFF
460
- }
461
-
462
- self._config = _core.SslConfig(
463
- encryption_level=encryption_level_map[encryption_level],
464
- trust_server_certificate=trust_server_certificate,
465
- ca_certificate_path=ca_certificate_path,
466
- enable_sni=enable_sni,
467
- server_name=server_name
468
- )
469
-
470
- @staticmethod
471
- def development() -> 'SslConfig':
472
- """Create SSL config for development (trusts all certificates).
473
-
474
- Warning: This configuration is insecure and should only be used in development.
475
- """
476
- config = SslConfig.__new__(SslConfig)
477
- config._config = _core.SslConfig.development()
478
- return config
479
-
480
- @staticmethod
481
- def with_ca_certificate(ca_cert_path: str) -> 'SslConfig':
482
- """Create SSL config for production with custom CA certificate.
483
-
484
- Args:
485
- ca_cert_path: Path to CA certificate file (.pem, .crt, or .der)
486
- """
487
- config = SslConfig.__new__(SslConfig)
488
- config._config = _core.SslConfig.with_ca_certificate(ca_cert_path)
489
- return config
490
-
491
- @staticmethod
492
- def login_only() -> 'SslConfig':
493
- """Create SSL config that only encrypts login (legacy mode)."""
494
- config = SslConfig.__new__(SslConfig)
495
- config._config = _core.SslConfig.login_only()
496
- return config
497
-
498
- @staticmethod
499
- def disabled() -> 'SslConfig':
500
- """Create SSL config with no encryption (not recommended)."""
501
- config = SslConfig.__new__(SslConfig)
502
- config._config = _core.SslConfig.disabled()
503
- return config
504
-
505
- @property
506
- def encryption_level(self) -> str:
507
- """Get the encryption level."""
508
- return str(self._config.encryption_level)
509
-
510
- @property
511
- def trust_server_certificate(self) -> bool:
512
- """Get trust server certificate setting."""
513
- return self._config.trust_server_certificate
514
-
515
- @property
516
- def ca_certificate_path(self) -> Optional[str]:
517
- """Get CA certificate path."""
518
- return self._config.ca_certificate_path
519
-
520
- @property
521
- def enable_sni(self) -> bool:
522
- """Get SNI setting."""
523
- return self._config.enable_sni
524
-
525
- @property
526
- def server_name(self) -> Optional[str]:
527
- """Get custom server name."""
528
- return self._config.server_name
529
-
530
- def __repr__(self) -> str:
531
- return f"SslConfig(encryption={self.encryption_level}, trust_cert={self.trust_server_certificate})"
532
-
533
-
534
- class Connection:
535
- """Async connection to Microsoft SQL Server database with enhanced type support."""
536
-
537
- def __init__(
538
- self,
539
- connection_string: Optional[str] = None,
540
- pool_config: Optional[PoolConfig] = None,
541
- ssl_config: Optional['SslConfig'] = None,
542
- auto_connect: bool = False,
543
- server: Optional[str] = None,
544
- database: Optional[str] = None,
545
- username: Optional[str] = None,
546
- password: Optional[str] = None,
547
- trusted_connection: Optional[bool] = None
548
- ):
549
- """Initialize a new async connection.
550
-
551
- Args:
552
- connection_string: SQL Server connection string (if not using individual parameters)
553
- pool_config: Optional connection pool configuration
554
- ssl_config: Optional SSL/TLS configuration
555
- auto_connect: If True, automatically connect on creation (not supported for async)
556
- server: Database server hostname or IP address
557
- database: Database name to connect to
558
- username: Username for SQL Server authentication
559
- password: Password for SQL Server authentication
560
- trusted_connection: Use Windows integrated authentication (default: True if no username provided)
561
-
562
- Note:
563
- Either connection_string OR server must be provided.
564
- If using individual parameters, server is required.
565
- If username is provided, password should also be provided for SQL authentication.
566
- If username is not provided, Windows integrated authentication will be used.
567
- """
568
- py_pool_config = pool_config._config if pool_config else None
569
- py_ssl_config = ssl_config._config if ssl_config else None
570
- self._conn = _core.Connection(
571
- connection_string,
572
- py_pool_config,
573
- py_ssl_config,
574
- server,
575
- database,
576
- username,
577
- password,
578
- trusted_connection
579
- )
580
- self._connected = False
581
- if auto_connect:
582
- # Note: Can't await in __init__, so auto_connect won't work for async
583
- pass
584
-
585
- async def connect(self) -> None:
586
- """Connect to the database asynchronously."""
587
- await self._conn.connect()
588
- self._connected = True
589
-
590
- async def disconnect(self) -> None:
591
- """Disconnect from the database asynchronously."""
592
- await self._conn.disconnect()
593
- self._connected = False
594
-
595
- async def is_connected(self) -> bool:
596
- """Check if connected to the database."""
597
- return await self._conn.is_connected()
598
-
599
- async def execute(self, sql: str, parameters: Optional[Union[List[Any], Parameters, Iterable[Any]]] = None) -> ExecutionResult:
600
- """Execute a query asynchronously and return enhanced results.
601
-
602
- Args:
603
- sql: SQL query to execute (must be non-empty)
604
- parameters: Optional parameters - can be:
605
- - List of values for @P1 placeholders
606
- - Parameters object for more control
607
- - Any iterable of values (tuple, set, generator, etc.)
608
-
609
- Returns:
610
- ExecutionResult object with rows or affected row count
611
-
612
- Raises:
613
- RuntimeError: If not connected to database
614
- ValueError: If sql is empty or None
615
-
616
- Examples:
617
- # Simple list of parameters
618
- result = await conn.execute("SELECT * FROM users WHERE age > @P1 AND name = @P2", [18, "John"])
619
-
620
- # Using tuple
621
- result = await conn.execute("SELECT * FROM users WHERE age > @P1 AND name = @P2", (18, "John"))
622
-
623
- # Automatic IN clause expansion (handled by Rust for performance)
624
- result = await conn.execute("SELECT * FROM users WHERE id IN (@P1)", [[1, 2, 3, 4]])
625
- # Rust automatically expands to: WHERE id IN (@P1, @P2, @P3, @P4)
626
-
627
- # Using Parameters with automatic IN clause expansion
628
- params = Parameters([1, 2, 3, 4], "John")
629
- result = await conn.execute("SELECT * FROM users WHERE id IN (@P1) AND name = @P2", params)
630
- # Rust expands the list automatically
631
-
632
- # Using Parameters with type hints
633
- params = Parameters(Parameter([1, 2, 3, 4], "INT"), "John")
634
- result = await conn.execute("SELECT * FROM users WHERE id IN (@P1) AND name = @P2", params)
635
-
636
- # Method chaining with iterables
637
- params = Parameters().add(18, "INT").add(["admin", "user"])
638
- result = await conn.execute("SELECT * FROM users WHERE age > @P1 AND role IN (@P2)", params)
639
- """
640
- if not sql or not sql.strip():
641
- raise ValueError("SQL query cannot be empty or None")
642
-
643
- if not self._connected:
644
- raise RuntimeError("Not connected to database. Call await conn.connect() first.")
645
-
646
- if parameters is None:
647
- py_result = await self._conn.execute(sql)
648
- elif isinstance(parameters, Parameters):
649
- # Convert Parameters object to list of values
650
- param_values = parameters.to_list()
651
- py_result = await self._conn.execute_with_python_params(sql, param_values)
652
- elif hasattr(parameters, '__iter__') and not isinstance(parameters, (str, bytes)):
653
- # Handle any iterable (list, tuple, set, generator, etc.)
654
- # Convert to list to ensure we can pass it to the Rust layer
655
- param_values = list(parameters)
656
- py_result = await self._conn.execute_with_python_params(sql, param_values)
657
- else:
658
- # Single value - wrap in list
659
- py_result = await self._conn.execute_with_python_params(sql, [parameters])
660
-
661
- return ExecutionResult(py_result)
662
-
663
- async def __aenter__(self):
664
- """Async context manager entry."""
665
- await self.connect()
666
- return self
667
-
668
- async def __aexit__(self, exc_type, exc_val, exc_tb):
669
- """Async context manager exit."""
670
- await self.disconnect()
671
-
672
-
673
- def version() -> str:
674
- """Get the version of the mssql-python-rust library.
675
-
676
- Returns:
677
- Version string
678
- """
679
- return _core.version()
680
-
681
- # Re-export core types for direct access if needed
682
- RustConnection = _core.Connection # Rename to avoid conflict with our main connect() function
683
- RustQuery = _core.Query # Rename to avoid conflict with our wrapper
684
- PyRow = _core.Row
685
- PyValue = _core.Value
686
- PyExecutionResult = _core.ExecutionResult
687
- PyQuery = _core.Query
688
-
689
- # Export main API
690
- __all__ = [
691
- 'Connection', # Main connection class
692
- 'Parameter', # Individual parameter with optional type
693
- 'Parameters', # Parameter container for execute()
694
- 'Row',
695
- 'ExecutionResult',
696
- 'PoolConfig',
697
- 'SslConfig', # SSL/TLS configuration
698
- 'version', # Version function
699
- # Core types for advanced usage
700
- 'RustConnection',
701
- 'RustQuery',
702
- 'PyRow',
703
- 'PyValue',
704
- 'PyExecutionResult',
705
- 'PyQuery'
706
- ]