1# mysql/gaerdbms.py 2# Copyright (C) 2005-2016 the SQLAlchemy authors and contributors 3# <see AUTHORS file> 4# 5# This module is part of SQLAlchemy and is released under 6# the MIT License: http://www.opensource.org/licenses/mit-license.php 7""" 8.. dialect:: mysql+gaerdbms 9 :name: Google Cloud SQL 10 :dbapi: rdbms 11 :connectstring: mysql+gaerdbms:///<dbname>?instance=<instancename> 12 :url: https://developers.google.com/appengine/docs/python/cloud-sql/\ 13developers-guide 14 15 This dialect is based primarily on the :mod:`.mysql.mysqldb` dialect with 16 minimal changes. 17 18 .. versionadded:: 0.7.8 19 20 .. deprecated:: 1.0 This dialect is **no longer necessary** for 21 Google Cloud SQL; the MySQLdb dialect can be used directly. 22 Cloud SQL now recommends creating connections via the 23 mysql dialect using the URL format 24 25 ``mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/<projectid>:<instancename>`` 26 27 28Pooling 29------- 30 31Google App Engine connections appear to be randomly recycled, 32so the dialect does not pool connections. The :class:`.NullPool` 33implementation is installed within the :class:`.Engine` by 34default. 35 36""" 37 38import os 39 40from .mysqldb import MySQLDialect_mysqldb 41from ...pool import NullPool 42import re 43from sqlalchemy.util import warn_deprecated 44 45 46def _is_dev_environment(): 47 return os.environ.get('SERVER_SOFTWARE', '').startswith('Development/') 48 49 50class MySQLDialect_gaerdbms(MySQLDialect_mysqldb): 51 52 @classmethod 53 def dbapi(cls): 54 55 warn_deprecated( 56 "Google Cloud SQL now recommends creating connections via the " 57 "MySQLdb dialect directly, using the URL format " 58 "mysql+mysqldb://root@/<dbname>?unix_socket=/cloudsql/" 59 "<projectid>:<instancename>" 60 ) 61 62 # from django: 63 # http://code.google.com/p/googleappengine/source/ 64 # browse/trunk/python/google/storage/speckle/ 65 # python/django/backend/base.py#118 66 # see also [ticket:2649] 67 # see also http://stackoverflow.com/q/14224679/34549 68 from google.appengine.api import apiproxy_stub_map 69 70 if _is_dev_environment(): 71 from google.appengine.api import rdbms_mysqldb 72 return rdbms_mysqldb 73 elif apiproxy_stub_map.apiproxy.GetStub('rdbms'): 74 from google.storage.speckle.python.api import rdbms_apiproxy 75 return rdbms_apiproxy 76 else: 77 from google.storage.speckle.python.api import rdbms_googleapi 78 return rdbms_googleapi 79 80 @classmethod 81 def get_pool_class(cls, url): 82 # Cloud SQL connections die at any moment 83 return NullPool 84 85 def create_connect_args(self, url): 86 opts = url.translate_connect_args() 87 if not _is_dev_environment(): 88 # 'dsn' and 'instance' are because we are skipping 89 # the traditional google.api.rdbms wrapper 90 opts['dsn'] = '' 91 opts['instance'] = url.query['instance'] 92 return [], opts 93 94 def _extract_error_code(self, exception): 95 match = re.compile(r"^(\d+)L?:|^\((\d+)L?,").match(str(exception)) 96 # The rdbms api will wrap then re-raise some types of errors 97 # making this regex return no matches. 98 code = match.group(1) or match.group(2) if match else None 99 if code: 100 return int(code) 101 102dialect = MySQLDialect_gaerdbms 103