Do you know if there is a downside to calling setinputsizes like that?
To put it another way, why doesn't pyodbc configure itself like that
automatically? Why do you think this belongs in SQLAlchemy rather than
pyodbc?

I suspect the answer is that most applications don't need it and there
is a downside (perhaps a performance implication?).

I've never used SQL Server, but the fact that the error message refers
to these collations as "legacy" suggests that an alternative collation
might be better.
https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver15#Supplementary_Characters
says:

    SQL Server 2019 (15.x) extends supplementary character support to
the char and varchar data types with the new UTF-8 enabled collations
(_UTF8). These data types are also capable of representing the full
Unicode character range.

If you can restrict yourself to SQL Server 2019, that might be a better option.

Simon

On Thu, Oct 15, 2020 at 10:08 AM Nicolas Lykke Iversen
<nlyk...@gmail.com> wrote:
>
> Thank you, Simon.
>
> I'm curious whether this is the way to do it in the future, or whether 
> SQLAlchemy should implement varchar(max)properly?
>
> What would the argument be for not implementing varchar(max)in the pyodbc 
> dialect?
>
> On Thursday, October 15, 2020 at 11:05:32 AM UTC+2 Simon King wrote:
>>
>> You could call 'setinputsizes' in a handler for the
>> 'before_cursor_execute' event, something like this:
>>
>>
>> from sqlalchemy import event
>>
>> @event.listens_for(SomeEngine, 'before_cursor_execute')
>> def receive_before_cursor_execute(conn, cursor, statement,
>> parameters, context, executemany):
>> cursor.setinputsizes([(pyodbc.SQL_WVARCHAR,0,0),])
>>
>>
>> https://docs.sqlalchemy.org/en/13/core/events.html#sqlalchemy.events.ConnectionEvents
>> https://docs.sqlalchemy.org/en/13/core/events.html#sqlalchemy.events.ConnectionEvents.before_cursor_execute
>>
>> Hope that helps,
>>
>> Simon
>>
>>
>> On Thu, Oct 15, 2020 at 8:27 AM Nicolas Lykke Iversen <nly...@gmail.com> 
>> wrote:
>> >
>> > Hi Mike,
>> >
>> > I have created an issue for pyodbc: 
>> > https://github.com/mkleehammer/pyodbc/issues/835#issuecomment-708930870
>> >
>> > I've gotten really good feedback there from Microsoft, and a fix has been 
>> > proposed that works:
>> >
>> > "You can try to use setinputsizes on your parameter to tell it to send as 
>> > varchar(max): cursor.setinputsizes([(pyodbc.SQL_WVARCHAR,0,0),])"
>> >
>> > I'm by no means an SQLAlchemy expert, but shouldn't the pyodbc dialect be 
>> > updated to support varchar(max)using the proposed method? If not, how can 
>> > I execute cursor.setinputsizes([(pyodbc.SQL_WVARCHAR,0,0),]) using 
>> > SQLAlchemy, so that I can make use of varchar(max)in my application?
>> >
>> > Can you recommend a hotfix for using varchar(max)in current SQLAlchemy 
>> > applications that need to handle Unicode supplementary characters (_SC)?
>> >
>> > I appreciate really appreciate your help.
>> >
>> > Best regards
>> > Nicolas
>> > On Wednesday, October 14, 2020 at 3:36:11 PM UTC+2 Mike Bayer wrote:
>> >>
>> >>
>> >>
>> >> On Wed, Oct 14, 2020, at 5:35 AM, Nicolas Lykke Iversen wrote:
>> >>
>> >> Hi Mike,
>> >>
>> >> I've now tested inserting strings with more than 2000 characters using 
>> >> Azure Data Studio (SQL Server GUI) and everything works.
>> >>
>> >> Furthermore, I've tested pyodbc (DBAPI) directly, and it only fails when 
>> >> inserting such strings using parameterised SQL queries (it succeeds 
>> >> without using parametrised queries).
>> >>
>> >>
>> >> that would be expected because all the datatype-related issues occur when 
>> >> bound parameters are passed.
>> >>
>> >>
>> >>
>> >> You can see my POC below, if you have any interest.
>> >>
>> >> I guess it should be submitted as a bug to pyodbc... Do you know if I can 
>> >> disable parametrisation for certain SQL queries in SQLAlchemy?
>> >>
>> >>
>> >> there is not and this is definitely an issue that has to be solved at the 
>> >> pyodbc level, either a bug on their end or something in your 
>> >> configuration that has to be changed.
>> >>
>> >>
>> >>
>> >>
>> >>
>> >> Best regards (and thanks for your help and support!!!)
>> >> Nicolas
>> >>
>> >> System info:
>> >> python v. 3.8.5
>> >> pyodbc v. 4.0.30
>> >> msodbcsql17 v. 17.6.1.1
>> >>
>> >> POC:
>> >> import sys
>> >> import pyodbc
>> >>
>> >> host = 'tcp:127.0.0.1,1433'
>> >> db = 'pyodbc_test'
>> >> user = 'sa'
>> >> pwd = 'P@ssw0rd'
>> >>
>> >> print('started')
>> >>
>> >> cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL 
>> >> Server};SERVER='+host+';DATABASE='+'master'+';UID='+user+';PWD='+ pwd, 
>> >> autocommit=True)
>> >> cursor = cnxn.cursor()
>> >>
>> >> try:
>> >> cursor.execute(f'CREATE DATABASE {db} COLLATE 
>> >> Latin1_General_100_CI_AS_SC')
>> >> except pyodbc.ProgrammingError as e:
>> >> pass # database exists
>> >>
>> >> cursor.execute(f'USE {db}')
>> >>
>> >> try:
>> >> cursor.execute("""
>> >> CREATE TABLE msg (
>> >> id int identity(1,1) not null,
>> >> content varchar(max) not null
>> >> );""")
>> >> except pyodbc.ProgrammingError as exc:
>> >> pass # table exists
>> >>
>> >> content = 2000 * 'A'
>> >>
>> >> cursor.execute(f"""
>> >> INSERT INTO msg (content)
>> >> VALUES ('{content}')""")
>> >> print(f'non-param: {len(content)=}: success')
>> >>
>> >> sql = f"""
>> >> INSERT INTO msg (content)
>> >> VALUES (?)"""
>> >> cursor.execute(sql, (content))
>> >> print(f'param: {len(content)=}: success')
>> >>
>> >> content = 2001 * 'A'
>> >>
>> >> cursor.execute(f"""
>> >> INSERT INTO msg (content)
>> >> VALUES ('{content}')""")
>> >> print(f'non-param: {len(content)=}: success')
>> >>
>> >> # this fails!
>> >> sql = f"""
>> >> INSERT INTO msg (content)
>> >> VALUES (?)"""
>> >> cursor.execute(sql, (content))
>> >> print(f'param: {len(content)=}: success')
>> >>
>> >>
>> >> #cursor.execute('SELECT * FROM msg')
>> >> #rows = cursor.fetchall()
>> >> #for r in rows:
>> >> # print(r)
>> >>
>> >> print('finished')
>> >>
>> >>
>> >>
>> >> On Wednesday, October 14, 2020 at 12:43:25 AM UTC+2 Mike Bayer wrote:
>> >>
>> >>
>> >>
>> >> On Tue, Oct 13, 2020, at 4:57 PM, Nicolas Lykke Iversen wrote:
>> >>
>> >> Thank you, Mike - very much appreciated!
>> >>
>> >> Just to be clear, pyodbc is not a driver, it’s a ODBC-compliant DBAPI, 
>> >> right? I separately downloaded a driver for SQL Server from Microsoft, 
>> >> which pyodbc makes use of.
>> >>
>> >>
>> >> right the pyodbc is the DBAPI in this case, which we usually refer to as 
>> >> a "driver" but in the case of ODBC the "driver" is more specifically the 
>> >> separate ODBC driver component.
>> >>
>> >>
>> >> Do you suggest that changing pyodbc to another SQL Server DPAPI would 
>> >> solve the problem?
>> >>
>> >>
>> >> I suggest that if there is no issue with the query you're running outside 
>> >> of the context of pyodbc that you submit an issue to pyodbc at 
>> >> https://github.com/mkleehammer/pyodbc/issues . However I was able to find 
>> >> a discussion thread about your error message that seemed to be 
>> >> independent of ODBC.
>> >>
>> >>
>> >>
>> >>
>> >>
>> >> If so, can you recommend another DBAPI for SQL Server? Or do you think 
>> >> that the problem is caused by Microsoft’s driver?
>> >>
>> >>
>> >> pyodbc is the only supported driver for SQL Server that exists now for 
>> >> Python. You also definitely want to use Microsoft's ODBC drivers so 
>> >> you're already there.
>> >>
>> >>
>> >>
>> >> I’m pretty sure SQL Server works fine when accessed using .NET, otherwise 
>> >> the Internet would be full of complaints regarding not being able to 
>> >> insert +2000 characters in a varchar(max).
>> >>
>> >>
>> >> you'd want to see if the same ODBC driver and options are in use in that 
>> >> scenario.
>> >>
>> >>
>> >>
>> >>
>> >>
>> >> Best regards
>> >> Nicolas
>> >>
>> >>
>> >> On Tue, 13 Oct 2020 at 22.22, Mike Bayer <mik...@zzzcomputing.com> wrote:
>> >>
>> >>
>> >>
>> >>
>> >> On Tue, Oct 13, 2020, at 10:50 AM, Nicolas Lykke Iversen wrote:
>> >>
>> >> Hi SQLAlchemy,
>> >>
>> >> System information:
>> >>
>> >> Mac OS X v. 10.15.7
>> >> Python v. 3.8.5
>> >> SQLAlchemy v. 1.3.19
>> >> MS SQL Server 2017 and 2019 (both Enterprise and Docker images e.g. 
>> >> mcr.microsoft.com/mssql/server:2019-latest)
>> >>
>> >> Problem:
>> >> I have an issue with inserting strings with a length greater than 2000 
>> >> into columns with a datatype of Text, VARCHAR, or NVARCHAR using MS SQL 
>> >> Server 2017 and 2019.
>> >>
>> >> I've checked the MS SQL Server and it creates it column properly with 
>> >> e.g. a datatype of varchar(max)for Text and VARCHAR, which should be able 
>> >> to store strings with a size up to 2 GB according to Microsoft 
>> >> documentation.
>> >>
>> >> Furthermore, I've tried using other collations, but I need _SC 
>> >> (supplementary character) support for my applications, so I cannot drop 
>> >> it, and adding _UTF8 (UTF-8) doesn't solve the problem either.
>> >>
>> >> Why am I not allowed to store strings with a size greater than 2000?
>> >>
>> >>
>> >> I'm not really sure, this has to do with a behavior of SQL Server and/or 
>> >> your ODBC driver. a google search finds fairly scant results but there is 
>> >> a long discussion regarding this error here: 
>> >> https://www.sqlservercentral.com/forums/topic/collation-error-when-adding-distributer
>> >>
>> >>
>> >>
>> >>
>> >>
>> >>
>> >>
>> >>
>> >> And why is SQLAlchemy displaying that error message, when trying to 
>> >> insert plain ASCII text ("AAAA...")?
>> >>
>> >>
>> >> SQLAlchemy runs SQL statements using a method called "cursor.execute()", 
>> >> which is a feature of the DBAPI (database driver) in use. As your error 
>> >> message indicates you're using the pyodbc driver, this method is 
>> >> documented here: 
>> >> https://github.com/mkleehammer/pyodbc/wiki/Cursor#executesql-parameters
>> >>
>> >> The DBAPI execute() method, and most of the other DBAPI methods, can 
>> >> throw exceptions if something goes wrong. SQLAlchemy has the policy that 
>> >> if it encounters such an exception when it calls cursor.execute(), it 
>> >> wraps it in a SQLAlchemy-specific version of that exception (named the 
>> >> same) and then throws it.
>> >>
>> >> In other words you're using a driver called pyodbc that's creating this 
>> >> error. SQLAlchemy just propagates it for you but otherwise has nothing to 
>> >> do with how it's produced.
>> >>
>> >>
>> >>
>> >>
>> >> I would really appreciate some guidance on how to solve this problem. 
>> >> What could be causing it?
>> >>
>> >> Error:
>> >> sqlalchemy.exc.ProgrammingError: (pyodbc.ProgrammingError) ('42000', 
>> >> "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Cannot 
>> >> convert to text/ntext or collate to 'Latin1_General_100_CI_AS_SC' because 
>> >> these legacy LOB types do not support UTF-8 or UTF-16 encodings. Use 
>> >> types varchar(max), nvarchar(max) or a collation which does not have the 
>> >> _SC or _UTF8 flags. (4189) (SQLParamData)")
>> >> [SQL: INSERT INTO msg (content) OUTPUT inserted.id VALUES (?)]
>> >> [parameters: 
>> >> ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>> >>  ... (1703 characters truncated) ... 
>> >> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',)]
>> >> (Background on this error at: http://sqlalche.me/e/13/f405)
>> >>
>> >>
>> >> Program (POC):
>> >> import logging
>> >> import sqlalchemy
>> >> from sqlalchemy import Column, Text, Integer, NVARCHAR, VARCHAR
>> >> from sqlalchemy.ext.declarative import declarative_base
>> >> from sqlalchemy.orm import sessionmaker
>> >>
>> >> logging.root.setLevel(logging.DEBUG)
>> >>
>> >> Base = declarative_base()
>> >>
>> >>
>> >> class Msg(Base):
>> >> __tablename__ = 'msg'
>> >>
>> >> id = Column(Integer, primary_key=True, autoincrement=True)
>> >> content = Column(VARCHAR, nullable=False)
>> >>
>> >>
>> >> user = 'sa'
>> >> pwd = 'P@ssw0rd'
>> >> host = 'localhost'
>> >> port = 1433
>> >> db = 'test'
>> >>
>> >> logging.info('started.')
>> >>
>> >> engine = sqlalchemy.create_engine(
>> >> f'mssql+pyodbc://{user}:{pwd}@{host}:{port}/{db}?driver=ODBC+Driver+17+for+SQL+Server',
>> >> pool_size=5,
>> >> max_overflow=10,
>> >> pool_pre_ping=True,
>> >> isolation_level='READ_UNCOMMITTED',
>> >> pool_recycle=900,
>> >> echo=False,
>> >> connect_args={'connect_timeout': 10})
>> >>
>> >> Base.metadata.create_all(engine)
>> >>
>> >> Session = sessionmaker(bind=engine, autoflush=False, autocommit=False)
>> >> session = Session()
>> >>
>> >> for i in range(10000):
>> >> try:
>> >> msg = Msg(content='A' * i)
>> >> session.add(msg)
>> >> session.commit()
>> >> except Exception as exc:
>> >> logging.exception(f'fail: {i=}: {exc}')
>> >> break
>> >> else:
>> >> logging.info(f'success: {i=}')
>> >>
>> >> logging.info('done.')
>> >>
>> >>
>> >> --
>> >> 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+...@googlegroups.com.
>> >> To view this discussion on the web visit 
>> >> https://groups.google.com/d/msgid/sqlalchemy/ca549391-4360-480e-8c58-06577f6d92dan%40googlegroups.com.
>> >>
>> >>
>> >>
>> >> --
>> >> 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 a topic in the 
>> >> Google Groups "sqlalchemy" group.
>> >> To unsubscribe from this topic, visit 
>> >> https://groups.google.com/d/topic/sqlalchemy/Kk6DkPNWlR4/unsubscribe.
>> >> To unsubscribe from this group and all its topics, send an email to 
>> >> sqlalchemy+...@googlegroups.com.
>> >> To view this discussion on the web visit 
>> >> https://groups.google.com/d/msgid/sqlalchemy/b4a135ef-1c1e-476c-a3f0-60326dca76ea%40www.fastmail.com.
>> >>
>> >>
>> >> --
>> >> 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+...@googlegroups.com.
>> >>
>> >> To view this discussion on the web visit 
>> >> https://groups.google.com/d/msgid/sqlalchemy/CAE%2B5CJABrO_459MHtDePMXah8mb67TFvQB8rUwgVgk6%2By4v-ow%40mail.gmail.com.
>> >>
>> >>
>> >>
>> >> --
>> >> 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+...@googlegroups.com.
>> >>
>> >> To view this discussion on the web visit 
>> >> https://groups.google.com/d/msgid/sqlalchemy/60f82633-887a-43a3-ac52-c5541058e0bcn%40googlegroups.com.
>> >>
>> >>
>> > --
>> > 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+...@googlegroups.com.
>> > To view this discussion on the web visit 
>> > https://groups.google.com/d/msgid/sqlalchemy/f3e2a277-8529-4fd5-83be-26445616f6c3n%40googlegroups.com.
>
> --
> 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 view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/38a5e081-6e34-42fa-bf99-3d27f445f727n%40googlegroups.com.

-- 
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/CAFHwexchgavU5U2kCLY_iSf6WB6fd1orzpS3yZX8rMjBK%3DBVrg%40mail.gmail.com.

Reply via email to