mssql-python 1.0.0a0__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 mssql-python might be problematic. Click here for more details.

@@ -0,0 +1,37 @@
1
+ from mssql_python.exceptions import DatabaseError, InterfaceError
2
+ from mssql_python.connection import Connection
3
+
4
+ def connect(connection_str: str, autocommit: bool = True, **kwargs) -> Connection:
5
+ """
6
+ Constructor for creating a connection to the database.
7
+
8
+ Args:
9
+ connection_str (str): The connection string to connect to.
10
+ autocommit (bool): If True, causes a commit to be performed after each SQL statement.
11
+ TODO: Add the following parameters to the function signature:
12
+ timeout (int): The timeout for the connection attempt, in seconds.
13
+ readonly (bool): If True, the connection is set to read-only.
14
+ attrs_before (dict): A dictionary of connection attributes to set before connecting.
15
+ Keyword Args:
16
+ **kwargs: Additional key/value pairs for the connection string.
17
+ Below attributes are not implemented in the internal driver:
18
+ - encoding (str): The encoding for the connection string.
19
+ - ansi (bool): If True, indicates the driver does not support Unicode.
20
+
21
+ Returns:
22
+ Connection: A new connection object to interact with the database.
23
+
24
+ Raises:
25
+ DatabaseError: If there is an error while trying to connect to the database.
26
+ InterfaceError: If there is an error related to the database interface.
27
+
28
+ This function provides a way to create a new connection object, which can then
29
+ be used to perform database operations such as executing queries, committing
30
+ transactions, and closing the connection.
31
+ """
32
+ conn = Connection(
33
+ connection_str,
34
+ autocommit=autocommit,
35
+ **kwargs
36
+ )
37
+ return conn
Binary file
@@ -0,0 +1,254 @@
1
+ from mssql_python.logging_config import get_logger, ENABLE_LOGGING
2
+
3
+ logger = get_logger()
4
+
5
+ class Exception(Exception):
6
+ """
7
+ Base class for all DB API 2.0 exceptions.
8
+ """
9
+ def __init__(self, driver_error, ddbc_error) -> None:
10
+ self.driver_error = driver_error
11
+ self.ddbc_error = truncate_error_message(ddbc_error)
12
+ self.message = f"Driver Error: {self.driver_error}; DDBC Error: {self.ddbc_error}"
13
+ super().__init__(self.message)
14
+
15
+ class Warning(Exception):
16
+ """
17
+ Exception raised for important warnings like data truncations while inserting, etc.
18
+ """
19
+ def __init__(self, driver_error, ddbc_error) -> None:
20
+ super().__init__(driver_error, ddbc_error)
21
+
22
+ class Error(Exception):
23
+ """
24
+ Base class for errors.
25
+ """
26
+ def __init__(self, driver_error, ddbc_error) -> None:
27
+ super().__init__(driver_error, ddbc_error)
28
+
29
+ class InterfaceError(Error):
30
+ """
31
+ Exception raised for errors that are related to the database interface rather than the database itself.
32
+ """
33
+ def __init__(self, driver_error, ddbc_error) -> None:
34
+ super().__init__(driver_error, ddbc_error)
35
+
36
+ class DatabaseError(Error):
37
+ """
38
+ Exception raised for errors that are related to the database.
39
+ """
40
+ def __init__(self, driver_error, ddbc_error) -> None:
41
+ super().__init__(driver_error, ddbc_error)
42
+
43
+ class DataError(DatabaseError):
44
+ """
45
+ Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range, etc.
46
+ """
47
+ def __init__(self, driver_error, ddbc_error) -> None:
48
+ super().__init__(driver_error, ddbc_error)
49
+
50
+ class OperationalError(DatabaseError):
51
+ """
52
+ Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer.
53
+ """
54
+ def __init__(self, driver_error, ddbc_error) -> None:
55
+ super().__init__(driver_error, ddbc_error)
56
+
57
+ class IntegrityError(DatabaseError):
58
+ """
59
+ Exception raised when the relational integrity of the database is affected, e.g., a foreign key check fails.
60
+ """
61
+ def __init__(self, driver_error, ddbc_error) -> None:
62
+ super().__init__(driver_error, ddbc_error)
63
+
64
+ class InternalError(DatabaseError):
65
+ """
66
+ Exception raised when the database encounters an internal error, e.g., the cursor is not valid anymore, the transaction is out of sync, etc.
67
+ """
68
+ def __init__(self, driver_error, ddbc_error) -> None:
69
+ super().__init__(driver_error, ddbc_error)
70
+
71
+ class ProgrammingError(DatabaseError):
72
+ """
73
+ Exception raised for programming errors, e.g., table not found or already exists, syntax error in the SQL statement, wrong number of parameters specified, etc.
74
+ """
75
+ def __init__(self, driver_error, ddbc_error) -> None:
76
+ super().__init__(driver_error, ddbc_error)
77
+
78
+ class NotSupportedError(DatabaseError):
79
+ """
80
+ Exception raised in case a method or database API was used which is not supported by the database, e.g., requesting a .rollback() on a connection that does not support transaction or has transactions turned off.
81
+ """
82
+ def __init__(self, driver_error, ddbc_error) -> None:
83
+ super().__init__(driver_error, ddbc_error)
84
+
85
+ # Mapping SQLSTATE codes to custom exception classes
86
+ def sqlstate_to_exception(sqlstate: str, ddbc_error: str) -> Exception:
87
+ """
88
+ Map an SQLSTATE code to a custom exception class.
89
+ This function maps an SQLSTATE code to a custom exception class based on the code.
90
+ If the code is not found in the mapping, a generic DatabaseError class is returned
91
+ to handle the error.
92
+ Args:
93
+ sqlstate (str): The SQLSTATE code to map to a custom exception class.
94
+ Returns:
95
+ mapping[str, Exception]: A mapping of SQLSTATE codes to custom exception classes.
96
+ """
97
+ mapping = {
98
+ '01000': Warning(driver_error="General warning", ddbc_error=ddbc_error), # General warning
99
+ '01001': OperationalError(driver_error="Cursor operation conflict", ddbc_error=ddbc_error), # Cursor operation conflict
100
+ '01002': OperationalError(driver_error="Disconnect error", ddbc_error=ddbc_error), # Disconnect error
101
+ '01003': DataError(driver_error="NULL value eliminated in set function", ddbc_error=ddbc_error), # NULL value eliminated in set function
102
+ '01004': DataError(driver_error="String data, right-truncated", ddbc_error=ddbc_error), # String data, right-truncated
103
+ '01006': OperationalError(driver_error="Privilege not revoked", ddbc_error=ddbc_error), # Privilege not revoked
104
+ '01007': OperationalError(driver_error="Privilege not granted", ddbc_error=ddbc_error), # Privilege not granted
105
+ '01S00': ProgrammingError(driver_error="Invalid connection string attribute", ddbc_error=ddbc_error), # Invalid connection string attribute
106
+ '01S01': DataError(driver_error="Error in row", ddbc_error=ddbc_error), # Error in row
107
+ '01S02': Warning(driver_error="Option value changed", ddbc_error=ddbc_error), # Option value changed
108
+ '01S06': OperationalError(driver_error="Attempt to fetch before the result set returned the first rowset", ddbc_error=ddbc_error), # Attempt to fetch before the result set returned the first rowset
109
+ '01S07': DataError(driver_error="Fractional truncation", ddbc_error=ddbc_error), # Fractional truncation
110
+ '01S08': OperationalError(driver_error="Error saving File DSN", ddbc_error=ddbc_error), # Error saving File DSN
111
+ '01S09': ProgrammingError(driver_error="Invalid keyword", ddbc_error=ddbc_error), # Invalid keyword
112
+ '07001': ProgrammingError(driver_error="Wrong number of parameters", ddbc_error=ddbc_error), # Wrong number of parameters
113
+ '07002': ProgrammingError(driver_error="COUNT field incorrect", ddbc_error=ddbc_error), # COUNT field incorrect
114
+ '07005': ProgrammingError(driver_error="Prepared statement not a cursor-specification", ddbc_error=ddbc_error), # Prepared statement not a cursor-specification
115
+ '07006': ProgrammingError(driver_error="Restricted data type attribute violation", ddbc_error=ddbc_error), # Restricted data type attribute violation
116
+ '07009': ProgrammingError(driver_error="Invalid descriptor index", ddbc_error=ddbc_error), # Invalid descriptor index
117
+ '07S01': ProgrammingError(driver_error="Invalid use of default parameter", ddbc_error=ddbc_error), # Invalid use of default parameter
118
+ '08001': OperationalError(driver_error="Client unable to establish connection", ddbc_error=ddbc_error), # Client unable to establish connection
119
+ '08002': OperationalError(driver_error="Connection name in use", ddbc_error=ddbc_error), # Connection name in use
120
+ '08003': OperationalError(driver_error="Connection not open", ddbc_error=ddbc_error), # Connection not open
121
+ '08004': OperationalError(driver_error="Server rejected the connection", ddbc_error=ddbc_error), # Server rejected the connection
122
+ '08007': OperationalError(driver_error="Connection failure during transaction", ddbc_error=ddbc_error), # Connection failure during transaction
123
+ '08S01': OperationalError(driver_error="Communication link failure", ddbc_error=ddbc_error), # Communication link failure
124
+ '21S01': ProgrammingError(driver_error="Insert value list does not match column list", ddbc_error=ddbc_error), # Insert value list does not match column list
125
+ '21S02': ProgrammingError(driver_error="Degree of derived table does not match column list", ddbc_error=ddbc_error), # Degree of derived table does not match column list
126
+ '22001': DataError(driver_error="String data, right-truncated", ddbc_error=ddbc_error), # String data, right-truncated
127
+ '22002': DataError(driver_error="Indicator variable required but not supplied", ddbc_error=ddbc_error), # Indicator variable required but not supplied
128
+ '22003': DataError(driver_error="Numeric value out of range", ddbc_error=ddbc_error), # Numeric value out of range
129
+ '22007': DataError(driver_error="Invalid datetime format", ddbc_error=ddbc_error), # Invalid datetime format
130
+ '22008': DataError(driver_error="Datetime field overflow", ddbc_error=ddbc_error), # Datetime field overflow
131
+ '22012': DataError(driver_error="Division by zero", ddbc_error=ddbc_error), # Division by zero
132
+ '22015': DataError(driver_error="Interval field overflow", ddbc_error=ddbc_error), # Interval field overflow
133
+ '22018': DataError(driver_error="Invalid character value for cast specification", ddbc_error=ddbc_error), # Invalid character value for cast specification
134
+ '22019': ProgrammingError(driver_error="Invalid escape character", ddbc_error=ddbc_error), # Invalid escape character
135
+ '22025': ProgrammingError(driver_error="Invalid escape sequence", ddbc_error=ddbc_error), # Invalid escape sequence
136
+ '22026': DataError(driver_error="String data, length mismatch", ddbc_error=ddbc_error), # String data, length mismatch
137
+ '23000': IntegrityError(driver_error="Integrity constraint violation", ddbc_error=ddbc_error), # Integrity constraint violation
138
+ '24000': ProgrammingError(driver_error="Invalid cursor state", ddbc_error=ddbc_error), # Invalid cursor state
139
+ '25000': OperationalError(driver_error="Invalid transaction state", ddbc_error=ddbc_error), # Invalid transaction state
140
+ '25S01': OperationalError(driver_error="Transaction state", ddbc_error=ddbc_error), # Transaction state
141
+ '25S02': OperationalError(driver_error="Transaction is still active", ddbc_error=ddbc_error), # Transaction is still active
142
+ '25S03': OperationalError(driver_error="Transaction is rolled back", ddbc_error=ddbc_error), # Transaction is rolled back
143
+ '28000': OperationalError(driver_error="Invalid authorization specification", ddbc_error=ddbc_error), # Invalid authorization specification
144
+ '34000': ProgrammingError(driver_error="Invalid cursor name", ddbc_error=ddbc_error), # Invalid cursor name
145
+ '3C000': ProgrammingError(driver_error="Duplicate cursor name", ddbc_error=ddbc_error), # Duplicate cursor name
146
+ '3D000': ProgrammingError(driver_error="Invalid catalog name", ddbc_error=ddbc_error), # Invalid catalog name
147
+ '3F000': ProgrammingError(driver_error="Invalid schema name", ddbc_error=ddbc_error), # Invalid schema name
148
+ '40001': OperationalError(driver_error="Serialization failure", ddbc_error=ddbc_error), # Serialization failure
149
+ '40002': IntegrityError(driver_error="Integrity constraint violation", ddbc_error=ddbc_error), # Integrity constraint violation
150
+ '40003': OperationalError(driver_error="Statement completion unknown", ddbc_error=ddbc_error), # Statement completion unknown
151
+ '42000': ProgrammingError(driver_error="Syntax error or access violation", ddbc_error=ddbc_error), # Syntax error or access violation
152
+ '42S01': ProgrammingError(driver_error="Base table or view already exists", ddbc_error=ddbc_error), # Base table or view already exists
153
+ '42S02': ProgrammingError(driver_error="Base table or view not found", ddbc_error=ddbc_error), # Base table or view not found
154
+ '42S11': ProgrammingError(driver_error="Index already exists", ddbc_error=ddbc_error), # Index already exists
155
+ '42S12': ProgrammingError(driver_error="Index not found", ddbc_error=ddbc_error), # Index not found
156
+ '42S21': ProgrammingError(driver_error="Column already exists", ddbc_error=ddbc_error), # Column already exists
157
+ '42S22': ProgrammingError(driver_error="Column not found", ddbc_error=ddbc_error), # Column not found
158
+ '44000': IntegrityError(driver_error="WITH CHECK OPTION violation", ddbc_error=ddbc_error), # WITH CHECK OPTION violation
159
+ 'HY000': OperationalError(driver_error="General error", ddbc_error=ddbc_error), # General error
160
+ 'HY001': OperationalError(driver_error="Memory allocation error", ddbc_error=ddbc_error), # Memory allocation error
161
+ 'HY003': ProgrammingError(driver_error="Invalid application buffer type", ddbc_error=ddbc_error), # Invalid application buffer type
162
+ 'HY004': ProgrammingError(driver_error="Invalid SQL data type", ddbc_error=ddbc_error), # Invalid SQL data type
163
+ 'HY007': ProgrammingError(driver_error="Associated statement is not prepared", ddbc_error=ddbc_error), # Associated statement is not prepared
164
+ 'HY008': OperationalError(driver_error="Operation canceled", ddbc_error=ddbc_error), # Operation canceled
165
+ 'HY009': ProgrammingError(driver_error="Invalid use of null pointer", ddbc_error=ddbc_error), # Invalid use of null pointer
166
+ 'HY010': ProgrammingError(driver_error="Function sequence error", ddbc_error=ddbc_error), # Function sequence error
167
+ 'HY011': ProgrammingError(driver_error="Attribute cannot be set now", ddbc_error=ddbc_error), # Attribute cannot be set now
168
+ 'HY012': ProgrammingError(driver_error="Invalid transaction operation code", ddbc_error=ddbc_error), # Invalid transaction operation code
169
+ 'HY013': OperationalError(driver_error="Memory management error", ddbc_error=ddbc_error), # Memory management error
170
+ 'HY014': OperationalError(driver_error="Limit on the number of handles exceeded", ddbc_error=ddbc_error), # Limit on the number of handles exceeded
171
+ 'HY015': ProgrammingError(driver_error="No cursor name available", ddbc_error=ddbc_error), # No cursor name available
172
+ 'HY016': ProgrammingError(driver_error="Cannot modify an implementation row descriptor", ddbc_error=ddbc_error), # Cannot modify an implementation row descriptor
173
+ 'HY017': ProgrammingError(driver_error="Invalid use of an automatically allocated descriptor handle", ddbc_error=ddbc_error), # Invalid use of an automatically allocated descriptor handle
174
+ 'HY018': OperationalError(driver_error="Server declined cancel request", ddbc_error=ddbc_error), # Server declined cancel request
175
+ 'HY019': DataError(driver_error="Non-character and non-binary data sent in pieces", ddbc_error=ddbc_error), # Non-character and non-binary data sent in pieces
176
+ 'HY020': DataError(driver_error="Attempt to concatenate a null value", ddbc_error=ddbc_error), # Attempt to concatenate a null value
177
+ 'HY021': ProgrammingError(driver_error="Inconsistent descriptor information", ddbc_error=ddbc_error), # Inconsistent descriptor information
178
+ 'HY024': ProgrammingError(driver_error="Invalid attribute value", ddbc_error=ddbc_error), # Invalid attribute value
179
+ 'HY090': ProgrammingError(driver_error="Invalid string or buffer length", ddbc_error=ddbc_error), # Invalid string or buffer length
180
+ 'HY091': ProgrammingError(driver_error="Invalid descriptor field identifier", ddbc_error=ddbc_error), # Invalid descriptor field identifier
181
+ 'HY092': ProgrammingError(driver_error="Invalid attribute/option identifier", ddbc_error=ddbc_error), # Invalid attribute/option identifier
182
+ 'HY095': ProgrammingError(driver_error="Function type out of range", ddbc_error=ddbc_error), # Function type out of range
183
+ 'HY096': ProgrammingError(driver_error="Invalid information type", ddbc_error=ddbc_error), # Invalid information type
184
+ 'HY097': ProgrammingError(driver_error="Column type out of range", ddbc_error=ddbc_error), # Column type out of range
185
+ 'HY098': ProgrammingError(driver_error="Scope type out of range", ddbc_error=ddbc_error), # Scope type out of range
186
+ 'HY099': ProgrammingError(driver_error="Nullable type out of range", ddbc_error=ddbc_error), # Nullable type out of range
187
+ 'HY100': ProgrammingError(driver_error="Uniqueness option type out of range", ddbc_error=ddbc_error), # Uniqueness option type out of range
188
+ 'HY101': ProgrammingError(driver_error="Accuracy option type out of range", ddbc_error=ddbc_error), # Accuracy option type out of range
189
+ 'HY103': ProgrammingError(driver_error="Invalid retrieval code", ddbc_error=ddbc_error), # Invalid retrieval code
190
+ 'HY104': ProgrammingError(driver_error="Invalid precision or scale value", ddbc_error=ddbc_error), # Invalid precision or scale value
191
+ 'HY105': ProgrammingError(driver_error="Invalid parameter type", ddbc_error=ddbc_error), # Invalid parameter type
192
+ 'HY106': ProgrammingError(driver_error="Fetch type out of range", ddbc_error=ddbc_error), # Fetch type out of range
193
+ 'HY107': ProgrammingError(driver_error="Row value out of range", ddbc_error=ddbc_error), # Row value out of range
194
+ 'HY109': ProgrammingError(driver_error="Invalid cursor position", ddbc_error=ddbc_error), # Invalid cursor position
195
+ 'HY110': ProgrammingError(driver_error="Invalid driver completion", ddbc_error=ddbc_error), # Invalid driver completion
196
+ 'HY111': ProgrammingError(driver_error="Invalid bookmark value", ddbc_error=ddbc_error), # Invalid bookmark value
197
+ 'HYC00': NotSupportedError(driver_error="Optional feature not implemented", ddbc_error=ddbc_error), # Optional feature not implemented
198
+ 'HYT00': OperationalError(driver_error="Timeout expired", ddbc_error=ddbc_error), # Timeout expired
199
+ 'HYT01': OperationalError(driver_error="Connection timeout expired", ddbc_error=ddbc_error), # Connection timeout expired
200
+ 'IM001': NotSupportedError(driver_error="Driver does not support this function", ddbc_error=ddbc_error), # Driver does not support this function
201
+ 'IM002': OperationalError(driver_error="Data source name not found and no default driver specified", ddbc_error=ddbc_error), # Data source name not found and no default driver specified
202
+ 'IM003': OperationalError(driver_error="Specified driver could not be loaded", ddbc_error=ddbc_error), # Specified driver could not be loaded
203
+ 'IM004': OperationalError(driver_error="Driver's SQLAllocHandle on SQL_HANDLE_ENV failed", ddbc_error=ddbc_error), # Driver's SQLAllocHandle on SQL_HANDLE_ENV failed
204
+ 'IM005': OperationalError(driver_error="Driver's SQLAllocHandle on SQL_HANDLE_DBC failed", ddbc_error=ddbc_error), # Driver's SQLAllocHandle on SQL_HANDLE_DBC failed
205
+ 'IM006': OperationalError(driver_error="Driver's SQLSetConnectAttr failed", ddbc_error=ddbc_error), # Driver's SQLSetConnectAttr failed
206
+ 'IM007': OperationalError(driver_error="No data source or driver specified; dialog prohibited", ddbc_error=ddbc_error), # No data source or driver specified; dialog prohibited
207
+ 'IM008': OperationalError(driver_error="Dialog failed", ddbc_error=ddbc_error), # Dialog failed
208
+ 'IM009': OperationalError(driver_error="Unable to load translation DLL", ddbc_error=ddbc_error), # Unable to load translation DLL
209
+ 'IM010': OperationalError(driver_error="Data source name too long", ddbc_error=ddbc_error), # Data source name too long
210
+ 'IM011': OperationalError(driver_error="Driver name too long", ddbc_error=ddbc_error), # Driver name too long
211
+ 'IM012': OperationalError(driver_error="DRIVER keyword syntax error", ddbc_error=ddbc_error), # DRIVER keyword syntax error
212
+ 'IM013': OperationalError(driver_error="Trace file error", ddbc_error=ddbc_error), # Trace file error
213
+ 'IM014': OperationalError(driver_error="Invalid name of File DSN", ddbc_error=ddbc_error), # Invalid name of File DSN
214
+ 'IM015': OperationalError(driver_error="Corrupt file data source", ddbc_error=ddbc_error), # Corrupt file data source
215
+ }
216
+ return mapping.get(sqlstate, None)
217
+
218
+ def truncate_error_message(error_message: str) -> str:
219
+ '''
220
+ - The Driver Error message is the message that is returned by the Internal driver.
221
+ - This section will always be at the start of the message.
222
+ '''
223
+ try:
224
+ if not error_message.startswith('[Microsoft]'):
225
+ # The message is not from the driver, so no need to truncate
226
+ return error_message
227
+ string_first = error_message[:error_message.index(']')+1]
228
+ string_second = error_message[error_message.index(']')+1:]
229
+ string_third = string_second[string_second.index(']')+1:]
230
+ return string_first + string_third
231
+ except Exception as e:
232
+ if ENABLE_LOGGING:
233
+ logger.error(f"Error while truncating error message: {e}")
234
+ return error_message
235
+
236
+ def raise_exception(sqlstate: str, ddbc_error: str) -> None:
237
+ """
238
+ Raise a custom exception based on the given SQLSTATE code.
239
+ This function raises a custom exception based on the provided SQLSTATE code.
240
+ If the code is not found in the mapping, a generic DatabaseError is raised.
241
+
242
+ Args:
243
+ sqlstate (str): The SQLSTATE code to map to a custom exception.
244
+ ddbc_error (str): The DDBC error message.
245
+
246
+ Raises:
247
+ DatabaseError: If the SQLSTATE code is not found in the mapping.
248
+ """
249
+ exception_class = sqlstate_to_exception(sqlstate, ddbc_error)
250
+ if exception_class:
251
+ if ENABLE_LOGGING:
252
+ logger.error(exception_class)
253
+ raise exception_class
254
+ raise DatabaseError(driver_error="An error occurred with SQLSTATE code", ddbc_error=f"Unknown DDBC error: {sqlstate}")
@@ -0,0 +1,99 @@
1
+ from mssql_python import ddbc_bindings
2
+ from mssql_python.exceptions import raise_exception
3
+ from mssql_python.logging_config import get_logger, ENABLE_LOGGING
4
+
5
+ logger = get_logger()
6
+
7
+ def add_driver_to_connection_str(connection_str):
8
+ """
9
+ Add the ODBC driver to the connection string if not present.
10
+
11
+ Args:
12
+ connection_str (str): The original connection string.
13
+
14
+ Returns:
15
+ str: The connection string with the ODBC driver added.
16
+
17
+ Raises:
18
+ Exception: If the connection string is invalid.
19
+ """
20
+ driver_name = 'Driver={ODBC Driver 18 for SQL Server}'
21
+ try:
22
+ # Strip any leading or trailing whitespace from the connection string
23
+ connection_str = connection_str.strip()
24
+ connection_str = add_driver_name_to_app_parameter(connection_str)
25
+
26
+ # Split the connection string into individual attributes
27
+ connection_attributes = connection_str.split(';')
28
+ final_connection_attributes = []
29
+
30
+ # Iterate through the attributes and exclude any existing driver attribute
31
+ for attribute in connection_attributes:
32
+ if attribute.lower().split('=')[0] == 'driver':
33
+ continue
34
+ final_connection_attributes.append(attribute)
35
+
36
+ # Join the remaining attributes back into a connection string
37
+ connection_str = ';'.join(final_connection_attributes)
38
+
39
+ # Insert the driver attribute at the beginning of the connection string
40
+ final_connection_attributes.insert(0, driver_name)
41
+ connection_str = ';'.join(final_connection_attributes)
42
+ except Exception as e:
43
+ raise Exception(("Invalid connection string, Please follow the format: "
44
+ "Server=server_name;Database=database_name;UID=user_name;PWD=password"))
45
+
46
+ return connection_str
47
+
48
+ def check_error(handle_type, handle, ret):
49
+ """
50
+ Check for errors and raise an exception if an error is found.
51
+
52
+ Args:
53
+ handle_type: The type of the handle (e.g., SQL_HANDLE_ENV, SQL_HANDLE_DBC).
54
+ handle: The handle to check for errors.
55
+ ret: The return code from the ODBC function call.
56
+
57
+ Raises:
58
+ RuntimeError: If an error is found.
59
+ """
60
+ if ret < 0:
61
+ error_info = ddbc_bindings.DDBCSQLCheckError(handle_type, handle, ret)
62
+ if ENABLE_LOGGING:
63
+ logger.error(f"Error: {error_info.ddbcErrorMsg}")
64
+ raise_exception(error_info.sqlState, error_info.ddbcErrorMsg)
65
+
66
+ def add_driver_name_to_app_parameter(connection_string):
67
+ """
68
+ Modifies the input connection string by appending the APP name.
69
+
70
+ Args:
71
+ connection_string (str): The input connection string.
72
+
73
+ Returns:
74
+ str: The modified connection string.
75
+ """
76
+ # Split the input string into key-value pairs
77
+ parameters = connection_string.split(';')
78
+
79
+ # Initialize variables
80
+ app_found = False
81
+ modified_parameters = []
82
+
83
+ # Iterate through the key-value pairs
84
+ for param in parameters:
85
+ if param.lower().startswith("app="):
86
+ # Overwrite the value with 'MSSQL-Python'
87
+ app_found = True
88
+ key, value = param.split('=', 1)
89
+ modified_parameters.append(f"{key}=MSSQL-Python")
90
+ else:
91
+ # Keep other parameters as is
92
+ modified_parameters.append(param)
93
+
94
+ # If APP key is not found, append it
95
+ if not app_found:
96
+ modified_parameters.append("APP=MSSQL-Python")
97
+
98
+ # Join the parameters back into a connection string
99
+ return ';'.join(modified_parameters) + ';'
Binary file
Binary file
@@ -0,0 +1,56 @@
1
+ import logging
2
+ from logging.handlers import RotatingFileHandler
3
+ import os
4
+
5
+ ENABLE_LOGGING = False
6
+
7
+ def setup_logging(mode='file', log_level=logging.DEBUG):
8
+ """
9
+ Set up logging configuration.
10
+
11
+ This method configures the logging settings for the application.
12
+ It sets the log level, format, and log file location.
13
+
14
+ Args:
15
+ log_level (int): The logging level (default: logging.INFO).
16
+ """
17
+ global ENABLE_LOGGING
18
+ ENABLE_LOGGING = True
19
+
20
+ # Create a logger for mssql_python module
21
+ logger = logging.getLogger(__name__)
22
+ logger.setLevel(log_level)
23
+
24
+ # Construct the path to the log file
25
+ # TODO: Use a different dir to dump log file
26
+ current_dir = os.path.dirname(os.path.abspath(__file__))
27
+ log_file = os.path.join(current_dir, 'mssql_python_trace_{pid}.log'.format(pid=os.getpid()))
28
+
29
+ # Create a log handler to log to driver specific file
30
+ # By default we only want to log to a file, max size 500MB, and keep 5 backups
31
+ # TODO: Rotate files based on time too? Ex: everyday
32
+ file_handler = RotatingFileHandler(log_file, maxBytes=512*1024*1024, backupCount=5)
33
+ file_handler.setLevel(log_level)
34
+ formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(message)s')
35
+ file_handler.setFormatter(formatter)
36
+ logger.addHandler(file_handler)
37
+
38
+ if mode == 'stdout':
39
+ # If the mode is stdout, then we want to log to the console as well
40
+ stdout_handler = logging.StreamHandler()
41
+ stdout_handler.setLevel(log_level)
42
+ stdout_handler.setFormatter(formatter)
43
+ logger.addHandler(stdout_handler)
44
+ else:
45
+ raise ValueError(f'Invalid logging mode: {mode}')
46
+
47
+ def get_logger():
48
+ """
49
+ Get the logger instance.
50
+
51
+ Returns:
52
+ logging.Logger: The logger instance.
53
+ """
54
+ if not ENABLE_LOGGING:
55
+ return None
56
+ return logging.getLogger(__name__)
@@ -0,0 +1,177 @@
1
+ from typing import (
2
+ Final, Union
3
+ )
4
+ import datetime
5
+
6
+ # GLOBALS
7
+ # Read-Only
8
+ apilevel: Final[str] = '2.0'
9
+ paramstyle: Final[str] = 'pyformat'
10
+ threadsafety: Final[int] = 1
11
+
12
+ # Type Objects
13
+ # https://www.python.org/dev/peps/pep-0249/#type-objects
14
+ class STRING:
15
+ """
16
+ This type object is used to describe columns in a database that are string-based (e.g. CHAR).
17
+ """
18
+ def __init__(self) -> None: ...
19
+
20
+ class BINARY:
21
+ """
22
+ This type object is used to describe (long) binary columns in a database (e.g. LONG, RAW, BLOBs).
23
+ """
24
+ def __init__(self) -> None: ...
25
+
26
+ class NUMBER:
27
+ """
28
+ This type object is used to describe numeric columns in a database.
29
+ """
30
+ def __init__(self) -> None: ...
31
+
32
+ class DATETIME:
33
+ """
34
+ This type object is used to describe date/time columns in a database.
35
+ """
36
+ def __init__(self) -> None: ...
37
+
38
+ class ROWID:
39
+ """
40
+ This type object is used to describe the “Row ID” column in a database.
41
+ """
42
+ def __init__(self) -> None: ...
43
+
44
+ # Type Constructors
45
+ def Date(year: int, month: int, day: int) -> datetime.date: ...
46
+ def Time(hour: int, minute: int, second: int) -> datetime.time: ...
47
+ def Timestamp(year: int, month: int, day: int, hour: int, minute: int, second: int) -> datetime.datetime: ...
48
+ def DateFromTicks(ticks: int) -> datetime.date: ...
49
+ def TimeFromTicks(ticks: int) -> datetime.time: ...
50
+ def TimestampFromTicks(ticks: int) -> datetime.datetime: ...
51
+ def Binary(string: str) -> bytes: ...
52
+
53
+ # Exceptions
54
+ # https://www.python.org/dev/peps/pep-0249/#exceptions
55
+ class Warning(Exception): ...
56
+ class Error(Exception): ...
57
+ class InterfaceError(Error): ...
58
+ class DatabaseError(Error): ...
59
+ class DataError(DatabaseError): ...
60
+ class OperationalError(DatabaseError): ...
61
+ class IntegrityError(DatabaseError): ...
62
+ class InternalError(DatabaseError): ...
63
+ class ProgrammingError(DatabaseError): ...
64
+ class NotSupportedError(DatabaseError): ...
65
+
66
+ # Connection Objects
67
+ class Connection:
68
+ """
69
+ Connection object for interacting with the database.
70
+
71
+ https://www.python.org/dev/peps/pep-0249/#connection-objects
72
+
73
+ This class should not be instantiated directly, instead call global connect() method to
74
+ create a Connection object.
75
+ """
76
+
77
+ def cursor(self) -> 'Cursor':
78
+ """
79
+ Return a new Cursor object using the connection.
80
+ """
81
+ ...
82
+
83
+ def commit(self) -> None:
84
+ """
85
+ Commit the current transaction.
86
+ """
87
+ ...
88
+
89
+ def rollback(self) -> None:
90
+ """
91
+ Roll back the current transaction.
92
+ """
93
+ ...
94
+
95
+ def close(self) -> None:
96
+ """
97
+ Close the connection now.
98
+ """
99
+ ...
100
+
101
+ # Cursor Objects
102
+ class Cursor:
103
+ """
104
+ Cursor object for executing SQL queries and fetching results.
105
+
106
+ https://www.python.org/dev/peps/pep-0249/#cursor-objects
107
+
108
+ This class should not be instantiated directly, instead call cursor() from a Connection
109
+ object to create a Cursor object.
110
+ """
111
+
112
+ def callproc(self, procname: str, parameters: Union[None, list] = None) -> Union[None, list]:
113
+ """
114
+ Call a stored database procedure with the given name.
115
+ """
116
+ ...
117
+
118
+ def close(self) -> None:
119
+ """
120
+ Close the cursor now.
121
+ """
122
+ ...
123
+
124
+ def execute(self, operation: str, parameters: Union[None, list, dict] = None) -> None:
125
+ """
126
+ Prepare and execute a database operation (query or command).
127
+ """
128
+ ...
129
+
130
+ def executemany(self, operation: str, seq_of_parameters: list) -> None:
131
+ """
132
+ Prepare a database operation and execute it against all parameter sequences.
133
+ """
134
+ ...
135
+
136
+ def fetchone(self) -> Union[None, tuple]:
137
+ """
138
+ Fetch the next row of a query result set.
139
+ """
140
+ ...
141
+
142
+ def fetchmany(self, size: int = None) -> list:
143
+ """
144
+ Fetch the next set of rows of a query result.
145
+ """
146
+ ...
147
+
148
+ def fetchall(self) -> list:
149
+ """
150
+ Fetch all (remaining) rows of a query result.
151
+ """
152
+ ...
153
+
154
+ def nextset(self) -> Union[None, bool]:
155
+ """
156
+ Skip to the next available result set.
157
+ """
158
+ ...
159
+
160
+ def setinputsizes(self, sizes: list) -> None:
161
+ """
162
+ Predefine memory areas for the operation’s parameters.
163
+ """
164
+ ...
165
+
166
+ def setoutputsize(self, size: int, column: int = None) -> None:
167
+ """
168
+ Set a column buffer size for fetches of large columns.
169
+ """
170
+ ...
171
+
172
+ # Module Functions
173
+ def connect(connection_str: str) -> Connection:
174
+ """
175
+ Constructor for creating a connection to the database.
176
+ """
177
+ ...
Binary file