From 955e2945b4e2fe5cede5f6bb3f21382618104a32 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Sun, 9 Oct 2022 19:59:10 +0100 Subject: [PATCH 01/12] Default database parameter --- sqlalchemy_utils/functions/database.py | 173 ++++++++++++------------- 1 file changed, 84 insertions(+), 89 deletions(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index d20618e3..2bd1e73e 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -11,7 +11,7 @@ from .orm import quote -def escape_like(string, escape_char='*'): +def escape_like(string, escape_char="*"): """ Escape the string paremeter used in SQL LIKE expressions. @@ -29,10 +29,9 @@ def escape_like(string, escape_char='*'): :param escape_char: escape character """ return ( - string - .replace(escape_char, escape_char * 2) - .replace('%', escape_char + '%') - .replace('_', escape_char + '_') + string.replace(escape_char, escape_char * 2) + .replace("%", escape_char + "%") + .replace("_", escape_char + "_") ) @@ -82,6 +81,7 @@ def json_sql(value, scalars_to_json=True): """ scalar_convert = sa.text if scalars_to_json: + def scalar_convert(a): return sa.func.to_json(sa.text(a)) @@ -96,10 +96,7 @@ def scalar_convert(a): return scalar_convert("'{0}'".format(value)) elif isinstance(value, Sequence): return sa.func.json_build_array( - *( - json_sql(v, scalars_to_json=False) - for v in value - ) + *(json_sql(v, scalars_to_json=False) for v in value) ) elif isinstance(value, (int, float)): return scalar_convert(str(value)) @@ -154,6 +151,7 @@ def jsonb_sql(value, scalars_to_jsonb=True): """ scalar_convert = sa.text if scalars_to_jsonb: + def scalar_convert(a): return sa.func.to_jsonb(sa.text(a)) @@ -168,10 +166,7 @@ def scalar_convert(a): return scalar_convert("'{0}'".format(value)) elif isinstance(value, Sequence): return sa.func.jsonb_build_array( - *( - jsonb_sql(v, scalars_to_jsonb=False) - for v in value - ) + *(jsonb_sql(v, scalars_to_jsonb=False) for v in value) ) elif isinstance(value, (int, float)): return scalar_convert(str(value)) @@ -275,8 +270,8 @@ class Article(Base): table = column_or_constraint.table if not isinstance(table, sa.Table): raise TypeError( - 'Only columns belonging to Table objects are supported. Given ' - 'column belongs to %r.' % table + "Only columns belonging to Table objects are supported. Given " + "column belongs to %r." % table ) primary_keys = table.primary_key.columns.values() if isinstance(column_or_constraint, sa.ForeignKeyConstraint): @@ -284,12 +279,8 @@ class Article(Base): else: columns = [column_or_constraint] - return ( - (primary_keys and starts_with(primary_keys, columns)) or - any( - starts_with(index.columns.values(), columns) - for index in table.indexes - ) + return (primary_keys and starts_with(primary_keys, columns)) or any( + starts_with(index.columns.values(), columns) for index in table.indexes ) @@ -375,8 +366,8 @@ class Article(Base): table = column_or_constraint.table if not isinstance(table, sa.Table): raise TypeError( - 'Only columns belonging to Table objects are supported. Given ' - 'column belongs to %r.' % table + "Only columns belonging to Table objects are supported. Given " + "column belongs to %r." % table ) primary_keys = list(table.primary_key.columns.values()) if isinstance(column_or_constraint, sa.ForeignKeyConstraint): @@ -385,13 +376,13 @@ class Article(Base): columns = [column_or_constraint] return ( - (columns == primary_keys) or - any( + (columns == primary_keys) + or any( columns == list(constraint.columns.values()) for constraint in table.constraints if isinstance(constraint, sa.sql.schema.UniqueConstraint) - ) or - any( + ) + or any( columns == list(index.columns.values()) for index in table.indexes if index.unique @@ -407,16 +398,12 @@ def is_auto_assigned_date_column(column): :param column: SQLAlchemy Column object """ return ( - ( - isinstance(column.type, sa.DateTime) or - isinstance(column.type, sa.Date) - ) and - ( - column.default or - column.server_default or - column.onupdate or - column.server_onupdate - ) + isinstance(column.type, sa.DateTime) or isinstance(column.type, sa.Date) + ) and ( + column.default + or column.server_default + or column.onupdate + or column.server_onupdate ) @@ -427,7 +414,7 @@ def _set_url_database(url: sa.engine.url.URL, database): :param database: New database to set. """ - if hasattr(url, '_replace'): + if hasattr(url, "_replace"): # Cannot use URL.set() as database may need to be set to None. ret = url._replace(database=database) else: # SQLAlchemy <1.4 @@ -447,10 +434,10 @@ def _sqlite_file_exists(database): if not os.path.isfile(database) or os.path.getsize(database) < 100: return False - with open(database, 'rb') as f: + with open(database, "rb") as f: header = f.read(100) - return header[:16] == b'SQLite format 3\x00' + return header[:16] == b"SQLite format 3\x00" def database_exists(url): @@ -479,9 +466,9 @@ def database_exists(url): dialect_name = url.get_dialect().name engine = None try: - if dialect_name == 'postgresql': + if dialect_name == "postgresql": text = "SELECT 1 FROM pg_database WHERE datname='%s'" % database - for db in (database, 'postgres', 'template1', 'template0', None): + for db in (database, "postgres", "template1", "template0", None): url = _set_url_database(url, database=db) engine = sa.create_engine(url) try: @@ -490,24 +477,26 @@ def database_exists(url): pass return False - elif dialect_name == 'mysql': + elif dialect_name == "mysql": url = _set_url_database(url, database=None) engine = sa.create_engine(url) - text = ("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA " - "WHERE SCHEMA_NAME = '%s'" % database) + text = ( + "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA " + "WHERE SCHEMA_NAME = '%s'" % database + ) return bool(_get_scalar_result(engine, sa.text(text))) - elif dialect_name == 'sqlite': + elif dialect_name == "sqlite": url = _set_url_database(url, database=None) engine = sa.create_engine(url) if database: - return database == ':memory:' or _sqlite_file_exists(database) + return database == ":memory:" or _sqlite_file_exists(database) else: # The default SQLAlchemy database is in memory, and :memory: is # not required, thus we should support that use case. return True else: - text = 'SELECT 1' + text = "SELECT 1" try: engine = sa.create_engine(url) return bool(_get_scalar_result(engine, sa.text(text))) @@ -518,7 +507,7 @@ def database_exists(url): engine.dispose() -def create_database(url, encoding='utf8', template=None): +def create_database(url, encoding="utf8", template=None, default_db=None): """Issue the appropriate CREATE DATABASE statement. :param url: A SQLAlchemy engine URL. @@ -545,51 +534,52 @@ def create_database(url, encoding='utf8', template=None): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if dialect_name == 'postgresql': - url = _set_url_database(url, database="postgres") - elif dialect_name == 'mssql': - url = _set_url_database(url, database="master") - elif dialect_name == 'cockroachdb': - url = _set_url_database(url, database="defaultdb") - elif not dialect_name == 'sqlite': - url = _set_url_database(url, database=None) + if default_db != None: + if dialect_name == "postgresql": + url = _set_url_database(url, database="postgres") + elif dialect_name == "mssql": + url = _set_url_database(url, database="master") + elif dialect_name == "cockroachdb": + url = _set_url_database(url, database="defaultdb") + elif not dialect_name == "sqlite": + url = _set_url_database(url, database=None) + else: + url = _set_url_database(url, database=default_db) - if (dialect_name == 'mssql' and dialect_driver in {'pymssql', 'pyodbc'}) \ - or (dialect_name == 'postgresql' and dialect_driver in { - 'asyncpg', 'pg8000', 'psycopg2', 'psycopg2cffi'}): - engine = sa.create_engine(url, isolation_level='AUTOCOMMIT') + if (dialect_name == "mssql" and dialect_driver in {"pymssql", "pyodbc"}) or ( + dialect_name == "postgresql" + and dialect_driver in {"asyncpg", "pg8000", "psycopg2", "psycopg2cffi"} + ): + engine = sa.create_engine(url, isolation_level="AUTOCOMMIT") else: engine = sa.create_engine(url) - if dialect_name == 'postgresql': + if dialect_name == "postgresql": if not template: - template = 'template1' + template = "template1" text = "CREATE DATABASE {0} ENCODING '{1}' TEMPLATE {2}".format( - quote(engine, database), - encoding, - quote(engine, template) + quote(engine, database), encoding, quote(engine, template) ) with engine.begin() as connection: connection.execute(sa.text(text)) - elif dialect_name == 'mysql': + elif dialect_name == "mysql": text = "CREATE DATABASE {0} CHARACTER SET = '{1}'".format( - quote(engine, database), - encoding + quote(engine, database), encoding ) with engine.begin() as connection: connection.execute(sa.text(text)) - elif dialect_name == 'sqlite' and database != ':memory:': + elif dialect_name == "sqlite" and database != ":memory:": if database: with engine.begin() as connection: connection.execute(sa.text("CREATE TABLE DB(id int);")) connection.execute(sa.text("DROP TABLE DB;")) else: - text = 'CREATE DATABASE {0}'.format(quote(engine, database)) + text = "CREATE DATABASE {0}".format(quote(engine, database)) with engine.begin() as connection: connection.execute(sa.text(text)) @@ -614,46 +604,51 @@ def drop_database(url): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if dialect_name == 'postgresql': + if dialect_name == "postgresql": url = _set_url_database(url, database="postgres") - elif dialect_name == 'mssql': + elif dialect_name == "mssql": url = _set_url_database(url, database="master") - elif dialect_name == 'cockroachdb': + elif dialect_name == "cockroachdb": url = _set_url_database(url, database="defaultdb") - elif not dialect_name == 'sqlite': + elif not dialect_name == "sqlite": url = _set_url_database(url, database=None) - if dialect_name == 'mssql' and dialect_driver in {'pymssql', 'pyodbc'}: - engine = sa.create_engine(url, connect_args={'autocommit': True}) - elif dialect_name == 'postgresql' and dialect_driver in { - 'asyncpg', 'pg8000', 'psycopg2', 'psycopg2cffi'}: - engine = sa.create_engine(url, isolation_level='AUTOCOMMIT') + if dialect_name == "mssql" and dialect_driver in {"pymssql", "pyodbc"}: + engine = sa.create_engine(url, connect_args={"autocommit": True}) + elif dialect_name == "postgresql" and dialect_driver in { + "asyncpg", + "pg8000", + "psycopg2", + "psycopg2cffi", + }: + engine = sa.create_engine(url, isolation_level="AUTOCOMMIT") else: engine = sa.create_engine(url) - if dialect_name == 'sqlite' and database != ':memory:': + if dialect_name == "sqlite" and database != ":memory:": if database: os.remove(database) - elif dialect_name == 'postgresql': + elif dialect_name == "postgresql": with engine.begin() as connection: # Disconnect all users from the database we are dropping. version = connection.dialect.server_version_info - pid_column = ( - 'pid' if (version >= (9, 2)) else 'procpid' - ) - text = ''' + pid_column = "pid" if (version >= (9, 2)) else "procpid" + text = """ SELECT pg_terminate_backend(pg_stat_activity.%(pid_column)s) FROM pg_stat_activity WHERE pg_stat_activity.datname = '%(database)s' AND %(pid_column)s <> pg_backend_pid(); - ''' % {'pid_column': pid_column, 'database': database} + """ % { + "pid_column": pid_column, + "database": database, + } connection.execute(sa.text(text)) # Drop the database. - text = 'DROP DATABASE {0}'.format(quote(connection, database)) + text = "DROP DATABASE {0}".format(quote(connection, database)) connection.execute(sa.text(text)) else: - text = 'DROP DATABASE {0}'.format(quote(engine, database)) + text = "DROP DATABASE {0}".format(quote(engine, database)) with engine.begin() as connection: connection.execute(sa.text(text)) From 3b97df1529ca6cdbfcf1da6f95456a6329556e20 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Sun, 9 Oct 2022 20:25:00 +0100 Subject: [PATCH 02/12] logic correction --- sqlalchemy_utils/functions/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 2bd1e73e..f47199dd 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -534,7 +534,7 @@ def create_database(url, encoding="utf8", template=None, default_db=None): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if default_db != None: + if default_db == None: if dialect_name == "postgresql": url = _set_url_database(url, database="postgres") elif dialect_name == "mssql": From 11d181630b6dd698650cd735e070379308ad6826 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:08:11 +0100 Subject: [PATCH 03/12] Revert "logic correction" This reverts commit 3b97df1529ca6cdbfcf1da6f95456a6329556e20. --- sqlalchemy_utils/functions/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index f47199dd..2bd1e73e 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -534,7 +534,7 @@ def create_database(url, encoding="utf8", template=None, default_db=None): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if default_db == None: + if default_db != None: if dialect_name == "postgresql": url = _set_url_database(url, database="postgres") elif dialect_name == "mssql": From 3cfb14575890e2b02add53d8acc7b7e4ebb322cc Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:08:45 +0100 Subject: [PATCH 04/12] Revert "Default database parameter" This reverts commit 955e2945b4e2fe5cede5f6bb3f21382618104a32. --- sqlalchemy_utils/functions/database.py | 173 +++++++++++++------------ 1 file changed, 89 insertions(+), 84 deletions(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 2bd1e73e..d20618e3 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -11,7 +11,7 @@ from .orm import quote -def escape_like(string, escape_char="*"): +def escape_like(string, escape_char='*'): """ Escape the string paremeter used in SQL LIKE expressions. @@ -29,9 +29,10 @@ def escape_like(string, escape_char="*"): :param escape_char: escape character """ return ( - string.replace(escape_char, escape_char * 2) - .replace("%", escape_char + "%") - .replace("_", escape_char + "_") + string + .replace(escape_char, escape_char * 2) + .replace('%', escape_char + '%') + .replace('_', escape_char + '_') ) @@ -81,7 +82,6 @@ def json_sql(value, scalars_to_json=True): """ scalar_convert = sa.text if scalars_to_json: - def scalar_convert(a): return sa.func.to_json(sa.text(a)) @@ -96,7 +96,10 @@ def scalar_convert(a): return scalar_convert("'{0}'".format(value)) elif isinstance(value, Sequence): return sa.func.json_build_array( - *(json_sql(v, scalars_to_json=False) for v in value) + *( + json_sql(v, scalars_to_json=False) + for v in value + ) ) elif isinstance(value, (int, float)): return scalar_convert(str(value)) @@ -151,7 +154,6 @@ def jsonb_sql(value, scalars_to_jsonb=True): """ scalar_convert = sa.text if scalars_to_jsonb: - def scalar_convert(a): return sa.func.to_jsonb(sa.text(a)) @@ -166,7 +168,10 @@ def scalar_convert(a): return scalar_convert("'{0}'".format(value)) elif isinstance(value, Sequence): return sa.func.jsonb_build_array( - *(jsonb_sql(v, scalars_to_jsonb=False) for v in value) + *( + jsonb_sql(v, scalars_to_jsonb=False) + for v in value + ) ) elif isinstance(value, (int, float)): return scalar_convert(str(value)) @@ -270,8 +275,8 @@ class Article(Base): table = column_or_constraint.table if not isinstance(table, sa.Table): raise TypeError( - "Only columns belonging to Table objects are supported. Given " - "column belongs to %r." % table + 'Only columns belonging to Table objects are supported. Given ' + 'column belongs to %r.' % table ) primary_keys = table.primary_key.columns.values() if isinstance(column_or_constraint, sa.ForeignKeyConstraint): @@ -279,8 +284,12 @@ class Article(Base): else: columns = [column_or_constraint] - return (primary_keys and starts_with(primary_keys, columns)) or any( - starts_with(index.columns.values(), columns) for index in table.indexes + return ( + (primary_keys and starts_with(primary_keys, columns)) or + any( + starts_with(index.columns.values(), columns) + for index in table.indexes + ) ) @@ -366,8 +375,8 @@ class Article(Base): table = column_or_constraint.table if not isinstance(table, sa.Table): raise TypeError( - "Only columns belonging to Table objects are supported. Given " - "column belongs to %r." % table + 'Only columns belonging to Table objects are supported. Given ' + 'column belongs to %r.' % table ) primary_keys = list(table.primary_key.columns.values()) if isinstance(column_or_constraint, sa.ForeignKeyConstraint): @@ -376,13 +385,13 @@ class Article(Base): columns = [column_or_constraint] return ( - (columns == primary_keys) - or any( + (columns == primary_keys) or + any( columns == list(constraint.columns.values()) for constraint in table.constraints if isinstance(constraint, sa.sql.schema.UniqueConstraint) - ) - or any( + ) or + any( columns == list(index.columns.values()) for index in table.indexes if index.unique @@ -398,12 +407,16 @@ def is_auto_assigned_date_column(column): :param column: SQLAlchemy Column object """ return ( - isinstance(column.type, sa.DateTime) or isinstance(column.type, sa.Date) - ) and ( - column.default - or column.server_default - or column.onupdate - or column.server_onupdate + ( + isinstance(column.type, sa.DateTime) or + isinstance(column.type, sa.Date) + ) and + ( + column.default or + column.server_default or + column.onupdate or + column.server_onupdate + ) ) @@ -414,7 +427,7 @@ def _set_url_database(url: sa.engine.url.URL, database): :param database: New database to set. """ - if hasattr(url, "_replace"): + if hasattr(url, '_replace'): # Cannot use URL.set() as database may need to be set to None. ret = url._replace(database=database) else: # SQLAlchemy <1.4 @@ -434,10 +447,10 @@ def _sqlite_file_exists(database): if not os.path.isfile(database) or os.path.getsize(database) < 100: return False - with open(database, "rb") as f: + with open(database, 'rb') as f: header = f.read(100) - return header[:16] == b"SQLite format 3\x00" + return header[:16] == b'SQLite format 3\x00' def database_exists(url): @@ -466,9 +479,9 @@ def database_exists(url): dialect_name = url.get_dialect().name engine = None try: - if dialect_name == "postgresql": + if dialect_name == 'postgresql': text = "SELECT 1 FROM pg_database WHERE datname='%s'" % database - for db in (database, "postgres", "template1", "template0", None): + for db in (database, 'postgres', 'template1', 'template0', None): url = _set_url_database(url, database=db) engine = sa.create_engine(url) try: @@ -477,26 +490,24 @@ def database_exists(url): pass return False - elif dialect_name == "mysql": + elif dialect_name == 'mysql': url = _set_url_database(url, database=None) engine = sa.create_engine(url) - text = ( - "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA " - "WHERE SCHEMA_NAME = '%s'" % database - ) + text = ("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA " + "WHERE SCHEMA_NAME = '%s'" % database) return bool(_get_scalar_result(engine, sa.text(text))) - elif dialect_name == "sqlite": + elif dialect_name == 'sqlite': url = _set_url_database(url, database=None) engine = sa.create_engine(url) if database: - return database == ":memory:" or _sqlite_file_exists(database) + return database == ':memory:' or _sqlite_file_exists(database) else: # The default SQLAlchemy database is in memory, and :memory: is # not required, thus we should support that use case. return True else: - text = "SELECT 1" + text = 'SELECT 1' try: engine = sa.create_engine(url) return bool(_get_scalar_result(engine, sa.text(text))) @@ -507,7 +518,7 @@ def database_exists(url): engine.dispose() -def create_database(url, encoding="utf8", template=None, default_db=None): +def create_database(url, encoding='utf8', template=None): """Issue the appropriate CREATE DATABASE statement. :param url: A SQLAlchemy engine URL. @@ -534,52 +545,51 @@ def create_database(url, encoding="utf8", template=None, default_db=None): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if default_db != None: - if dialect_name == "postgresql": - url = _set_url_database(url, database="postgres") - elif dialect_name == "mssql": - url = _set_url_database(url, database="master") - elif dialect_name == "cockroachdb": - url = _set_url_database(url, database="defaultdb") - elif not dialect_name == "sqlite": - url = _set_url_database(url, database=None) - else: - url = _set_url_database(url, database=default_db) + if dialect_name == 'postgresql': + url = _set_url_database(url, database="postgres") + elif dialect_name == 'mssql': + url = _set_url_database(url, database="master") + elif dialect_name == 'cockroachdb': + url = _set_url_database(url, database="defaultdb") + elif not dialect_name == 'sqlite': + url = _set_url_database(url, database=None) - if (dialect_name == "mssql" and dialect_driver in {"pymssql", "pyodbc"}) or ( - dialect_name == "postgresql" - and dialect_driver in {"asyncpg", "pg8000", "psycopg2", "psycopg2cffi"} - ): - engine = sa.create_engine(url, isolation_level="AUTOCOMMIT") + if (dialect_name == 'mssql' and dialect_driver in {'pymssql', 'pyodbc'}) \ + or (dialect_name == 'postgresql' and dialect_driver in { + 'asyncpg', 'pg8000', 'psycopg2', 'psycopg2cffi'}): + engine = sa.create_engine(url, isolation_level='AUTOCOMMIT') else: engine = sa.create_engine(url) - if dialect_name == "postgresql": + if dialect_name == 'postgresql': if not template: - template = "template1" + template = 'template1' text = "CREATE DATABASE {0} ENCODING '{1}' TEMPLATE {2}".format( - quote(engine, database), encoding, quote(engine, template) + quote(engine, database), + encoding, + quote(engine, template) ) with engine.begin() as connection: connection.execute(sa.text(text)) - elif dialect_name == "mysql": + elif dialect_name == 'mysql': text = "CREATE DATABASE {0} CHARACTER SET = '{1}'".format( - quote(engine, database), encoding + quote(engine, database), + encoding ) with engine.begin() as connection: connection.execute(sa.text(text)) - elif dialect_name == "sqlite" and database != ":memory:": + elif dialect_name == 'sqlite' and database != ':memory:': if database: with engine.begin() as connection: connection.execute(sa.text("CREATE TABLE DB(id int);")) connection.execute(sa.text("DROP TABLE DB;")) else: - text = "CREATE DATABASE {0}".format(quote(engine, database)) + text = 'CREATE DATABASE {0}'.format(quote(engine, database)) with engine.begin() as connection: connection.execute(sa.text(text)) @@ -604,51 +614,46 @@ def drop_database(url): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if dialect_name == "postgresql": + if dialect_name == 'postgresql': url = _set_url_database(url, database="postgres") - elif dialect_name == "mssql": + elif dialect_name == 'mssql': url = _set_url_database(url, database="master") - elif dialect_name == "cockroachdb": + elif dialect_name == 'cockroachdb': url = _set_url_database(url, database="defaultdb") - elif not dialect_name == "sqlite": + elif not dialect_name == 'sqlite': url = _set_url_database(url, database=None) - if dialect_name == "mssql" and dialect_driver in {"pymssql", "pyodbc"}: - engine = sa.create_engine(url, connect_args={"autocommit": True}) - elif dialect_name == "postgresql" and dialect_driver in { - "asyncpg", - "pg8000", - "psycopg2", - "psycopg2cffi", - }: - engine = sa.create_engine(url, isolation_level="AUTOCOMMIT") + if dialect_name == 'mssql' and dialect_driver in {'pymssql', 'pyodbc'}: + engine = sa.create_engine(url, connect_args={'autocommit': True}) + elif dialect_name == 'postgresql' and dialect_driver in { + 'asyncpg', 'pg8000', 'psycopg2', 'psycopg2cffi'}: + engine = sa.create_engine(url, isolation_level='AUTOCOMMIT') else: engine = sa.create_engine(url) - if dialect_name == "sqlite" and database != ":memory:": + if dialect_name == 'sqlite' and database != ':memory:': if database: os.remove(database) - elif dialect_name == "postgresql": + elif dialect_name == 'postgresql': with engine.begin() as connection: # Disconnect all users from the database we are dropping. version = connection.dialect.server_version_info - pid_column = "pid" if (version >= (9, 2)) else "procpid" - text = """ + pid_column = ( + 'pid' if (version >= (9, 2)) else 'procpid' + ) + text = ''' SELECT pg_terminate_backend(pg_stat_activity.%(pid_column)s) FROM pg_stat_activity WHERE pg_stat_activity.datname = '%(database)s' AND %(pid_column)s <> pg_backend_pid(); - """ % { - "pid_column": pid_column, - "database": database, - } + ''' % {'pid_column': pid_column, 'database': database} connection.execute(sa.text(text)) # Drop the database. - text = "DROP DATABASE {0}".format(quote(connection, database)) + text = 'DROP DATABASE {0}'.format(quote(connection, database)) connection.execute(sa.text(text)) else: - text = "DROP DATABASE {0}".format(quote(engine, database)) + text = 'DROP DATABASE {0}'.format(quote(engine, database)) with engine.begin() as connection: connection.execute(sa.text(text)) From fddbe343318a831308251207bed4d273b14be8b4 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:10:56 +0100 Subject: [PATCH 05/12] Update database.py --- sqlalchemy_utils/functions/database.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 7bc2ff69..95d4a6c7 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -564,6 +564,7 @@ def create_database(url, encoding='utf8', template=None): if dialect_name == 'postgresql': if not template: template = "template1" + text = "CREATE DATABASE {} ENCODING '{}' TEMPLATE {}".format( quote(engine, database), encoding, @@ -572,6 +573,7 @@ def create_database(url, encoding='utf8', template=None): with engine.begin() as connection: connection.execute(sa.text(text)) + elif dialect_name == 'mysql': text = "CREATE DATABASE {} CHARACTER SET = '{}'".format( quote(engine, database), From 9e7717c286b60a845431b6decaedf5620dac3355 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:11:27 +0100 Subject: [PATCH 06/12] Update database.py --- sqlalchemy_utils/functions/database.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 95d4a6c7..1be92a85 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -563,7 +563,7 @@ def create_database(url, encoding='utf8', template=None): if dialect_name == 'postgresql': if not template: - template = "template1" + template = 'template1' text = "CREATE DATABASE {} ENCODING '{}' TEMPLATE {}".format( quote(engine, database), @@ -573,7 +573,7 @@ def create_database(url, encoding='utf8', template=None): with engine.begin() as connection: connection.execute(sa.text(text)) - + elif dialect_name == 'mysql': text = "CREATE DATABASE {} CHARACTER SET = '{}'".format( quote(engine, database), From 0cd98df5ff46969be1ffe899ad26859c2329d5bf Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:12:42 +0100 Subject: [PATCH 07/12] default db override no formatting --- sqlalchemy_utils/functions/database.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 1be92a85..299a3151 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -518,7 +518,7 @@ def database_exists(url): engine.dispose() -def create_database(url, encoding='utf8', template=None): +def create_database(url, encoding='utf8', template=None, default_db=None): """Issue the appropriate CREATE DATABASE statement. :param url: A SQLAlchemy engine URL. @@ -553,6 +553,18 @@ def create_database(url, encoding='utf8', template=None): url = _set_url_database(url, database="defaultdb") elif not dialect_name == 'sqlite': url = _set_url_database(url, database=None) + + if default_db != None: + if dialect_name == "postgresql": + url = _set_url_database(url, database="postgres") + elif dialect_name == "mssql": + url = _set_url_database(url, database="master") + elif dialect_name == "cockroachdb": + url = _set_url_database(url, database="defaultdb") + elif not dialect_name == "sqlite": + url = _set_url_database(url, database=None) + else: + url = _set_url_database(url, database=default_db) if (dialect_name == 'mssql' and dialect_driver in {'pymssql', 'pyodbc'}) \ or (dialect_name == 'postgresql' and dialect_driver in { From 81e4ecf518e45ed78055222357c888ff4862dc3e Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:13:19 +0100 Subject: [PATCH 08/12] whitespace --- sqlalchemy_utils/functions/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 299a3151..32232fe5 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -563,7 +563,7 @@ def create_database(url, encoding='utf8', template=None, default_db=None): url = _set_url_database(url, database="defaultdb") elif not dialect_name == "sqlite": url = _set_url_database(url, database=None) - else: + else: url = _set_url_database(url, database=default_db) if (dialect_name == 'mssql' and dialect_driver in {'pymssql', 'pyodbc'}) \ From 6d526586c0f866c75f47603eb3dedb9fb92c95a9 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:14:35 +0100 Subject: [PATCH 09/12] codebase style preservation --- sqlalchemy_utils/functions/database.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 32232fe5..eaab43ab 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -545,23 +545,14 @@ def create_database(url, encoding='utf8', template=None, default_db=None): dialect_name = url.get_dialect().name dialect_driver = url.get_dialect().driver - if dialect_name == 'postgresql': - url = _set_url_database(url, database="postgres") - elif dialect_name == 'mssql': - url = _set_url_database(url, database="master") - elif dialect_name == 'cockroachdb': - url = _set_url_database(url, database="defaultdb") - elif not dialect_name == 'sqlite': - url = _set_url_database(url, database=None) - if default_db != None: - if dialect_name == "postgresql": + if dialect_name == 'postgresql': url = _set_url_database(url, database="postgres") - elif dialect_name == "mssql": + elif dialect_name == 'mssql': url = _set_url_database(url, database="master") - elif dialect_name == "cockroachdb": + elif dialect_name == 'cockroachdb': url = _set_url_database(url, database="defaultdb") - elif not dialect_name == "sqlite": + elif not dialect_name == 'sqlite': url = _set_url_database(url, database=None) else: url = _set_url_database(url, database=default_db) From c610ec62e238a2573ee2793f0592ac34d459eb88 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 27 Oct 2022 02:19:34 +0100 Subject: [PATCH 10/12] Documentation --- sqlalchemy_utils/functions/database.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index eaab43ab..b37f0d95 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -526,6 +526,7 @@ def create_database(url, encoding='utf8', template=None, default_db=None): :param template: The name of the template from which to create the new database. At the moment only supported by PostgreSQL driver. + :param defualt_db: Overwrite the defualt database used when connecting. To create a database, you can pass a simple URL that would have been passed to ``create_engine``. :: From ec7ab4f9aae469b244107a51d272695cb6abaadd Mon Sep 17 00:00:00 2001 From: George Taylor Date: Fri, 28 Oct 2022 14:44:06 +0100 Subject: [PATCH 11/12] typo --- sqlalchemy_utils/functions/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index b37f0d95..e31e1e42 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -526,7 +526,7 @@ def create_database(url, encoding='utf8', template=None, default_db=None): :param template: The name of the template from which to create the new database. At the moment only supported by PostgreSQL driver. - :param defualt_db: Overwrite the defualt database used when connecting. + :param defualt_db: Overwrite the default database used when connecting. To create a database, you can pass a simple URL that would have been passed to ``create_engine``. :: From 6d87c2af342f6e3525d18c4cee6e86de897a51e6 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Tue, 15 Nov 2022 23:08:18 +0000 Subject: [PATCH 12/12] support for named default_db in database_exists --- sqlalchemy_utils/functions/database.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index e31e1e42..c9c1a426 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -453,10 +453,11 @@ def _sqlite_file_exists(database): return header[:16] == b'SQLite format 3\x00' -def database_exists(url): +def database_exists(url, default_db=None): """Check if a database exists. :param url: A SQLAlchemy engine URL. + :param default_db: The default database to use instead of requiring standard Performs backend-specific testing to quickly determine if a database exists on the server. :: @@ -481,7 +482,7 @@ def database_exists(url): try: if dialect_name == 'postgresql': text = "SELECT 1 FROM pg_database WHERE datname='%s'" % database - for db in (database, 'postgres', 'template1', 'template0', None): + for db in (database, default_db or 'postgres', 'template1', 'template0', None): url = _set_url_database(url, database=db) engine = sa.create_engine(url) try: