Hi,

I'm working on the Teradata external dialect and I want to be able to 
infer, at the SQLAlchemy level, the expression type for binary operations 
(specifically arithmetic) that involve types supported by the dialect. The 
goal is to get them to match the types of binary expressions in Teradata.

For example, if we had the following Table where SMALLINT and INTEGER are 
in sqlalchemy_teradata:

table = Table('table_test', metadata,
         Column('c1', SMALLINT),
         Column('c2', INTEGER))

If we get the type from a BinaryExpression:

print(type((table.c.c1 + table.c.c2).type))

We get:

<class 'sqlalchemy.sql.sqltypes.SMALLINT'>

But if we were to reflect a table with the same operator and operands:

engine.execute(
    'CREATE Table t1 as ('
    'SELECT c1 + c2 as sum_c1_c2 from table_test)')

t = Table('t1', metadata, autoload=True)

print(type(t.columns['sum_c1_c2'].type))

We would instead get:

<class 'sqlalchemy.sql.sqltypes.INTEGER'>

Which is what we expect from Teradata. So we would like:

print(type((table.c.c1 + table.c.c2).type))

to output:

<class 'sqlalchemy.sql.sqltypes.INTEGER'>

It looks like providing your own Comparator (comparator_factory) class is 
the way to designate the resulting type of a BinaryExpression. I am able to 
override this class in the relevant data types and then define my own 
specific type inference behavior in the Comparator's _adapt_expression() 
method. I'm playing around with something basic like the following for a 
mixin for all the types implemented by the dialect (inspired by the 
Comparator in _LookupExpressionAdapter):

class _TDComparable:

    @property
    def _expression_adaptations(self):
        raise NotImplementedError()

    class Comparator(TypeEngine.Comparator):
        _blank_dict = util.immutabledict()

        def _adapt_expression(self, op, other_comparator):
            othertype = other_comparator.type.__class__
            lookup = self.type._expression_adaptations.get(
                op, self._blank_dict).get(
                othertype, self.type)
            return (op, lookup)

    comparator_factory = Comparator

and then in a derived type like VARCHAR I can do something like:

class VARCHAR(_TDComparable, sqltypes.VARCHAR):

    def __init__(self, length=None, charset=None, **kwargs):
        super(VARCHAR, self).__init__(length=length, **kwargs)
        self.charset = charset

    @property
    def _expression_adaptations(self):
        return {
            operators.concat_op: {
                CLOB: CLOB
           }
        }

As far as I can tell, this is one of the more straightforward ways to 
accomplish what I want.

I've noticed though that most of the keys and values in 
_expression_adaptations for the types defined in sqltypes seem to be 
generic types (probably because most of the underlying logic for type 
inference revolves around the more general _type_affinity?). My approach 
here is slightly different, and more specific. So maybe I should define my 
own private method/property for each of my dialect-implemented type instead 
of using _expression_adaptations? Is what I'm doing within the full support 
of SQLAlchemy? Is there a better way to accomplish what I want?

Either way, any insight into this and/or suggestions of what I could do 
would be greatly appreciated.

Thanks,
Wis

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to