:ref:`change_3740`
+ .. change:: 3959
+ :tags: bug, postgresql
+ :tickets: 3959
+
+ Added support for all possible "fields" identifiers when reflecting the
+ Postgresql ``INTERVAL`` datatype, e.g. "YEAR", "MONTH", "DAY TO
+ MINUTE", etc.. In addition, the :class:`.postgresql.INTERVAL`
+ datatype itself now includes a new parameter
+ :paramref:`.postgresql.INTERVAL.fields` where these qualifiers can be
+ specified; the qualifier is also reflected back into the resulting
+ datatype upon reflection / inspection.
+
+ .. seealso::
+
+ :ref:`change_3959`
+
.. change:: 3957
:tags: bug, sql
:tickets: 3957
Dialect Improvements and Changes - PostgreSQL
=============================================
+.. _change_3959:
+
+Support for fields specification in INTERVAL, including full reflection
+-----------------------------------------------------------------------
+
+The "fields" specifier in Postgresql's INTERVAL datatype allows specification
+of which fields of the interval to store, including such values as "YEAR",
+"MONTH", "YEAR TO MONTH", etc. The :class:`.postgresql.INTERVAL` datatype
+now allows these values to be specified::
+
+ from sqlalchemy.dialects.postgresql import INTERVAL
+
+ Table(
+ 'my_table', metadata,
+ Column("some_interval", INTERVAL(fields="DAY TO SECOND"))
+ )
+
+Additionally, all INTERVAL datatypes can now be reflected independently
+of the "fields" specifier present; the "fields" parameter in the datatype
+itself will also be present::
+
+ >>> inspect(engine).get_columns("my_table")
+ [{'comment': None,
+ 'name': u'some_interval', 'nullable': True,
+ 'default': None, 'autoincrement': False,
+ 'type': INTERVAL(fields=u'day to second')}]
+
+:ticket:`3959`
+
Dialect Improvements and Changes - MySQL
=============================================
"""
__visit_name__ = 'INTERVAL'
- def __init__(self, precision=None):
+ def __init__(self, precision=None, fields=None):
+ """Construct an INTERVAL.
+
+ :param precision: optional integer precision value
+ :param fields: string fields specifier. allows storage of fields
+ to be limited, such as ``"YEAR"``, ``"MONTH"``, ``"DAY TO HOUR"``,
+ etc.
+
+ .. versionadded:: 1.2
+
+ """
self.precision = precision
+ self.fields = fields
@classmethod
def _adapt_from_generic_interval(cls, interval):
'bytea': BYTEA,
'boolean': BOOLEAN,
'interval': INTERVAL,
- 'interval year to month': INTERVAL,
- 'interval day to second': INTERVAL,
'tsvector': TSVECTOR
}
)
def visit_INTERVAL(self, type_, **kw):
+ text = "INTERVAL"
+ if type_.fields is not None:
+ text += " " + type_.fields
if type_.precision is not None:
- return "INTERVAL(%d)" % type_.precision
- else:
- return "INTERVAL"
+ text += " (%d)" % type_.precision
+ return text
def visit_BIT(self, type_, **kw):
if type_.varying:
args = (int(charlen),)
else:
args = ()
- elif attype in ('interval', 'interval year to month',
- 'interval day to second'):
+ elif attype.startswith('interval'):
+ field_match = re.match(r'interval (.+)', attype, re.I)
if charlen:
kwargs['precision'] = int(charlen)
+ if field_match:
+ kwargs['fields'] = field_match.group(1)
+ attype = "interval"
args = ()
elif charlen:
args = (int(charlen),)
from sqlalchemy import exc
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import base as postgresql
-from sqlalchemy.dialects.postgresql import ARRAY
+from sqlalchemy.dialects.postgresql import ARRAY, INTERVAL
import re
dialect.ischema_names = dialect.ischema_names.copy()
dialect.ischema_names['my_custom_type'] = self.CustomType
self._assert_reflected(dialect)
+
+
+class IntervalReflectionTest(fixtures.TestBase):
+ __only_on__ = 'postgresql'
+ __backend__ = True
+
+ def test_interval_types(self):
+ for sym in [
+ "YEAR",
+ "MONTH",
+ "DAY",
+ "HOUR",
+ "MINUTE",
+ "SECOND",
+ "YEAR TO MONTH",
+ "DAY TO HOUR",
+ "DAY TO MINUTE",
+ "DAY TO SECOND",
+ "HOUR TO MINUTE",
+ "HOUR TO SECOND",
+ "MINUTE TO SECOND",
+ ]:
+ self._test_interval_symbol(sym)
+
+ @testing.provide_metadata
+ def _test_interval_symbol(self, sym):
+ t = Table(
+ 'i_test', self.metadata,
+ Column('id', Integer, primary_key=True),
+ Column('data1', INTERVAL(fields=sym)),
+ )
+ t.create(testing.db)
+
+ columns = {
+ rec['name']: rec
+ for rec in inspect(testing.db).get_columns("i_test")
+ }
+ assert isinstance(columns["data1"]["type"], INTERVAL)
+ eq_(columns["data1"]["type"].fields, sym.lower())
+ eq_(columns["data1"]["type"].precision, None)
+
+ @testing.provide_metadata
+ def test_interval_precision(self):
+ t = Table(
+ 'i_test', self.metadata,
+ Column('id', Integer, primary_key=True),
+ Column('data1', INTERVAL(precision=6)),
+ )
+ t.create(testing.db)
+
+ columns = {
+ rec['name']: rec
+ for rec in inspect(testing.db).get_columns("i_test")
+ }
+ assert isinstance(columns["data1"]["type"], INTERVAL)
+ eq_(columns["data1"]["type"].fields, None)
+ eq_(columns["data1"]["type"].precision, 6)
+