From 012daf68b275b41b6cce5a7d1a269724385e078d Mon Sep 17 00:00:00 2001 From: diegofsousa Date: Wed, 18 Apr 2018 18:08:20 -0300 Subject: [PATCH 1/2] fix bug in mysql extension --- db_multitenant/db/backends/mysql/base.py | 36 ++++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/db_multitenant/db/backends/mysql/base.py b/db_multitenant/db/backends/mysql/base.py index b41a47d..26a4842 100644 --- a/db_multitenant/db/backends/mysql/base.py +++ b/db_multitenant/db/backends/mysql/base.py @@ -1,8 +1,9 @@ -from importlib import import_module import logging import time +from django.conf import settings from django.core.exceptions import ImproperlyConfigured +from importlib import import_module from db_multitenant.threadlocal import MultiTenantThreadlocal from db_multitenant.utils import update_database_from_env @@ -21,33 +22,32 @@ def get_threadlocal(self): def _cursor(self): """Supplies a cursor, executing the `USE` statement if required. - Ideally we'd override a get_new_connection DatabaseWrapper function, but _cursor() is as close as it gets. """ cursor = super(DatabaseWrapper, self)._cursor() - db_name = self.threadlocal.get_db_name() - if not db_name: - # Django loads the settings after it tries to connect to mysql, when - # running management commands If that's the case, update database - # name manually + dbname = self.threadlocal.get_dbname() + if not dbname: + # Django loads the settings after it tries to connect to mysql, when running management commands + # If that's the case, update database name manually update_database_from_env(super(DatabaseWrapper, self).get_connection_params()) - db_name = self.threadlocal.get_db_name() - if not db_name: - raise ImproperlyConfigured('db_name not set at cursor create time') - - # Cache the applied db_name as "mt_db_name" on the connection, avoiding + dbname = self.threadlocal.get_dbname() + if not dbname: + #raise ImproperlyConfigured('dbname not set at cursor create time') + dbname = settings.DATABASES['default']['NAME'] + LOGGER.debug('dbname not set at cursor create time, using default database name: "{}".'.format(dbname)) + # Cache the applied dbname as "mt_dbname" on the connection, avoiding # an extra execute() if already set. Importantly, we assume no other # code in the app is executing `USE`. connection = cursor.cursor.connection - connection_db_name = getattr(connection, 'mt_db_name', None) + connection_dbname = getattr(connection, 'mt_dbname', None) - if connection_db_name != db_name: + if connection_dbname != dbname: start_time = time.time() - cursor.execute('USE `%s`;' % db_name) + cursor.execute('USE `%s`;' % dbname) time_ms = int((time.time() - start_time) * 1000) - LOGGER.debug('Applied db_name `%s` in %s ms', db_name, time_ms) - connection.mt_db_name = db_name + LOGGER.debug('Applied dbname `%s` in %s ms' % (dbname, time_ms)) + connection.mt_dbname = dbname - return cursor + return cursor \ No newline at end of file From 48d70bade1c375b0e92cdeb60a38c06ac8efe6a1 Mon Sep 17 00:00:00 2001 From: diegofsousa Date: Thu, 19 Apr 2018 16:31:40 -0300 Subject: [PATCH 2/2] fix bug in middleware for mysql --- db_multitenant/db/backends/mysql/base.py | 58 ++++++++++++++----- .../mysql_hostname_tenant_mapper.py | 4 +- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/db_multitenant/db/backends/mysql/base.py b/db_multitenant/db/backends/mysql/base.py index 26a4842..373bd47 100644 --- a/db_multitenant/db/backends/mysql/base.py +++ b/db_multitenant/db/backends/mysql/base.py @@ -1,10 +1,33 @@ +# Copyright (c) 2013, mike wakerly +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. Redistributions in binary +# form must reproduce the above copyright notice, this list of conditions and +# the following disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from importlib import import_module import logging import time -from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from importlib import import_module +from django.conf import settings from db_multitenant.threadlocal import MultiTenantThreadlocal from db_multitenant.utils import update_database_from_env @@ -22,32 +45,35 @@ def get_threadlocal(self): def _cursor(self): """Supplies a cursor, executing the `USE` statement if required. + Ideally we'd override a get_new_connection DatabaseWrapper function, but _cursor() is as close as it gets. """ cursor = super(DatabaseWrapper, self)._cursor() - dbname = self.threadlocal.get_dbname() - if not dbname: - # Django loads the settings after it tries to connect to mysql, when running management commands - # If that's the case, update database name manually + db_name = self.threadlocal.get_db_name() + if not db_name: + # Django loads the settings after it tries to connect to mysql, when + # running management commands If that's the case, update database + # name manually update_database_from_env(super(DatabaseWrapper, self).get_connection_params()) - dbname = self.threadlocal.get_dbname() - if not dbname: - #raise ImproperlyConfigured('dbname not set at cursor create time') + db_name = self.threadlocal.get_db_name() + if not db_name: dbname = settings.DATABASES['default']['NAME'] LOGGER.debug('dbname not set at cursor create time, using default database name: "{}".'.format(dbname)) - # Cache the applied dbname as "mt_dbname" on the connection, avoiding + #raise ImproperlyConfigured('db_name not set at cursor create time') + + # Cache the applied db_name as "mt_db_name" on the connection, avoiding # an extra execute() if already set. Importantly, we assume no other # code in the app is executing `USE`. connection = cursor.cursor.connection - connection_dbname = getattr(connection, 'mt_dbname', None) + connection_db_name = getattr(connection, 'mt_db_name', None) - if connection_dbname != dbname: + if connection_db_name != db_name: start_time = time.time() - cursor.execute('USE `%s`;' % dbname) + cursor.execute('USE `%s`;' % db_name) time_ms = int((time.time() - start_time) * 1000) - LOGGER.debug('Applied dbname `%s` in %s ms' % (dbname, time_ms)) - connection.mt_dbname = dbname + LOGGER.debug('Applied db_name `%s` in %s ms', db_name, time_ms) + connection.mt_db_name = db_name - return cursor \ No newline at end of file + return cursor diff --git a/mapper_examples/mysql_hostname_tenant_mapper.py b/mapper_examples/mysql_hostname_tenant_mapper.py index 23b3376..127341a 100644 --- a/mapper_examples/mysql_hostname_tenant_mapper.py +++ b/mapper_examples/mysql_hostname_tenant_mapper.py @@ -18,8 +18,8 @@ def get_tenant_name(self, request): return hostname.split('.')[0] def get_db_name(self, request, tenant_name): - return 'tenant-%s' % tenant_name + return tenant_name def get_cache_prefix(self, request, tenant_name, db_name): """The arguments db_name and tenant_name are provided by the methods of this TenantMapper""" - return 'tenant-%s' % tenant_name + return tenant_name