Merge lp:~ack/storm/drop-mysql-support into lp:storm
- drop-mysql-support
- Merge into trunk
Proposed by
Alberto Donato
Status: | Merged |
---|---|
Approved by: | Adam Collard |
Approved revision: | 482 |
Merged at revision: | 486 |
Proposed branch: | lp:~ack/storm/drop-mysql-support |
Merge into: | lp:storm |
Diff against target: |
784 lines (+15/-566) 10 files modified
Makefile (+0/-4) README (+13/-42) dev/db-setup (+0/-6) dev/ubuntu-deps (+0/-2) storm/databases/mysql.py (+0/-227) tests/databases/base.py (+0/-2) tests/databases/mysql.py (+0/-145) tests/store/base.py (+0/-30) tests/store/mysql.py (+0/-106) tests/tutorial.txt (+2/-2) |
To merge this branch: | bzr merge lp:~ack/storm/drop-mysql-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Simon Poirier (community) | Approve | ||
Chad Smith (community) | Approve | ||
Review via email: mp+288652@code.launchpad.net |
Commit message
Description of the change
Drop support for MySQL.
To post a comment you must log in.
Revision history for this message
Simon Poirier (simpoir) wrote : | # |
+1 with inline nitpick
review:
Approve
lp:~ack/storm/drop-mysql-support
updated
- 482. By Alberto Donato
-
Drop extra space.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile' |
2 | --- Makefile 2012-02-01 17:02:31 +0000 |
3 | +++ Makefile 2017-02-08 11:47:07 +0000 |
4 | @@ -6,13 +6,9 @@ |
5 | |
6 | STORM_POSTGRES_URI = postgres:storm_test |
7 | STORM_POSTGRES_HOST_URI = postgres://localhost:$(PGPORT)/storm_test |
8 | -STORM_MYSQL_URI = mysql:storm_test |
9 | -STORM_MYSQL_HOST_URI = mysql://localhost/storm_test |
10 | |
11 | export STORM_POSTGRES_URI |
12 | export STORM_POSTGRES_HOST_URI |
13 | -export STORM_MYSQL_URI |
14 | -export STORM_MYSQL_HOST_URI |
15 | |
16 | all: build |
17 | |
18 | |
19 | === modified file 'README' |
20 | --- README 2016-03-01 13:09:28 +0000 |
21 | +++ README 2017-02-08 11:47:07 +0000 |
22 | @@ -22,8 +22,7 @@ |
23 | * Storm is well designed (different classes have very clear |
24 | boundaries, with small and clean public APIs). |
25 | * Designed from day one to work both with thin relational |
26 | - databases, such as SQLite, and big iron systems like PostgreSQL |
27 | - and MySQL. |
28 | + databases, such as SQLite, and big iron systems like PostgreSQL. |
29 | * Storm is easy to debug, since its code is written with a KISS |
30 | principle, and thus is easy to understand. |
31 | * Designed from day one to work both at the low end, with trivial |
32 | @@ -102,26 +101,18 @@ |
33 | Installing dependencies |
34 | ----------------------- |
35 | |
36 | -The following instructions assume that you're using Ubuntu. The |
37 | -same procedure will probably work without changes on a Debian system |
38 | -and with minimal changes on a non-Debian-based linux distribution. |
39 | -In order to run the test suite, and exercise all supported backends, |
40 | -you will need to install MySQL and PostgreSQL, along with the |
41 | -related Python database drivers: |
42 | +The following instructions assume that you're using Ubuntu. The same procedure |
43 | +will probably work without changes on a Debian system and with minimal changes |
44 | +on a non-Debian-based linux distribution. In order to run the test suite, and |
45 | +exercise all supported backends, you will need to install PostgreSQL, along |
46 | +with the related Python database drivers: |
47 | |
48 | $ sudo apt-get install \ |
49 | - python-mysqldb mysql-server \ |
50 | postgresql pgbouncer \ |
51 | build-essential |
52 | |
53 | These will take a few minutes to download (its a bit under 200MB all |
54 | -together). Once the download is complete, a screen called |
55 | -"configuring mysql-server-5.0" will be shown. When asked for a |
56 | -password for the root user leave the field blank and hit enter to |
57 | -continue. This is not a recommended setting for a production |
58 | -server, but makes life easier on a development machine. You may be |
59 | -asked to enter a password multiple times. Leave it blank in each |
60 | -case. |
61 | +together). |
62 | |
63 | The Python dependencies for running tests can mostly be installed with |
64 | apt-get: |
65 | @@ -167,38 +158,18 @@ |
66 | |
67 | $ sudo /etc/init.d/postgresql-8.4 restart |
68 | |
69 | -Next, you probably noticed that, while MySQL asked us about a root |
70 | -user several times, PostgreSQL didn't ask us at all. Lets create |
71 | -our PostgreSQL user now. As noted in the Ubuntu PostgreSQL |
72 | -documentation, the easiest thing is to create a user with the same |
73 | -name as your username. Run the following command to create a user |
74 | -for yourself (if prompted for a password, leave it blank): |
75 | +Lets create our PostgreSQL user now. As noted in the Ubuntu PostgreSQL |
76 | +documentation, the easiest thing is to create a user with the same name as your |
77 | +username. Run the following command to create a user for yourself (if prompted |
78 | +for a password, leave it blank): |
79 | |
80 | $ sudo -u postgres createuser --superuser $USER |
81 | |
82 | -Despite having created our root user already, MySQL requires an |
83 | -extra step. First we start mysql as the root user (which, you may |
84 | -recall, has no password) with: |
85 | - |
86 | - $ mysql -u root |
87 | - |
88 | -Then we create a new user. Be sure to replace YOUR_USERNAME with |
89 | -your actual user name (leaving the quotes in place). |
90 | - |
91 | -mysql> GRANT ALL PRIVILEGES ON *.* TO 'YOUR_USERNAME'@'localhost' |
92 | -IDENTIFIED BY '' WITH GRANT OPTION; |
93 | - |
94 | Creating test databases |
95 | ----------------------- |
96 | |
97 | -The test suite needs some local databases in place to exercise MySQL |
98 | -and PostgreSQL functionality. While still at the MySQL command |
99 | -prompt run: |
100 | - |
101 | -mysql> CREATE DATABASE storm_test CHARACTER SET utf8; |
102 | - |
103 | -Use Ctrl+D to exit, then, once back on the standard terminal, run |
104 | -the command for PostgreSQL: |
105 | +The test suite needs some local databases in place to exercise PostgreSQL |
106 | +functionality. Run: |
107 | |
108 | $ createdb storm_test |
109 | |
110 | |
111 | === modified file 'dev/db-setup' |
112 | --- dev/db-setup 2013-02-26 21:39:56 +0000 |
113 | +++ dev/db-setup 2017-02-08 11:47:07 +0000 |
114 | @@ -3,7 +3,6 @@ |
115 | PGHBA=/etc/postgresql/*/main/pg_hba.conf |
116 | PGCONF=/etc/postgresql/*/main/postgresql.conf |
117 | PGINIT=/etc/init.d/postgresql* |
118 | -MYSQL_PRIVS="GRANT ALL PRIVILEGES ON *.* TO '$USER'@'localhost' IDENTIFIED BY '' WITH GRANT OPTION;" |
119 | |
120 | is_number() { |
121 | test "$1" && printf '%f' "$1" >/dev/null; |
122 | @@ -37,15 +36,10 @@ |
123 | # If this fails, we will get errors later, so don't fail. |
124 | sudo -u postgres createuser --superuser $USER || /bin/true |
125 | |
126 | -echo " * Granting all mysql privs to $USER" |
127 | -mysql -u root -e "$MYSQL_PRIVS" |
128 | - |
129 | echo " * Create DB Test Fixtures (will fail if existing)" |
130 | createdb storm_test || /bin/true |
131 | -mysql -u $USER -e "CREATE DATABASE storm_test CHARACTER SET utf8;" || /bin/true |
132 | |
133 | echo " * Testing DB Access for user:$USER" |
134 | pg_dump storm_test > /dev/null |
135 | -mysqldump -u $USER storm_test > /dev/null |
136 | |
137 | echo " * All Done." |
138 | |
139 | === modified file 'dev/ubuntu-deps' |
140 | --- dev/ubuntu-deps 2016-03-01 13:15:05 +0000 |
141 | +++ dev/ubuntu-deps 2017-02-08 11:47:07 +0000 |
142 | @@ -6,8 +6,6 @@ |
143 | } |
144 | |
145 | apt_get install --no-install-recommends \ |
146 | - python-mysqldb \ |
147 | - mysql-server \ |
148 | postgresql \ |
149 | pgbouncer \ |
150 | build-essential \ |
151 | |
152 | === removed file 'storm/databases/mysql.py' |
153 | --- storm/databases/mysql.py 2015-06-15 12:02:12 +0000 |
154 | +++ storm/databases/mysql.py 1970-01-01 00:00:00 +0000 |
155 | @@ -1,227 +0,0 @@ |
156 | -# |
157 | -# Copyright (c) 2006, 2007 Canonical |
158 | -# |
159 | -# Written by Gustavo Niemeyer <gustavo@niemeyer.net> |
160 | -# |
161 | -# This file is part of Storm Object Relational Mapper. |
162 | -# |
163 | -# Storm is free software; you can redistribute it and/or modify |
164 | -# it under the terms of the GNU Lesser General Public License as |
165 | -# published by the Free Software Foundation; either version 2.1 of |
166 | -# the License, or (at your option) any later version. |
167 | -# |
168 | -# Storm is distributed in the hope that it will be useful, |
169 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
170 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
171 | -# GNU Lesser General Public License for more details. |
172 | -# |
173 | -# You should have received a copy of the GNU Lesser General Public License |
174 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
175 | -# |
176 | -from datetime import time, timedelta |
177 | -from array import array |
178 | -import sys |
179 | - |
180 | -from storm.databases import dummy |
181 | - |
182 | -try: |
183 | - import MySQLdb |
184 | - import MySQLdb.converters |
185 | -except ImportError: |
186 | - MySQLdb = dummy |
187 | - |
188 | -from storm.expr import ( |
189 | - compile, Insert, Select, compile_select, Undef, And, Eq, |
190 | - SQLRaw, SQLToken, is_safe_token) |
191 | -from storm.variables import Variable |
192 | -from storm.database import Database, Connection, Result |
193 | -from storm.exceptions import ( |
194 | - install_exceptions, DatabaseModuleError, OperationalError) |
195 | -from storm.variables import IntVariable |
196 | - |
197 | - |
198 | -install_exceptions(MySQLdb) |
199 | - |
200 | - |
201 | -compile = compile.create_child() |
202 | - |
203 | -@compile.when(Select) |
204 | -def compile_select_mysql(compile, select, state): |
205 | - if select.offset is not Undef and select.limit is Undef: |
206 | - select.limit = sys.maxint |
207 | - return compile_select(compile, select, state) |
208 | - |
209 | -@compile.when(SQLToken) |
210 | -def compile_sql_token_mysql(compile, expr, state): |
211 | - """MySQL uses ` as the escape character by default.""" |
212 | - if is_safe_token(expr) and not compile.is_reserved_word(expr): |
213 | - return expr |
214 | - return '`%s`' % expr.replace('`', '``') |
215 | - |
216 | - |
217 | -class MySQLResult(Result): |
218 | - |
219 | - @staticmethod |
220 | - def from_database(row): |
221 | - """Convert MySQL-specific datatypes to "normal" Python types. |
222 | - |
223 | - If there are any C{array} instances in the row, convert them |
224 | - to strings. |
225 | - """ |
226 | - for value in row: |
227 | - if isinstance(value, array): |
228 | - yield value.tostring() |
229 | - else: |
230 | - yield value |
231 | - |
232 | - |
233 | -class MySQLConnection(Connection): |
234 | - |
235 | - result_factory = MySQLResult |
236 | - param_mark = "%s" |
237 | - compile = compile |
238 | - |
239 | - def execute(self, statement, params=None, noresult=False): |
240 | - if (isinstance(statement, Insert) and |
241 | - statement.primary_variables is not Undef): |
242 | - |
243 | - result = Connection.execute(self, statement, params) |
244 | - |
245 | - # The lastrowid value will be set if: |
246 | - # - the table had an AUTO INCREMENT column, and |
247 | - # - the column was not set during the insert or set to 0 |
248 | - # |
249 | - # If these conditions are met, then lastrowid will be the |
250 | - # value of the first such column set. We assume that it |
251 | - # is the first undefined primary key variable. |
252 | - if result._raw_cursor.lastrowid: |
253 | - for variable in statement.primary_variables: |
254 | - if not variable.is_defined(): |
255 | - variable.set(result._raw_cursor.lastrowid, |
256 | - from_db=True) |
257 | - break |
258 | - if noresult: |
259 | - result = None |
260 | - return result |
261 | - return Connection.execute(self, statement, params, noresult) |
262 | - |
263 | - def to_database(self, params): |
264 | - for param in params: |
265 | - if isinstance(param, Variable): |
266 | - param = param.get(to_db=True) |
267 | - if isinstance(param, timedelta): |
268 | - yield str(param) |
269 | - else: |
270 | - yield param |
271 | - |
272 | - def is_disconnection_error(self, exc, extra_disconnection_errors=()): |
273 | - # http://dev.mysql.com/doc/refman/5.0/en/gone-away.html |
274 | - return (isinstance(exc, (OperationalError, |
275 | - extra_disconnection_errors)) and |
276 | - exc.args[0] in (2006, 2013)) # (SERVER_GONE_ERROR, SERVER_LOST) |
277 | - |
278 | - |
279 | -class MySQL(Database): |
280 | - |
281 | - connection_factory = MySQLConnection |
282 | - _converters = None |
283 | - |
284 | - def __init__(self, uri): |
285 | - super(MySQL, self).__init__(uri) |
286 | - if MySQLdb is dummy: |
287 | - raise DatabaseModuleError("'MySQLdb' module not found") |
288 | - self._connect_kwargs = {} |
289 | - if uri.database is not None: |
290 | - self._connect_kwargs["db"] = uri.database |
291 | - if uri.host is not None: |
292 | - self._connect_kwargs["host"] = uri.host |
293 | - if uri.port is not None: |
294 | - self._connect_kwargs["port"] = uri.port |
295 | - if uri.username is not None: |
296 | - self._connect_kwargs["user"] = uri.username |
297 | - if uri.password is not None: |
298 | - self._connect_kwargs["passwd"] = uri.password |
299 | - for option in ["unix_socket"]: |
300 | - if option in uri.options: |
301 | - self._connect_kwargs[option] = uri.options.get(option) |
302 | - |
303 | - if self._converters is None: |
304 | - # MySQLdb returns a timedelta by default on TIME fields. |
305 | - converters = MySQLdb.converters.conversions.copy() |
306 | - converters[MySQLdb.converters.FIELD_TYPE.TIME] = _convert_time |
307 | - self.__class__._converters = converters |
308 | - |
309 | - self._connect_kwargs["conv"] = self._converters |
310 | - self._connect_kwargs["use_unicode"] = True |
311 | - self._connect_kwargs["charset"] = uri.options.get("charset", "utf8") |
312 | - |
313 | - def raw_connect(self): |
314 | - raw_connection = MySQLdb.connect(**self._connect_kwargs) |
315 | - |
316 | - # Here is another sad story about bad transactional behavior. MySQL |
317 | - # offers a feature to automatically reconnect dropped connections. |
318 | - # What sounds like a dream, is actually a nightmare for anyone who |
319 | - # is dealing with transactions. When a reconnection happens, the |
320 | - # currently running transaction is transparently rolled back, and |
321 | - # everything that was being done is lost, without notice. Not only |
322 | - # that, but the connection may be put back in AUTOCOMMIT mode, even |
323 | - # when that's not the default MySQLdb behavior. The MySQL developers |
324 | - # quickly understood that this is a terrible idea, and removed the |
325 | - # behavior in MySQL 5.0.3. Unfortunately, Debian and Ubuntu still |
326 | - # have a patch for the MySQLdb module which *reenables* that |
327 | - # behavior by default even past version 5.0.3 of MySQL. |
328 | - # |
329 | - # Some links: |
330 | - # http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html |
331 | - # http://dev.mysql.com/doc/refman/5.0/en/mysql-reconnect.html |
332 | - # http://dev.mysql.com/doc/refman/5.0/en/gone-away.html |
333 | - # |
334 | - # What we do here is to explore something that is a very weird |
335 | - # side-effect, discovered by reading the code. When we call the |
336 | - # ping() with a False argument, the automatic reconnection is |
337 | - # disabled in a *permanent* way for this connection. The argument |
338 | - # to ping() is new in 1.2.2, though. |
339 | - if MySQLdb.version_info >= (1, 2, 2): |
340 | - raw_connection.ping(False) |
341 | - |
342 | - return raw_connection |
343 | - |
344 | - |
345 | -create_from_uri = MySQL |
346 | - |
347 | - |
348 | -def _convert_time(time_str): |
349 | - h, m, s = time_str.split(":") |
350 | - if "." in s: |
351 | - f = float(s) |
352 | - s = int(f) |
353 | - return time(int(h), int(m), s, (f-s)*1000000) |
354 | - return time(int(h), int(m), int(s), 0) |
355 | - |
356 | - |
357 | -# -------------------------------------------------------------------- |
358 | -# Reserved words, MySQL specific |
359 | - |
360 | -# The list of reserved words here are MySQL specific. SQL92 reserved words |
361 | -# are registered in storm.expr, near the "Reserved words, from SQL1992" |
362 | -# comment. The reserved words here were taken from: |
363 | -# |
364 | -# http://dev.mysql.com/doc/refman/5.4/en/reserved-words.html |
365 | -compile.add_reserved_words(""" |
366 | - accessible analyze asensitive before bigint binary blob call change |
367 | - condition current_user database databases day_hour day_microsecond |
368 | - day_minute day_second delayed deterministic distinctrow div dual each |
369 | - elseif enclosed escaped exit explain float4 float8 force fulltext |
370 | - high_priority hour_microsecond hour_minute hour_second if ignore index |
371 | - infile inout int1 int2 int3 int4 int8 iterate keys kill leave limit linear |
372 | - lines load localtime localtimestamp lock long longblob longtext loop |
373 | - low_priority master_ssl_verify_server_cert mediumblob mediumint mediumtext |
374 | - middleint minute_microsecond minute_second mod modifies no_write_to_binlog |
375 | - optimize optionally out outfile purge range read_write reads regexp |
376 | - release rename repeat replace require return rlike schemas |
377 | - second_microsecond sensitive separator show spatial specific |
378 | - sql_big_result sql_calc_found_rows sql_small_result sqlexception |
379 | - sqlwarning ssl starting straight_join terminated tinyblob tinyint tinytext |
380 | - trigger undo unlock unsigned use utc_date utc_time utc_timestamp varbinary |
381 | - varcharacter while xor year_month zerofill |
382 | - """.split()) |
383 | |
384 | === modified file 'tests/databases/base.py' |
385 | --- tests/databases/base.py 2015-06-15 12:06:44 +0000 |
386 | +++ tests/databases/base.py 2017-02-08 11:47:07 +0000 |
387 | @@ -330,8 +330,6 @@ |
388 | that works with data in memory, in fact) becomes a dangerous thing. |
389 | |
390 | For PostgreSQL, isolation level must be SERIALIZABLE. |
391 | - For MySQL, isolation level must be REPEATABLE READ (the default), |
392 | - and the InnoDB engine must be in use. |
393 | For SQLite, the isolation level already is SERIALIZABLE when not |
394 | in autocommit mode. OTOH, PySQLite is nuts regarding transactional |
395 | behavior, and will easily offer READ COMMITTED behavior inside a |
396 | |
397 | === removed file 'tests/databases/mysql.py' |
398 | --- tests/databases/mysql.py 2009-09-21 17:03:08 +0000 |
399 | +++ tests/databases/mysql.py 1970-01-01 00:00:00 +0000 |
400 | @@ -1,145 +0,0 @@ |
401 | -# |
402 | -# Copyright (c) 2006, 2007 Canonical |
403 | -# |
404 | -# Written by Gustavo Niemeyer <gustavo@niemeyer.net> |
405 | -# |
406 | -# This file is part of Storm Object Relational Mapper. |
407 | -# |
408 | -# Storm is free software; you can redistribute it and/or modify |
409 | -# it under the terms of the GNU Lesser General Public License as |
410 | -# published by the Free Software Foundation; either version 2.1 of |
411 | -# the License, or (at your option) any later version. |
412 | -# |
413 | -# Storm is distributed in the hope that it will be useful, |
414 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
415 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
416 | -# GNU Lesser General Public License for more details. |
417 | -# |
418 | -# You should have received a copy of the GNU Lesser General Public License |
419 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
420 | -# |
421 | -import os |
422 | - |
423 | -from storm.databases.mysql import MySQL |
424 | -from storm.database import create_database |
425 | -from storm.expr import Column, Insert |
426 | -from storm.uri import URI |
427 | -from storm.variables import IntVariable, UnicodeVariable |
428 | - |
429 | -from tests.databases.base import ( |
430 | - DatabaseTest, DatabaseDisconnectionTest, UnsupportedDatabaseTest) |
431 | -from tests.helper import TestHelper |
432 | - |
433 | - |
434 | -class MySQLTest(DatabaseTest, TestHelper): |
435 | - |
436 | - supports_microseconds = False |
437 | - |
438 | - def is_supported(self): |
439 | - return bool(os.environ.get("STORM_MYSQL_URI")) |
440 | - |
441 | - def create_database(self): |
442 | - self.database = create_database(os.environ["STORM_MYSQL_URI"]) |
443 | - |
444 | - def create_tables(self): |
445 | - self.connection.execute("CREATE TABLE number " |
446 | - "(one INTEGER, two INTEGER, three INTEGER)") |
447 | - self.connection.execute("CREATE TABLE test " |
448 | - "(id INT AUTO_INCREMENT PRIMARY KEY," |
449 | - " title VARCHAR(50)) ENGINE=InnoDB") |
450 | - self.connection.execute("CREATE TABLE datetime_test " |
451 | - "(id INT AUTO_INCREMENT PRIMARY KEY," |
452 | - " dt TIMESTAMP, d DATE, t TIME, td TEXT) " |
453 | - "ENGINE=InnoDB") |
454 | - self.connection.execute("CREATE TABLE bin_test " |
455 | - "(id INT AUTO_INCREMENT PRIMARY KEY," |
456 | - " b BLOB) ENGINE=InnoDB") |
457 | - |
458 | - def test_wb_create_database(self): |
459 | - database = create_database("mysql://un:pw@ht:12/db?unix_socket=us") |
460 | - self.assertTrue(isinstance(database, MySQL)) |
461 | - for key, value in [("db", "db"), ("host", "ht"), ("port", 12), |
462 | - ("user", "un"), ("passwd", "pw"), |
463 | - ("unix_socket", "us")]: |
464 | - self.assertEquals(database._connect_kwargs.get(key), value) |
465 | - |
466 | - def test_charset_defaults_to_utf8(self): |
467 | - result = self.connection.execute("SELECT @@character_set_client") |
468 | - self.assertEquals(result.get_one(), ("utf8",)) |
469 | - |
470 | - def test_charset_option(self): |
471 | - uri = URI(os.environ["STORM_MYSQL_URI"]) |
472 | - uri.options["charset"] = "ascii" |
473 | - database = create_database(uri) |
474 | - connection = database.connect() |
475 | - result = connection.execute("SELECT @@character_set_client") |
476 | - self.assertEquals(result.get_one(), ("ascii",)) |
477 | - |
478 | - def test_get_insert_identity(self): |
479 | - # Primary keys are filled in during execute() for MySQL |
480 | - pass |
481 | - |
482 | - def test_get_insert_identity_composed(self): |
483 | - # Primary keys are filled in during execute() for MySQL |
484 | - pass |
485 | - |
486 | - def test_execute_insert_auto_increment_primary_key(self): |
487 | - id_column = Column("id", "test") |
488 | - id_variable = IntVariable() |
489 | - title_column = Column("title", "test") |
490 | - title_variable = UnicodeVariable(u"testing") |
491 | - |
492 | - # This is not part of the table. It is just used to show that |
493 | - # only one primary key variable is set from the insert ID. |
494 | - dummy_column = Column("dummy", "test") |
495 | - dummy_variable = IntVariable() |
496 | - |
497 | - insert = Insert({title_column: title_variable}, |
498 | - primary_columns=(id_column, dummy_column), |
499 | - primary_variables=(id_variable, dummy_variable)) |
500 | - self.connection.execute(insert) |
501 | - self.assertTrue(id_variable.is_defined()) |
502 | - self.assertFalse(dummy_variable.is_defined()) |
503 | - |
504 | - # The newly inserted row should have the maximum id value for |
505 | - # the table. |
506 | - result = self.connection.execute("SELECT MAX(id) FROM test") |
507 | - self.assertEqual(result.get_one()[0], id_variable.get()) |
508 | - |
509 | - def test_mysql_specific_reserved_words(self): |
510 | - reserved_words = """ |
511 | - accessible analyze asensitive before bigint binary blob call |
512 | - change condition current_user database databases day_hour |
513 | - day_microsecond day_minute day_second delayed deterministic |
514 | - distinctrow div dual each elseif enclosed escaped exit explain |
515 | - float4 float8 force fulltext high_priority hour_microsecond |
516 | - hour_minute hour_second if ignore index infile inout int1 int2 |
517 | - int3 int4 int8 iterate keys kill leave limit linear lines load |
518 | - localtime localtimestamp lock long longblob longtext loop |
519 | - low_priority master_ssl_verify_server_cert mediumblob mediumint |
520 | - mediumtext middleint minute_microsecond minute_second mod modifies |
521 | - no_write_to_binlog optimize optionally out outfile purge range |
522 | - read_write reads regexp release rename repeat replace require |
523 | - return rlike schemas second_microsecond sensitive separator show |
524 | - spatial specific sql_big_result sql_calc_found_rows |
525 | - sql_small_result sqlexception sqlwarning ssl starting |
526 | - straight_join terminated tinyblob tinyint tinytext trigger undo |
527 | - unlock unsigned use utc_date utc_time utc_timestamp varbinary |
528 | - varcharacter while xor year_month zerofill |
529 | - """.split() |
530 | - for word in reserved_words: |
531 | - self.assertTrue(self.connection.compile.is_reserved_word(word), |
532 | - "Word missing: %s" % (word,)) |
533 | - |
534 | - |
535 | -class MySQLUnsupportedTest(UnsupportedDatabaseTest, TestHelper): |
536 | - |
537 | - dbapi_module_names = ["MySQLdb"] |
538 | - db_module_name = "mysql" |
539 | - |
540 | - |
541 | -class MySQLDisconnectionTest(DatabaseDisconnectionTest, TestHelper): |
542 | - |
543 | - environment_variable = "STORM_MYSQL_URI" |
544 | - host_environment_variable = "STORM_MYSQL_HOST_URI" |
545 | - default_port = 3306 |
546 | |
547 | === modified file 'tests/store/base.py' |
548 | --- tests/store/base.py 2016-05-13 18:55:24 +0000 |
549 | +++ tests/store/base.py 2017-02-08 11:47:07 +0000 |
550 | @@ -869,8 +869,6 @@ |
551 | result2 = self.store.find(Foo, Foo.id != 10) |
552 | self.assertEquals(foo in result1.union(result2), True) |
553 | |
554 | - if self.__class__.__name__.startswith("MySQL"): |
555 | - return |
556 | self.assertEquals(foo in result1.intersection(result2), False) |
557 | self.assertEquals(foo in result1.intersection(result1), True) |
558 | self.assertEquals(foo in result1.difference(result2), True) |
559 | @@ -1322,8 +1320,6 @@ |
560 | result2 = self.store.find((Foo, Bar), Bar.foo_id == Foo.id) |
561 | self.assertEquals((foo, bar) in result1.union(result2), True) |
562 | |
563 | - if self.__class__.__name__.startswith("MySQL"): |
564 | - return |
565 | self.assertEquals((foo, bar) in result1.intersection(result2), True) |
566 | self.assertEquals((foo, bar) in result1.difference(result2), False) |
567 | |
568 | @@ -1440,8 +1436,6 @@ |
569 | result2 = self.store.find(Foo.title) |
570 | self.assertEquals(u"Title 10" in result1.union(result2), True) |
571 | |
572 | - if self.__class__.__name__.startswith("MySQL"): |
573 | - return |
574 | self.assertEquals(u"Title 10" in result1.intersection(result2), True) |
575 | self.assertEquals(u"Title 10" in result1.difference(result2), False) |
576 | |
577 | @@ -5633,9 +5627,6 @@ |
578 | self.assertEquals(result3.avg(Foo.id), 10) |
579 | |
580 | def test_result_difference(self): |
581 | - if self.__class__.__name__.startswith("MySQL"): |
582 | - return |
583 | - |
584 | result1 = self.store.find(Foo) |
585 | result2 = self.store.find(Foo, id=20) |
586 | result3 = result1.difference(result2) |
587 | @@ -5653,9 +5644,6 @@ |
588 | ]) |
589 | |
590 | def test_result_difference_with_empty(self): |
591 | - if self.__class__.__name__.startswith("MySQL"): |
592 | - return |
593 | - |
594 | result1 = self.store.find(Foo, id=30) |
595 | result2 = EmptyResultSet() |
596 | |
597 | @@ -5666,17 +5654,11 @@ |
598 | ]) |
599 | |
600 | def test_result_difference_incompatible(self): |
601 | - if self.__class__.__name__.startswith("MySQL"): |
602 | - return |
603 | - |
604 | result1 = self.store.find(Foo, id=10) |
605 | result2 = self.store.find(Bar, id=100) |
606 | self.assertRaises(FeatureError, result1.difference, result2) |
607 | |
608 | def test_result_difference_count(self): |
609 | - if self.__class__.__name__.startswith("MySQL"): |
610 | - return |
611 | - |
612 | result1 = self.store.find(Foo) |
613 | result2 = self.store.find(Foo, id=20) |
614 | |
615 | @@ -5694,9 +5676,6 @@ |
616 | self.assertEquals(result2.count(), 3) |
617 | |
618 | def test_result_intersection(self): |
619 | - if self.__class__.__name__.startswith("MySQL"): |
620 | - return |
621 | - |
622 | result1 = self.store.find(Foo) |
623 | result2 = self.store.find(Foo, Foo.id.is_in((10, 30))) |
624 | result3 = result1.intersection(result2) |
625 | @@ -5714,9 +5693,6 @@ |
626 | ]) |
627 | |
628 | def test_result_intersection_with_empty(self): |
629 | - if self.__class__.__name__.startswith("MySQL"): |
630 | - return |
631 | - |
632 | result1 = self.store.find(Foo, id=30) |
633 | result2 = EmptyResultSet() |
634 | result3 = result1.intersection(result2) |
635 | @@ -5724,17 +5700,11 @@ |
636 | self.assertEquals(len(list(result3)), 0) |
637 | |
638 | def test_result_intersection_incompatible(self): |
639 | - if self.__class__.__name__.startswith("MySQL"): |
640 | - return |
641 | - |
642 | result1 = self.store.find(Foo, id=10) |
643 | result2 = self.store.find(Bar, id=100) |
644 | self.assertRaises(FeatureError, result1.intersection, result2) |
645 | |
646 | def test_result_intersection_count(self): |
647 | - if self.__class__.__name__.startswith("MySQL"): |
648 | - return |
649 | - |
650 | result1 = self.store.find(Foo, Foo.id.is_in((10, 20))) |
651 | result2 = self.store.find(Foo, Foo.id.is_in((10, 30))) |
652 | result3 = result1.intersection(result2) |
653 | |
654 | === removed file 'tests/store/mysql.py' |
655 | --- tests/store/mysql.py 2011-02-14 12:17:54 +0000 |
656 | +++ tests/store/mysql.py 1970-01-01 00:00:00 +0000 |
657 | @@ -1,106 +0,0 @@ |
658 | -# |
659 | -# Copyright (c) 2006, 2007 Canonical |
660 | -# |
661 | -# Written by Gustavo Niemeyer <gustavo@niemeyer.net> |
662 | -# |
663 | -# This file is part of Storm Object Relational Mapper. |
664 | -# |
665 | -# Storm is free software; you can redistribute it and/or modify |
666 | -# it under the terms of the GNU Lesser General Public License as |
667 | -# published by the Free Software Foundation; either version 2.1 of |
668 | -# the License, or (at your option) any later version. |
669 | -# |
670 | -# Storm is distributed in the hope that it will be useful, |
671 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
672 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
673 | -# GNU Lesser General Public License for more details. |
674 | -# |
675 | -# You should have received a copy of the GNU Lesser General Public License |
676 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
677 | -# |
678 | -import os |
679 | - |
680 | -from storm.database import create_database |
681 | - |
682 | -from tests.store.base import StoreTest, EmptyResultSetTest |
683 | -from tests.helper import TestHelper |
684 | - |
685 | - |
686 | -class MySQLStoreTest(TestHelper, StoreTest): |
687 | - |
688 | - def setUp(self): |
689 | - TestHelper.setUp(self) |
690 | - StoreTest.setUp(self) |
691 | - |
692 | - def tearDown(self): |
693 | - TestHelper.tearDown(self) |
694 | - StoreTest.tearDown(self) |
695 | - |
696 | - def is_supported(self): |
697 | - return bool(os.environ.get("STORM_MYSQL_URI")) |
698 | - |
699 | - def create_database(self): |
700 | - self.database = create_database(os.environ["STORM_MYSQL_URI"]) |
701 | - |
702 | - def create_tables(self): |
703 | - connection = self.connection |
704 | - connection.execute("CREATE TABLE foo " |
705 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
706 | - " title VARCHAR(50) DEFAULT 'Default Title') " |
707 | - "ENGINE=InnoDB") |
708 | - connection.execute("CREATE TABLE bar " |
709 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
710 | - " foo_id INTEGER, title VARCHAR(50)) " |
711 | - "ENGINE=InnoDB") |
712 | - connection.execute("CREATE TABLE bin " |
713 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
714 | - " bin BLOB, foo_id INTEGER) " |
715 | - "ENGINE=InnoDB") |
716 | - connection.execute("CREATE TABLE link " |
717 | - "(foo_id INTEGER, bar_id INTEGER," |
718 | - " PRIMARY KEY (foo_id, bar_id)) " |
719 | - "ENGINE=InnoDB") |
720 | - connection.execute("CREATE TABLE money " |
721 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
722 | - " value NUMERIC(6,4)) " |
723 | - "ENGINE=InnoDB") |
724 | - connection.execute("CREATE TABLE selfref " |
725 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
726 | - " title VARCHAR(50)," |
727 | - " selfref_id INTEGER," |
728 | - " INDEX (selfref_id)," |
729 | - " FOREIGN KEY (selfref_id) REFERENCES selfref(id)) " |
730 | - "ENGINE=InnoDB") |
731 | - connection.execute("CREATE TABLE foovalue " |
732 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
733 | - " foo_id INTEGER," |
734 | - " value1 INTEGER, value2 INTEGER) " |
735 | - "ENGINE=InnoDB") |
736 | - connection.execute("CREATE TABLE unique_id " |
737 | - "(id VARCHAR(36) PRIMARY KEY) " |
738 | - "ENGINE=InnoDB") |
739 | - connection.commit() |
740 | - |
741 | - |
742 | -class MySQLEmptyResultSetTest(TestHelper, EmptyResultSetTest): |
743 | - |
744 | - def setUp(self): |
745 | - TestHelper.setUp(self) |
746 | - EmptyResultSetTest.setUp(self) |
747 | - |
748 | - def tearDown(self): |
749 | - TestHelper.tearDown(self) |
750 | - EmptyResultSetTest.tearDown(self) |
751 | - |
752 | - def is_supported(self): |
753 | - return bool(os.environ.get("STORM_MYSQL_URI")) |
754 | - |
755 | - def create_database(self): |
756 | - self.database = create_database(os.environ["STORM_MYSQL_URI"]) |
757 | - |
758 | - def create_tables(self): |
759 | - self.connection.execute("CREATE TABLE foo " |
760 | - "(id INT PRIMARY KEY AUTO_INCREMENT," |
761 | - " title VARCHAR(50) DEFAULT 'Default Title') " |
762 | - "ENGINE=InnoDB") |
763 | - self.connection.commit() |
764 | |
765 | === modified file 'tests/tutorial.txt' |
766 | --- tests/tutorial.txt 2010-08-02 17:42:54 +0000 |
767 | +++ tests/tutorial.txt 2017-02-08 11:47:07 +0000 |
768 | @@ -43,14 +43,14 @@ |
769 | >>> |
770 | }}} |
771 | |
772 | -Three databases are supported at the moment: SQLite, MySQL and PostgreSQL. |
773 | +Two databases are supported at the moment: SQLite and PostgreSQL. |
774 | The parameter passed to `create_database()` is an URI, as follows: |
775 | |
776 | {{{ |
777 | database = create_database("scheme://username:password@hostname:port/database_name") |
778 | }}} |
779 | |
780 | -The scheme may be "sqlite", "postgres", or "mysql". |
781 | +The scheme may be "sqlite" or "postgres". |
782 | |
783 | Now we have to create the table that will actually hold the data |
784 | for our class. |
Thank you for the amazing branch. Since we are the only consumers of this project, makes sense to drop mysql and eventually sqlite support.
This merge proposal inspired me to reflect on life for about six months.