diff options
Diffstat (limited to 'lib/sqlalchemy/dialects')
| -rw-r--r-- | lib/sqlalchemy/dialects/mssql/base.py | 96 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/mysql/base.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/base.py | 27 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/cx_oracle.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/_psycopg_common.py | 19 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/asyncpg.py | 34 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 115 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/pg8000.py | 27 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/psycopg.py | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/psycopg2.py | 2 |
10 files changed, 140 insertions, 193 deletions
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index b4c620f91..4295e0ed0 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -813,7 +813,9 @@ import codecs import datetime import operator import re +from typing import overload from typing import TYPE_CHECKING +from uuid import UUID as _python_UUID from . import information_schema as ischema from .json import JSON @@ -854,6 +856,7 @@ from ...types import SMALLINT from ...types import TEXT from ...types import VARCHAR from ...util import update_wrapper +from ...util.typing import Literal if TYPE_CHECKING: from ...sql.dml import DMLState @@ -1369,9 +1372,89 @@ class SMALLMONEY(sqltypes.TypeEngine): __visit_name__ = "SMALLMONEY" -class UNIQUEIDENTIFIER(sqltypes.TypeEngine): +class MSUUid(sqltypes.Uuid): + def bind_processor(self, dialect): + if self.native_uuid: + # this is currently assuming pyodbc; might not work for + # some other mssql driver + return None + else: + if self.as_uuid: + + def process(value): + if value is not None: + value = value.hex + return value + + return process + else: + + def process(value): + if value is not None: + value = value.replace("-", "").replace("''", "'") + return value + + return process + + def literal_processor(self, dialect): + if self.native_uuid: + + def process(value): + if value is not None: + value = f"""'{str(value).replace("''", "'")}'""" + return value + + return process + else: + if self.as_uuid: + + def process(value): + if value is not None: + value = f"""'{value.hex}'""" + return value + + return process + else: + + def process(value): + if value is not None: + value = f"""'{ + value.replace("-", "").replace("'", "''") + }'""" + return value + + return process + + +class UNIQUEIDENTIFIER(sqltypes.Uuid[sqltypes._UUID_RETURN]): __visit_name__ = "UNIQUEIDENTIFIER" + @overload + def __init__( + self: "UNIQUEIDENTIFIER[_python_UUID]", as_uuid: Literal[True] = ... + ): + ... + + @overload + def __init__(self: "UNIQUEIDENTIFIER[str]", as_uuid: Literal[False] = ...): + ... + + def __init__(self, as_uuid: bool = True): + """Construct a :class:`_mssql.UNIQUEIDENTIFIER` type. + + + :param as_uuid=True: if True, values will be interpreted + as Python uuid objects, converting to/from string via the + DBAPI. + + .. versionchanged: 2.0 Added direct "uuid" support to the + :class:`_mssql.UNIQUEIDENTIFIER` datatype; uuid interpretation + defaults to ``True``. + + """ + self.as_uuid = as_uuid + self.native_uuid = True + class SQL_VARIANT(sqltypes.TypeEngine): __visit_name__ = "SQL_VARIANT" @@ -1619,6 +1702,12 @@ class MSTypeCompiler(compiler.GenericTypeCompiler): def visit_SMALLMONEY(self, type_, **kw): return "SMALLMONEY" + def visit_uuid(self, type_, **kw): + if type_.native_uuid: + return self.visit_UNIQUEIDENTIFIER(type_, **kw) + else: + return super().visit_uuid(type_, **kw) + def visit_UNIQUEIDENTIFIER(self, type_, **kw): return "UNIQUEIDENTIFIER" @@ -2709,6 +2798,10 @@ class MSDialect(default.DefaultDialect): supports_statement_cache = True supports_default_values = True supports_empty_insert = False + + # supports_native_uuid is partial here, so we implement our + # own impl type + execution_ctx_cls = MSExecutionContext use_scope_identity = True max_identifier_length = 128 @@ -2730,6 +2823,7 @@ class MSDialect(default.DefaultDialect): DATETIME2: DATETIME2, SMALLDATETIME: SMALLDATETIME, DATETIME: DATETIME, + sqltypes.Uuid: MSUUid, } engine_config_types = default.DefaultDialect.engine_config_types.union( diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 65de88cfe..b585ea992 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -2248,6 +2248,9 @@ class MySQLTypeCompiler(compiler.GenericTypeCompiler): else: return self._extend_string(type_, {"national": True}, "CHAR") + def visit_UUID(self, type_, **kw): + return "UUID" + def visit_VARBINARY(self, type_, **kw): return "VARBINARY(%d)" % type_.length diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index 55d5a4fb5..37b81e1dd 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -690,7 +690,7 @@ class LONG(sqltypes.Text): class _OracleDateLiteralRender: - def literal_processor(self, dialect): + def _literal_processor_datetime(self, dialect): def process(value): if value is not None: if getattr(value, "microsecond", None): @@ -709,6 +709,25 @@ class _OracleDateLiteralRender: return process + def _literal_processor_date(self, dialect): + def process(value): + if value is not None: + if getattr(value, "microsecond", None): + value = ( + f"""TO_TIMESTAMP""" + f"""('{value.isoformat().split("T")[0]}', """ + """'YYYY-MM-DD')""" + ) + else: + value = ( + f"""TO_DATE""" + f"""('{value.isoformat().split("T")[0]}', """ + """'YYYY-MM-DD')""" + ) + return value + + return process + class DATE(_OracleDateLiteralRender, sqltypes.DateTime): """Provide the oracle DATE type. @@ -723,12 +742,16 @@ class DATE(_OracleDateLiteralRender, sqltypes.DateTime): __visit_name__ = "DATE" + def literal_processor(self, dialect): + return self._literal_processor_datetime(dialect) + def _compare_type_affinity(self, other): return other._type_affinity in (sqltypes.DateTime, sqltypes.Date) class _OracleDate(_OracleDateLiteralRender, sqltypes.Date): - pass + def literal_processor(self, dialect): + return self._literal_processor_date(dialect) class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval): diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index 290789f32..fbac8b93e 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -580,7 +580,8 @@ class _CXOracleDate(oracle._OracleDate): class _CXOracleTIMESTAMP(oracle._OracleDateLiteralRender, sqltypes.TIMESTAMP): - pass + def literal_processor(self, dialect): + return self._literal_processor_datetime(dialect) # TODO: the names used across CHAR / VARCHAR / NCHAR / NVARCHAR diff --git a/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py b/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py index 6b8b3f6d0..e831f2ed9 100644 --- a/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py +++ b/lib/sqlalchemy/dialects/postgresql/_psycopg_common.py @@ -8,7 +8,6 @@ from .base import _FLOAT_TYPES from .base import _INT_TYPES from .base import PGDialect from .base import PGExecutionContext -from .base import UUID from .hstore import HSTORE from ... import exc from ... import types as sqltypes @@ -63,21 +62,6 @@ class _PsycopgHStore(HSTORE): ) -class _PsycopgUUID(UUID): - def bind_processor(self, dialect): - return None - - def result_processor(self, dialect, coltype): - if not self.as_uuid and dialect.use_native_uuid: - - def process(value): - if value is not None: - value = str(value) - return value - - return process - - class _PsycopgARRAY(PGARRAY): render_bind_cast = True @@ -106,7 +90,6 @@ class _PGDialect_common_psycopg(PGDialect): { sqltypes.Numeric: _PsycopgNumeric, HSTORE: _PsycopgHStore, - UUID: _PsycopgUUID, sqltypes.ARRAY: _PsycopgARRAY, }, ) @@ -115,14 +98,12 @@ class _PGDialect_common_psycopg(PGDialect): self, client_encoding=None, use_native_hstore=True, - use_native_uuid=True, **kwargs, ): PGDialect.__init__(self, **kwargs) if not use_native_hstore: self._has_native_hstore = False self.use_native_hstore = use_native_hstore - self.use_native_uuid = use_native_uuid self.client_encoding = client_encoding def create_connect_args(self, url): diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py index d320c323c..1ec787e1f 100644 --- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py +++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py @@ -138,7 +138,6 @@ from .base import PGDialect from .base import PGExecutionContext from .base import PGIdentifierPreparer from .base import REGCLASS -from .base import UUID from ... import exc from ... import pool from ... import util @@ -150,12 +149,6 @@ from ...util.concurrency import await_fallback from ...util.concurrency import await_only -try: - from uuid import UUID as _python_UUID # noqa -except ImportError: - _python_UUID = None - - class AsyncpgString(sqltypes.String): render_bind_cast = True @@ -237,30 +230,6 @@ class AsyncpgJSONPathType(json.JSONPathType): return process -class AsyncpgUUID(UUID): - render_bind_cast = True - - def bind_processor(self, dialect): - if not self.as_uuid and dialect.use_native_uuid: - - def process(value): - if value is not None: - value = _python_UUID(value) - return value - - return process - - def result_processor(self, dialect, coltype): - if not self.as_uuid and dialect.use_native_uuid: - - def process(value): - if value is not None: - value = str(value) - return value - - return process - - class AsyncpgNumeric(sqltypes.Numeric): render_bind_cast = True @@ -831,8 +800,6 @@ class PGDialect_asyncpg(PGDialect): statement_compiler = PGCompiler_asyncpg preparer = PGIdentifierPreparer_asyncpg - use_native_uuid = True - colspecs = util.update_copy( PGDialect.colspecs, { @@ -842,7 +809,6 @@ class PGDialect_asyncpg(PGDialect): sqltypes.DateTime: AsyncpgDateTime, sqltypes.Interval: AsyncPgInterval, INTERVAL: AsyncPgInterval, - UUID: AsyncpgUUID, sqltypes.Boolean: AsyncpgBoolean, sqltypes.Integer: AsyncpgInteger, sqltypes.BigInteger: AsyncpgBigInteger, diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 0aeeb806b..146e59c4d 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -1453,9 +1453,6 @@ from collections import defaultdict import datetime as dt import re from typing import Any -from typing import overload -from typing import TypeVar -from uuid import UUID as _python_UUID from . import array as _array from . import dml @@ -1489,8 +1486,8 @@ from ...types import NUMERIC from ...types import REAL from ...types import SMALLINT from ...types import TEXT +from ...types import UUID as UUID from ...types import VARCHAR -from ...util.typing import Literal IDX_USING = re.compile(r"^(?:btree|hash|gist|gin|[\w_]+)$", re.I) @@ -1606,6 +1603,11 @@ _FLOAT_TYPES = (700, 701, 1021, 1022) _INT_TYPES = (20, 21, 23, 26, 1005, 1007, 1016) +class PGUuid(UUID): + render_bind_cast = True + render_literal_cast = True + + class BYTEA(sqltypes.LargeBinary[bytes]): __visit_name__ = "BYTEA" @@ -1765,103 +1767,6 @@ class BIT(sqltypes.TypeEngine[int]): PGBit = BIT -_UUID_RETURN = TypeVar("_UUID_RETURN", str, _python_UUID) - - -class UUID(sqltypes.TypeEngine[_UUID_RETURN]): - - """PostgreSQL UUID type. - - Represents the UUID column type, interpreting - data either as natively returned by the DBAPI - or as Python uuid objects. - - The UUID type is currently known to work within the prominent DBAPI - drivers supported by SQLAlchemy including psycopg, psycopg2, pg8000 and - asyncpg. Support for other DBAPI drivers may be incomplete or non-present. - - """ - - __visit_name__ = "UUID" - - @overload - def __init__(self: "UUID[_python_UUID]", as_uuid: Literal[True] = ...): - ... - - @overload - def __init__(self: "UUID[str]", as_uuid: Literal[False] = ...): - ... - - def __init__(self, as_uuid: bool = True): - """Construct a UUID type. - - - :param as_uuid=True: if True, values will be interpreted - as Python uuid objects, converting to/from string via the - DBAPI. - - .. versionchanged: 2 ``as_uuid`` now defaults to ``True``. - - """ - self.as_uuid = as_uuid - - def coerce_compared_value(self, op, value): - """See :meth:`.TypeEngine.coerce_compared_value` for a description.""" - - if isinstance(value, str): - return self - else: - return super(UUID, self).coerce_compared_value(op, value) - - def bind_processor(self, dialect): - if self.as_uuid: - - def process(value): - if value is not None: - value = str(value) - return value - - return process - else: - return None - - def result_processor(self, dialect, coltype): - if self.as_uuid: - - def process(value): - if value is not None: - value = _python_UUID(value) - return value - - return process - else: - return None - - def literal_processor(self, dialect): - if self.as_uuid: - - def process(value): - if value is not None: - value = "'%s'::UUID" % value - return value - - return process - else: - - def process(value): - if value is not None: - value = "'%s'" % value - return value - - return process - - @property - def python_type(self): - return _python_UUID if self.as_uuid else str - - -PGUuid = UUID - class TSVECTOR(sqltypes.TypeEngine[Any]): @@ -2162,6 +2067,7 @@ colspecs = { sqltypes.Enum: ENUM, sqltypes.JSON.JSONPathType: _json.JSONPathType, sqltypes.JSON: _json.JSON, + UUID: PGUuid, } ischema_names = { @@ -3043,6 +2949,12 @@ class PGTypeCompiler(compiler.GenericTypeCompiler): compiled = "BIT(%d)" % type_.length return compiled + def visit_uuid(self, type_, **kw): + if type_.native_uuid: + return self.visit_UUID(type_, **kw) + else: + return super().visit_uuid(type_, **kw) + def visit_UUID(self, type_, **kw): return "UUID" @@ -3267,6 +3179,7 @@ class PGDialect(default.DefaultDialect): supports_native_enum = True supports_native_boolean = True + supports_native_uuid = True supports_smallserial = True supports_sequences = True diff --git a/lib/sqlalchemy/dialects/postgresql/pg8000.py b/lib/sqlalchemy/dialects/postgresql/pg8000.py index fbed3a464..6cb97ece4 100644 --- a/lib/sqlalchemy/dialects/postgresql/pg8000.py +++ b/lib/sqlalchemy/dialects/postgresql/pg8000.py @@ -93,7 +93,6 @@ of the :ref:`psycopg2 <psycopg2_isolation_level>` dialect: """ # noqa import decimal import re -from uuid import UUID as _python_UUID from .array import ARRAY as PGARRAY from .base import _DECIMAL_TYPES @@ -105,7 +104,6 @@ from .base import PGCompiler from .base import PGDialect from .base import PGExecutionContext from .base import PGIdentifierPreparer -from .base import UUID from .json import JSON from .json import JSONB from .json import JSONPathType @@ -195,30 +193,6 @@ class _PGJSONPathType(JSONPathType): # DBAPI type 1009 -class _PGUUID(UUID): - render_bind_cast = True - - def bind_processor(self, dialect): - if not self.as_uuid: - - def process(value): - if value is not None: - value = _python_UUID(value) - return value - - return process - - def result_processor(self, dialect, coltype): - if not self.as_uuid: - - def process(value): - if value is not None: - value = str(value) - return value - - return process - - class _PGEnum(ENUM): def get_dbapi_type(self, dbapi): return dbapi.UNKNOWN @@ -391,7 +365,6 @@ class PGDialect_pg8000(PGDialect): sqltypes.JSON.JSONIndexType: _PGJSONIndexType, sqltypes.JSON.JSONIntIndexType: _PGJSONIntIndexType, sqltypes.JSON.JSONStrIndexType: _PGJSONStrIndexType, - UUID: _PGUUID, sqltypes.Interval: _PGInterval, INTERVAL: _PGInterval, sqltypes.DateTime: _PGTimeStamp, diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg.py b/lib/sqlalchemy/dialects/postgresql/psycopg.py index 634cea38a..7ec26cb4e 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg.py @@ -62,11 +62,9 @@ import re from ._psycopg_common import _PGDialect_common_psycopg from ._psycopg_common import _PGExecutionContext_common_psycopg -from ._psycopg_common import _PsycopgUUID from .base import INTERVAL from .base import PGCompiler from .base import PGIdentifierPreparer -from .base import UUID from .json import JSON from .json import JSONB from .json import JSONPathType @@ -120,10 +118,6 @@ class _PGJSONPathType(JSONPathType): pass -class _PGUUID(_PsycopgUUID): - render_bind_cast = True - - class _PGInterval(INTERVAL): render_bind_cast = True @@ -201,7 +195,6 @@ class PGDialect_psycopg(_PGDialect_common_psycopg): sqltypes.JSON.JSONPathType: _PGJSONPathType, sqltypes.JSON.JSONIntIndexType: _PGJSONIntIndexType, sqltypes.JSON.JSONStrIndexType: _PGJSONStrIndexType, - UUID: _PGUUID, sqltypes.Interval: _PGInterval, INTERVAL: _PGInterval, sqltypes.Date: _PGDate, diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index f7d1942a0..f5d84a5a3 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -676,7 +676,7 @@ class PGDialect_psycopg2(_PGDialect_common_psycopg): fns.append(on_connect) - if self.dbapi and self.use_native_uuid: + if self.dbapi: def on_connect(dbapi_conn): extras.register_uuid(None, dbapi_conn) |
