--- /dev/null
+.. change::
+ :tags: usecase, datatypes
+ :tickets: 5052
+
+ Added modified ISO-8601 rendering (i.e. ISO-8601 with the T converted to a
+ space) when using ``literal_binds`` with the SQL compilers provided by the
+ PostgreSQL, MySQL, MariaDB, MSSQL, Oracle dialects. For Oracle, the ISO
+ format is wrapped inside of an appropriate TO_DATE() function call.
+ Previously this rendering was not implemented for dialect-specific
+ compilation.
from ... import Sequence
from ... import sql
from ... import text
-from ... import types as sqltypes
from ... import util
from ...engine import cursor as _cursor
from ...engine import default
from ...sql import func
from ...sql import quoted_name
from ...sql import roles
+from ...sql import sqltypes
from ...sql import util as sql_util
from ...sql._typing import is_sql_compiler
from ...types import BIGINT
import re
import struct
+from .base import _MSDateTime
from .base import BINARY
from .base import DATETIMEOFFSET
from .base import MSDialect
return process
-class _ODBCDateTime(_ODBCDateTimeBindProcessor, sqltypes.DateTime):
+class _ODBCDateTime(_ODBCDateTimeBindProcessor, _MSDateTime):
pass
from ... import log
from ... import schema as sa_schema
from ... import sql
-from ... import types as sqltypes
from ... import util
from ...engine import default
from ...engine import reflection
from ...sql import functions
from ...sql import operators
from ...sql import roles
+from ...sql import sqltypes
from ...sql import util as sql_util
from ...sql.sqltypes import Unicode
from ...types import BINARY
import datetime
from ... import exc
-from ... import types as sqltypes
from ... import util
+from ...sql import sqltypes
class _NumericType:
__visit_name__ = "LONG"
-class DATE(sqltypes.DateTime):
+class _OracleDateLiteralRender:
+ def literal_processor(self, dialect):
+ def process(value):
+ if value is not None:
+ if getattr(value, "microsecond", None):
+ value = (
+ f"""TO_TIMESTAMP"""
+ f"""('{value.isoformat().replace("T", " ")}', """
+ """'YYYY-MM-DD HH24:MI:SS.FF')"""
+ )
+ else:
+ value = (
+ f"""TO_DATE"""
+ f"""('{value.isoformat().replace("T", " ")}', """
+ """'YYYY-MM-DD HH24:MI:SS')"""
+ )
+ return value
+
+ return process
+
+
+class DATE(_OracleDateLiteralRender, sqltypes.DateTime):
"""Provide the oracle DATE type.
This type has no special Python behavior, except that it subclasses
return other._type_affinity in (sqltypes.DateTime, sqltypes.Date)
+class _OracleDate(_OracleDateLiteralRender, sqltypes.Date):
+ pass
+
+
class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval):
__visit_name__ = "INTERVAL"
sqltypes.Boolean: _OracleBoolean,
sqltypes.Interval: INTERVAL,
sqltypes.DateTime: DATE,
+ sqltypes.Date: _OracleDate,
}
ischema_names = {
from .base import OracleDialect
from .base import OracleExecutionContext
from ... import exc
-from ... import types as sqltypes
from ... import util
from ...engine import cursor as _cursor
from ...engine import interfaces
from ...engine import processors
+from ...sql import sqltypes
from ...sql._typing import is_sql_compiler
is_number = True
-class _OracleDate(sqltypes.Date):
+class _CXOracleDate(oracle._OracleDate):
def bind_processor(self, dialect):
return None
return process
+class _CXOracleTIMESTAMP(oracle._OracleDateLiteralRender, sqltypes.TIMESTAMP):
+ pass
+
+
# TODO: the names used across CHAR / VARCHAR / NCHAR / NVARCHAR
# here are inconsistent and not very good
class _OracleChar(sqltypes.CHAR):
driver = "cx_oracle"
- colspecs = {
- sqltypes.Numeric: _OracleNumeric,
- sqltypes.Float: _OracleNumeric,
- oracle.BINARY_FLOAT: _OracleBINARY_FLOAT,
- oracle.BINARY_DOUBLE: _OracleBINARY_DOUBLE,
- sqltypes.Integer: _OracleInteger,
- oracle.NUMBER: _OracleNUMBER,
- sqltypes.Date: _OracleDate,
- sqltypes.LargeBinary: _OracleBinary,
- sqltypes.Boolean: oracle._OracleBoolean,
- sqltypes.Interval: _OracleInterval,
- oracle.INTERVAL: _OracleInterval,
- sqltypes.Text: _OracleText,
- sqltypes.String: _OracleString,
- sqltypes.UnicodeText: _OracleUnicodeTextCLOB,
- sqltypes.CHAR: _OracleChar,
- sqltypes.NCHAR: _OracleNChar,
- sqltypes.Enum: _OracleEnum,
- oracle.LONG: _OracleLong,
- oracle.RAW: _OracleRaw,
- sqltypes.Unicode: _OracleUnicodeStringCHAR,
- sqltypes.NVARCHAR: _OracleUnicodeStringNCHAR,
- oracle.NCLOB: _OracleUnicodeTextNCLOB,
- oracle.ROWID: _OracleRowid,
- }
+ colspecs = OracleDialect.colspecs
+ colspecs.update(
+ {
+ sqltypes.TIMESTAMP: _CXOracleTIMESTAMP,
+ sqltypes.Numeric: _OracleNumeric,
+ sqltypes.Float: _OracleNumeric,
+ oracle.BINARY_FLOAT: _OracleBINARY_FLOAT,
+ oracle.BINARY_DOUBLE: _OracleBINARY_DOUBLE,
+ sqltypes.Integer: _OracleInteger,
+ oracle.NUMBER: _OracleNUMBER,
+ sqltypes.Date: _CXOracleDate,
+ sqltypes.LargeBinary: _OracleBinary,
+ sqltypes.Boolean: oracle._OracleBoolean,
+ sqltypes.Interval: _OracleInterval,
+ oracle.INTERVAL: _OracleInterval,
+ sqltypes.Text: _OracleText,
+ sqltypes.String: _OracleString,
+ sqltypes.UnicodeText: _OracleUnicodeTextCLOB,
+ sqltypes.CHAR: _OracleChar,
+ sqltypes.NCHAR: _OracleNChar,
+ sqltypes.Enum: _OracleEnum,
+ oracle.LONG: _OracleLong,
+ oracle.RAW: _OracleRaw,
+ sqltypes.Unicode: _OracleUnicodeStringCHAR,
+ sqltypes.NVARCHAR: _OracleUnicodeStringNCHAR,
+ oracle.NCLOB: _OracleUnicodeTextNCLOB,
+ oracle.ROWID: _OracleRowid,
+ }
+ )
execute_sequence_format = list
from .json import JSONB
from .json import JSONPathType
from ... import exc
-from ... import types as sqltypes
from ... import util
from ...engine import processors
+from ...sql import sqltypes
from ...sql.elements import quoted_name
from .json import JSONB
from .json import JSONPathType
from ... import pool
-from ... import types as sqltypes
from ... import util
from ...engine import AdaptedConnection
+from ...sql import sqltypes
from ...util.concurrency import await_fallback
from ...util.concurrency import await_only
from .. import event
from .. import exc
from .. import pool
-from .. import types as sqltypes
from .. import util
from ..sql import compiler
from ..sql import expression
+from ..sql import type_api
from ..sql._typing import is_tuple_type
from ..sql.compiler import DDLCompiler
from ..sql.compiler import SQLCompiler
and passes on to :func:`_types.adapt_type`.
"""
- return sqltypes.adapt_type(typeobj, self.colspecs)
+ return type_api.adapt_type(typeobj, self.colspecs)
def has_index(self, connection, table_name, index_name, schema=None):
if not self.has_table(connection, table_name, schema=schema):
return connection
-class _RendersLiteral:
- def literal_processor(self, dialect):
- def process(value):
- return "'%s'" % value
-
- return process
-
-
-class _StrDateTime(_RendersLiteral, sqltypes.DateTime):
- pass
-
-
-class _StrDate(_RendersLiteral, sqltypes.Date):
- pass
-
-
-class _StrTime(_RendersLiteral, sqltypes.Time):
- pass
-
-
class StrCompileDialect(DefaultDialect):
statement_compiler = compiler.StrSQLCompiler
supports_multivalues_insert = True
supports_simple_order_by_label = True
- colspecs = {
- sqltypes.DateTime: _StrDateTime,
- sqltypes.Date: _StrDate,
- sqltypes.Time: _StrTime,
- }
-
class DefaultExecutionContext(ExecutionContext):
isinsert = False
__visit_name__ = "double"
-class DateTime(HasExpressionLookup, TypeEngine[dt.datetime]):
+class _RenderISO8601NoT:
+ def literal_processor(self, dialect):
+ def process(value):
+ if value is not None:
+ value = f"""'{value.isoformat().replace("T", " ")}'"""
+ return value
+
+ return process
+
+
+class DateTime(
+ _RenderISO8601NoT, HasExpressionLookup, TypeEngine[dt.datetime]
+):
"""A type for ``datetime.datetime()`` objects.
}
-class Date(HasExpressionLookup, TypeEngine[dt.date]):
+class Date(_RenderISO8601NoT, HasExpressionLookup, TypeEngine[dt.date]):
"""A type for ``datetime.date()`` objects."""
}
-class Time(HasExpressionLookup, TypeEngine[dt.time]):
+class Time(_RenderISO8601NoT, HasExpressionLookup, TypeEngine[dt.time]):
"""A type for ``datetime.time()`` objects."""
self.assert_compile(oracle.LONG(), "LONG")
@testing.combinations(
- (Date(), cx_oracle._OracleDate),
+ (Date(), cx_oracle._CXOracleDate),
(oracle.OracleRaw(), cx_oracle._OracleRaw),
(String(), String),
(VARCHAR(), cx_oracle._OracleString),
- (DATE(), cx_oracle._OracleDate),
+ (DATE(), cx_oracle._CXOracleDate),
(oracle.DATE(), oracle.DATE),
(String(50), cx_oracle._OracleString),
(Unicode(), cx_oracle._OracleUnicodeStringCHAR),
literal string, e.g. via the TypeEngine.literal_processor() method.
"""
-
- return fails_on_everything_except("sqlite")
+ return exclusions.open()
@property
def datetime(self):