This commit is contained in:
2025-05-22 20:25:38 +02:00
parent 09f6750c2b
commit ce03fbf12f
529 changed files with 3353 additions and 3312 deletions

View File

@@ -6,6 +6,7 @@
# the MIT License: https://www.opensource.org/licenses/mit-license.php
# mypy: ignore-errors
from . import aioodbc # noqa
from . import base # noqa
from . import pymssql # noqa
from . import pyodbc # noqa

View File

@@ -1537,28 +1537,22 @@ class MSUUid(sqltypes.Uuid):
if self.native_uuid:
def process(value):
if value is not None:
value = f"""'{str(value).replace("''", "'")}'"""
return value
return f"""'{str(value).replace("''", "'")}'"""
return process
else:
if self.as_uuid:
def process(value):
if value is not None:
value = f"""'{value.hex}'"""
return value
return f"""'{value.hex}'"""
return process
else:
def process(value):
if value is not None:
value = f"""'{
return f"""'{
value.replace("-", "").replace("'", "''")
}'"""
return value
return process
@@ -1942,6 +1936,7 @@ class MSExecutionContext(default.DefaultExecutionContext):
row = self.cursor.fetchall()[0]
self._lastrowid = int(row[0])
self.cursor_fetch_strategy = _cursor._NO_CURSOR_DML
elif (
self.compiled is not None
and is_sql_compiler(self.compiled)
@@ -2057,6 +2052,12 @@ class MSSQLCompiler(compiler.SQLCompiler):
def visit_char_length_func(self, fn, **kw):
return "LEN%s" % self.function_argspec(fn, **kw)
def visit_aggregate_strings_func(self, fn, **kw):
expr = fn.clauses.clauses[0]._compiler_dispatch(self, **kw)
kw["literal_execute"] = True
delimeter = fn.clauses.clauses[1]._compiler_dispatch(self, **kw)
return f"string_agg({expr}, {delimeter})"
def visit_concat_op_expression_clauselist(
self, clauselist, operator, **kw
):
@@ -2119,6 +2120,7 @@ class MSSQLCompiler(compiler.SQLCompiler):
or (
# limit can use TOP with is by itself. fetch only uses TOP
# when it needs to because of PERCENT and/or WITH TIES
# TODO: Why? shouldn't we use TOP always ?
select._simple_int_clause(select._fetch_clause)
and (
select._fetch_clause_options["percent"]
@@ -2379,10 +2381,13 @@ class MSSQLCompiler(compiler.SQLCompiler):
return ""
def order_by_clause(self, select, **kw):
# MSSQL only allows ORDER BY in subqueries if there is a LIMIT
# MSSQL only allows ORDER BY in subqueries if there is a LIMIT:
# "The ORDER BY clause is invalid in views, inline functions,
# derived tables, subqueries, and common table expressions,
# unless TOP, OFFSET or FOR XML is also specified."
if (
self.is_subquery()
and not select._limit
and not self._use_top(select)
and (
select._offset is None
or not self.dialect._supports_offset_fetch

View File

@@ -211,7 +211,7 @@ class NumericSqlVariant(TypeDecorator):
cache_ok = True
def column_expression(self, colexpr):
return cast(colexpr, Numeric)
return cast(colexpr, Numeric(38, 0))
identity_columns = Table(

View File

@@ -26,7 +26,7 @@ def generate_driver_url(url, driver, query_str):
new_url = url.set(drivername="%s+%s" % (backend, driver))
if driver != "pyodbc":
if driver not in ("pyodbc", "aioodbc"):
new_url = new_url.set(query="")
if query_str:

View File

@@ -365,6 +365,7 @@ from ... import exc
from ... import types as sqltypes
from ... import util
from ...connectors.pyodbc import PyODBCConnector
from ...engine import cursor as _cursor
class _ms_numeric_pyodbc:
@@ -585,14 +586,22 @@ class MSExecutionContext_pyodbc(MSExecutionContext):
try:
# fetchall() ensures the cursor is consumed
# without closing it (FreeTDS particularly)
row = self.cursor.fetchall()[0]
break
rows = self.cursor.fetchall()
except self.dialect.dbapi.Error:
# no way around this - nextset() consumes the previous set
# so we need to just keep flipping
self.cursor.nextset()
else:
if not rows:
# async adapter drivers just return None here
self.cursor.nextset()
continue
row = rows[0]
break
self._lastrowid = int(row[0])
self.cursor_fetch_strategy = _cursor._NO_CURSOR_DML
else:
super().post_exec()