From df421724c974dc10800540ea22aa6d8039278d08 Mon Sep 17 00:00:00 2001 From: Michal Franczel Date: Fri, 16 May 2025 11:52:24 +0200 Subject: [PATCH 1/3] fix: disable backslash escapes --- sqlalchemy_redshift/dialect.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sqlalchemy_redshift/dialect.py b/sqlalchemy_redshift/dialect.py index c2328316..5291fdbb 100644 --- a/sqlalchemy_redshift/dialect.py +++ b/sqlalchemy_redshift/dialect.py @@ -1240,6 +1240,9 @@ class RedshiftDialect_psycopg2( ): supports_statement_cache = False + def _set_backslash_escapes(self, connection): + self._backslash_escapes = "off" + # Add RedshiftDialect synonym for backwards compatibility. RedshiftDialect = RedshiftDialect_psycopg2 @@ -1250,6 +1253,9 @@ class RedshiftDialect_psycopg2cffi( ): supports_statement_cache = False + def _set_backslash_escapes(self, connection): + self._backslash_escapes = "off" + class RedshiftDialect_redshift_connector(RedshiftDialectMixin, PGDialect): From 4064d6e3da1f785f4990f860b9c64dd572977a44 Mon Sep 17 00:00:00 2001 From: Michal Franczel Date: Wed, 21 May 2025 19:47:48 +0200 Subject: [PATCH 2/3] fix: support --- CHANGES.rst | 2 +- setup.py | 4 +++- sqlalchemy_redshift/commands.py | 9 +++++---- sqlalchemy_redshift/dialect.py | 17 +++++++++++------ tox.ini | 4 ++++ 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3eea33d0..48e076c2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ 0.8.15 (unreleased) ------------------- -- Nothing changed yet. +- Fix SQLAlchemy V2 support (https://github.com/sqlalchemy-redshift/sqlalchemy-redshift/pull/319). 0.8.14 (2023-04-07) diff --git a/setup.py b/setup.py index b8604f28..5e133467 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='sqlalchemy-redshift', - version='0.8.16.dev0', + version='0.8.15.dev1', description='Amazon Redshift Dialect for sqlalchemy', long_description=readme + '\n\n' + history, long_description_content_type='text/x-rst', @@ -39,6 +39,8 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], entry_points={ 'sqlalchemy.dialects': [ diff --git a/sqlalchemy_redshift/commands.py b/sqlalchemy_redshift/commands.py index a3590d78..af7754da 100644 --- a/sqlalchemy_redshift/commands.py +++ b/sqlalchemy_redshift/commands.py @@ -8,6 +8,7 @@ from collections import Iterable import sqlalchemy as sa +from sqlalchemy.sql import text from sqlalchemy import exc as sa_exc from sqlalchemy.ext import compiler as sa_compiler from sqlalchemy.sql import expression as sa_expression @@ -188,7 +189,7 @@ def visit_alter_table_append_command(element, compiler, **kw): source=compiler.preparer.format_table(element.source), fill_option=fill_option, ) - return compiler.process(sa.text(query_text), **kw) + return compiler.process(text(query_text), **kw) class UnloadFromSelect(_ExecutableClause): @@ -385,7 +386,7 @@ def visit_unload_from_select(element, compiler, **kw): ), ) - query = sa.text(qs) + query = text(qs) if el.delimiter is not None: query = query.bindparams(sa.bindparam( @@ -895,7 +896,7 @@ def visit_copy_command(element, compiler, **kw): parameters='\n'.join(parameters) ) - return compiler.process(sa.text(qs).bindparams(*bindparams), **kw) + return compiler.process(text(qs).bindparams(*bindparams), **kw) class CreateLibraryCommand(_ExecutableClause): @@ -995,7 +996,7 @@ def visit_create_library_command(element, compiler, **kw): query = query.format(name=quoted_lib_name, or_replace='OR REPLACE' if element.replace else '', region='REGION :region' if element.region else '') - return compiler.process(sa.text(query).bindparams(*bindparams), **kw) + return compiler.process(text(query).bindparams(*bindparams), **kw) class RefreshMaterializedView(_ExecutableClause): diff --git a/sqlalchemy_redshift/dialect.py b/sqlalchemy_redshift/dialect.py index 5291fdbb..a12128fb 100644 --- a/sqlalchemy_redshift/dialect.py +++ b/sqlalchemy_redshift/dialect.py @@ -6,6 +6,7 @@ import pkg_resources import sqlalchemy as sa +from sqlalchemy.sql import text from packaging.version import Version from sqlalchemy import inspect from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION @@ -753,7 +754,7 @@ def get_check_constraints(self, connection, table_name, schema=None, **kw): ) table_oid = 'NULL' if not table_oid else table_oid - result = connection.execute(sa.text(""" + result = connection.execute(text(""" SELECT cons.conname as name, pg_get_constraintdef(cons.oid) as src @@ -796,7 +797,7 @@ def get_table_oid(self, connection, table_name, schema=None, **kw): schema_field = '"{schema}".'.format(schema=schema) if schema else "" result = connection.execute( - sa.text( + text( """ select '{schema_field}"{table_name}"'::regclass::oid; """.format( @@ -894,7 +895,7 @@ def get_view_definition(self, connection, view_name, schema=None, **kw): :meth:`~sqlalchemy.engine.interfaces.Dialect.get_view_definition`. """ view = self._get_redshift_relation(connection, view_name, schema, **kw) - return sa.text(view.view_definition) + return text(view.view_definition) def get_indexes(self, connection, table_name, schema, **kw): """ @@ -1060,7 +1061,7 @@ def _get_all_relation_info(self, connection, **kw): ) if table_name else "" ) - result = connection.execute(sa.text(""" + result = connection.execute(text(""" SELECT c.relkind, n.oid as "schema_oid", @@ -1122,7 +1123,7 @@ def _get_schema_column_info(self, connection, **kw): ) all_columns = defaultdict(list) - result = connection.execute(sa.text(REFLECTION_SQL.format( + result = connection.execute(text(REFLECTION_SQL.format( schema_clause=schema_clause, table_clause=table_clause ))) @@ -1147,7 +1148,7 @@ def _get_all_constraint_info(self, connection, **kw): ) if table_name else "" ) - result = connection.execute(sa.text(""" + result = connection.execute(text(""" SELECT n.nspname as "schema", c.relname as "table_name", @@ -1434,6 +1435,10 @@ def create_connect_args(self, *args, **kwargs): default_args.update(cparams) return cargs, default_args + def _set_backslash_escapes(self, connection): + # Redshift doesn’t implement SHOW standard_conforming_strings + self._backslash_escapes = "off" + def gen_columns_from_children(root): """ diff --git a/tox.ini b/tox.ini index b24a463b..4d30f448 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,9 @@ envlist = py39-pg28-sa14 py310-pg28-sa13 py310-pg28-sa14 + py310-pg28-sa20 + py311-pg28-sa20 + py312-pg28-sa20 lint docs @@ -13,6 +16,7 @@ passenv = PGPASSWORD,REDSHIFT_USERNAME,REDSHIFT_HOST,REDSHIFT_PORT,REDSHIFT_DATA deps = sa13: sqlalchemy==1.3.24 sa14: sqlalchemy==1.4.15 + sa20: sqlalchemy==2.0.23 pg28: psycopg2==2.8.6 pg29: psycopg2==2.9.5 alembic==1.9.2 From 90940c42b44cb27212cd9a19cbab1d09c8e7c4a2 Mon Sep 17 00:00:00 2001 From: Michal Franczel Date: Thu, 22 May 2025 13:39:03 +0200 Subject: [PATCH 3/3] remove dev1 from version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5e133467..ef5c2618 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='sqlalchemy-redshift', - version='0.8.15.dev1', + version='0.8.15', description='Amazon Redshift Dialect for sqlalchemy', long_description=readme + '\n\n' + history, long_description_content_type='text/x-rst',