Is it expected that before_execute only has the statement and no param values on a parameterized query? When I run the code below note that the ==>BE line doesn't actually have the bind values available just the clauseelement appears to be populated.
<output> ... *==>BE ce: INSERT INTO testsqlatd.t (id, x) VALUES (?, ?), mp: (), p: {}* ... </output> If that is the case then I'm not sure how I could remove the second parameter (field 'x') if it was None. I've also taken a look at the before_cursor_execute to see if I can remove null numeric bind parameters. In this case I can see the the bind parameters (and their types) but the SQL has already been rendered which means I can't easily remove the field reference. <output> ... 2015-01-18 11:58:22,779 INFO sqlalchemy.engine.base.Engine () ==>BCE kw: {'executemany': False, 'statement': u'INSERT INTO testsqlatd.t (id, x) VALUES (?, ?)', 'parameters': (Decimal('1'), 1), 'cursor': <pyodbc.Cursor object at 0xb5944c28>, 'context': <sqlalchemy_teradata.base.TeradataExecutionContext object at 0xb595f80c>, 'conn': <sqlalchemy.engine.base.Connection object at 0xb595f7cc>} 2015-01-18 11:58:22,890 INFO sqlalchemy.engine.base.Engine INSERT INTO testsqlatd.t (id, x) VALUES (?, ?) 2015-01-18 11:58:22,890 INFO sqlalchemy.engine.base.Engine (Decimal('1'), 1) 2015-01-18 11:58:22,907 INFO sqlalchemy.engine.base.Engine COMMIT </output> Here is the code: <code> ####### End Boilerplate ######### def create_table(type_, engine): metadata = MetaData(bind=engine, schema='testsqlatd') t = Table('t', metadata, Column('id', Integer, primary_key=True), Column('x', type_)) if metadata.bind.has_table('t'): t.drop() t.create() return t def gen_insert_stmt(type_, value, engine): t = create_table(type_, engine) ins = t.insert().values(x=value).compile() return ins @event.listens_for(engine, "before_execute", named=True) def before_execute(**kw): print "==>BE ce: {}, mp: {}, p: {}".format(kw['clauseelement'], kw['multiparams'], kw['params']) @event.listens_for(engine, "before_cursor_execute", named=True) def before_cursor_execute(**kw): print "==>BCE kw: {}".format(kw) ins = gen_insert_stmt(Boolean,1 , engine) engine.connect().execute(ins) </code> I totally agree with many of the things you said about the broken driver unfortunately I have verified that the current and 3 latest versions of Teradata ODBC driver have this issue with trying to pass a None parameter to a numeric column. This includes the Windows and Unix drivers. I'm only trying to get this all to work as my company would like to move off Teradata due to support costs. Because of this though the company will not pay any additional support to TD therefore I can't get support for the TD ODBC driver, although from what I can find this bug has been in their ODBC driver since 2011 and my tests with the latest driver (15.00) show the issue is still affecting the driver, so it is unlikely they would fix the issue anyway. I wish it wasn't like this but it is so my working plan and best idea to get us off of their database is to re-write our current ETL pipeline to fork the ETL data stream using SA. One stream would continue to flow to TD and another to a more mainstream DB like PostgreSQL/Redshift. That way at some point I could simply turn off the INSERTs to Teradata and continue to enjoy the benefits of SA going forward. To get this all to work though at minimum I need to get INSERT & UPDATE working to Teradata, even with all of its warts, as well as possible. Just to restate the issue for anyone who might like to help (besides Mike B). I need to "rewrite" any parameterized INSERT statement that is attempting to insert a None value into any numeric field (Integer/Float/Real/Boolean) due to a long-standing bug in the Teradata ODBC driver. The idea is that if the field in question doesn't have a default clause and the field is explicitly being set in SA. It is understood that you could simply not reference the field in the INSERT statement but in an attempt to make this dialect relatively easy to use if possible I was hoping to catch and fix this use case. Thanks again if anyone can help. On Friday, January 16, 2015 at 5:50:50 PM UTC-8, Lycovian wrote: > > For a Teradata SA dialect I had posted an earlier question regarding > re-writing a query triggered only on Integer columns with primary_key=True. > Thanks to a comment from Mike B I figured out a workaround. Today though > I discovered thought that this issue is due to a rather nasty bug in > Teradata's ODBC implementation. The short version is that via ODBC (in my > case PyODBC and Ubuntu) you can not insert any numeric column as None. Le > sigh. > > For example here is the issue showing a table can be inserted: > # connection stuff here > t = Table('test', metadata, Column('id', sqlalchemy.Integer(), > primary_key=True), Column('x', sqlalchemy.Integer())) > ins = t.insert().values(x=2).compile() > engine.connect().execute(ins) > # success! > > ins = t.insert().values(x=None).compile() > engine.connect().execute(ins) > > ...<stack trace> > > /home/etl/build/sqlalchemy/lib/sqlalchemy/engine/default.pyc in > do_execute(self, cursor, statement, parameters, context) > 434 > 435 def do_execute(self, cursor, statement, parameters, > context=None): > --> 436 cursor.execute(statement, parameters) > 437 > 438 def do_execute_no_params(self, cursor, statement, > context=None): > > DBAPIError: (pyodbc.Error) ('HY000', '[HY000] [Teradata][ODBC Teradata > Driver][Teradata Database] The source parcel length does not match data > that was defined. (-2673) (SQLExecDirectW)') [SQL: u'INSERT INTO > testsqlatd.test (id, x) VALUES (?, ?)'] [parameters: (Decimal('2'), None)] > > This is a rather serious bug in the driver and possibly it has been fixed > in later versions of their products ODBC driver but I unfortunately don't > have access to any updates. > > I guess the question is basically the same: > *How do I remove a bindparameter in my dialect if the underlying datatype > is numeric (Float/Integer/Boolean/etc) if the statement is an Insert (and > update) and the bind value is None? * > > I've been looking at subclassing visit_insert or possibly visit_bindparam > to remove parameters that are attempting to set None any number based > parameter without success thus far. Does that sound reasonable? Anyone > have an example of removing a bind parameter from an INSERT statement based > on the bound value and the type? > > > > -- 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 http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.