Code

Ticket #7732: oracle_pool.5.diff

File oracle_pool.5.diff, 7.4 KB (added by mboersma, 5 years ago)

Modified patch against trunk [10832] that passes the Django test suite

Line 
1Index: django/db/backends/oracle/base.py
2===================================================================
3--- django/db/backends/oracle/base.py   (revision 10832)
4+++ django/db/backends/oracle/base.py   (working copy)
5@@ -273,6 +273,9 @@
6         'iendswith': "LIKEC UPPER(%s) ESCAPE '\\'",
7     }
8     oracle_version = None
9+    _pool_defaults = {'SESSION_POOL_MIN': 1,
10+                      'SESSION_POOL_MAX': 3,
11+                      'SESSION_POOL_INCREMENT': 1,}
12 
13     def __init__(self, *args, **kwargs):
14         super(DatabaseWrapper, self).__init__(*args, **kwargs)
15@@ -284,6 +287,53 @@
16         self.introspection = DatabaseIntrospection(self)
17         self.validation = BaseDatabaseValidation()
18 
19+        # Fetch connection pool settings
20+        opts = self.settings_dict['DATABASE_OPTIONS']
21+        if not isinstance(opts, dict):
22+            # Insurance against foolish users...
23+            opts = {}
24+        if opts.get('SESSION_POOL', False):
25+            self._use_pool = True
26+            self._pool_settings = dict(self._pool_defaults)
27+            self._pool_settings.update(opts)
28+        else:
29+            self._use_pool = False
30+
31+    def _get_pool (self):
32+        if not hasattr (self.__class__, '_pool'):
33+            settings_dict = self.settings_dict
34+            Database.OPT_Threading = 1
35+            if len(settings_dict['DATABASE_HOST'].strip()) == 0:
36+                settings_dict['DATABASE_HOST'] = 'localhost'
37+            if len(settings_dict['DATABASE_PORT'].strip()) != 0:
38+                dsn = Database.makedsn(settings_dict['DATABASE_HOST'],
39+                                       int(settings_dict['DATABASE_PORT']),
40+                                       settings_dict['DATABASE_NAME'])
41+                p = Database.SessionPool(settings_dict['DATABASE_USER'],
42+                                         settings_dict['DATABASE_PASSWORD'],
43+                                         dsn,
44+                                         self._pool_settings['SESSION_POOL_MIN'],
45+                                         self._pool_settings['SESSION_POOL_MAX'],
46+                                         self._pool_settings['SESSION_POOL_INCREMENT'],
47+                                         threaded = True)
48+            else:
49+                p = Database.SessionPool(settings_dict['DATABASE_USER'],
50+                                         settings_dict['DATABASE_PASSWORD'],
51+                                         settings_dict['DATABASE_NAME'],
52+                                         self._pool_settings['SESSION_POOL_MIN'],
53+                                         self._pool_settings['SESSION_POOL_MAX'],
54+                                         self._pool_settings['SESSION_POOL_INCREMENT'],
55+                                         threaded = True)
56+            setattr(self.__class__, '_pool', p)
57+            # TODO I think the init stuff only needs to happen once per pool,
58+            # but it may be once per connection...
59+            conn = p.acquire()
60+            self._init_oracle_settings(conn)
61+            p.release(conn)
62+        return getattr(self.__class__, '_pool')
63+
64+    pool = property (_get_pool)
65+
66     def _valid_connection(self):
67         return self.connection is not None
68 
69@@ -300,12 +350,8 @@
70         return "%s/%s@%s" % (settings_dict['DATABASE_USER'],
71                              settings_dict['DATABASE_PASSWORD'], dsn)
72 
73-    def _cursor(self):
74-        cursor = None
75-        if not self._valid_connection():
76-            conn_string = self._connect_string()
77-            self.connection = Database.connect(conn_string, **self.settings_dict['DATABASE_OPTIONS'])
78-            cursor = FormatStylePlaceholderCursor(self.connection)
79+    def _init_oracle_settings(self, connection):
80+        cursor = FormatStylePlaceholderCursor(connection)
81             # Set oracle date to ansi date format.  This only needs to execute
82             # once when we create a new connection. We also set the Territory
83             # to 'AMERICA' which forces Sunday to evaluate to a '1' in TO_CHAR().
84@@ -313,7 +359,7 @@
85                            "NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF' "
86                            "NLS_TERRITORY = 'AMERICA'")
87             try:
88-                self.oracle_version = int(self.connection.version.split('.')[0])
89+            self.oracle_version = int(connection.version.split('.')[0])
90                 # There's no way for the DatabaseOperations class to know the
91                 # currently active Oracle version, so we do some setups here.
92                 # TODO: Multi-db support will need a better solution (a way to
93@@ -324,17 +370,40 @@
94                     self.ops.regex_lookup = self.ops.regex_lookup_10
95             except ValueError:
96                 pass
97+        return cursor
98+
99+    def _cursor(self):
100+        cursor = None
101+        if not self._valid_connection():
102+            if self._use_pool and self.pool is not None:
103+                self.connection = self.pool.acquire()
104+            else:
105+                    self.connection = Database.connect(self._connect_string(),
106+                        **self.settings_dict['DATABASE_OPTIONS'])
107+                    cursor = self._init_oracle_settings(self.connection)
108             try:
109                 self.connection.stmtcachesize = 20
110             except:
111                 # Django docs specify cx_Oracle version 4.3.1 or higher, but
112                 # stmtcachesize is available only in 4.3.2 and up.
113                 pass
114+            # TODO Does pulling a connection from the pool count as "creation"?
115             connection_created.send(sender=self.__class__)
116         if not cursor:
117             cursor = FormatStylePlaceholderCursor(self.connection)
118         return cursor
119 
120+    def close(self):
121+       from django.conf import settings
122+       if self._use_pool:
123+           if self.connection is not None:
124+               self.pool.release(self.connection)
125+               self.connection = None
126+       else:
127+           if self.connection is not None:
128+               self.connection.close()
129+               self.connection = None
130+
131     # Oracle doesn't support savepoint commits.  Ignore them.
132     def _savepoint_commit(self, sid):
133         pass
134Index: django/db/backends/oracle/creation.py
135===================================================================
136--- django/db/backends/oracle/creation.py       (revision 10832)
137+++ django/db/backends/oracle/creation.py       (working copy)
138@@ -111,6 +111,10 @@
139         settings.TEST_DATABASE_USER = settings.DATABASE_USER = self.connection.settings_dict["DATABASE_USER"] = TEST_DATABASE_USER
140         settings.DATABASE_PASSWORD = self.connection.settings_dict["DATABASE_PASSWORD"] = TEST_DATABASE_PASSWD
141 
142+        if hasattr(self.connection, '_pool'):
143+            self.connection.close()
144+            del self.connection.__class__._pool
145+
146         return settings.DATABASE_NAME
147 
148     def _destroy_test_db(self, test_database_name, verbosity=1):
149@@ -118,6 +122,11 @@
150         Destroy a test database, prompting the user for confirmation if the
151         database already exists. Returns the name of the test database created.
152         """
153+
154+        if hasattr(self.connection, '_pool'):
155+            self.connection.close()
156+            del self.connection.__class__._pool
157+
158         TEST_DATABASE_NAME = self._test_database_name(settings)
159         TEST_DATABASE_USER = self._test_database_user(settings)
160         TEST_DATABASE_PASSWD = self._test_database_passwd(settings)