1 | import re |
---|
2 | from .base import SQLAdapter |
---|
3 | from ..utils import split_uri_args |
---|
4 | from . import adapters, with_connection |
---|
5 | |
---|
6 | |
---|
7 | @adapters.register_for("mysql") |
---|
8 | class MySQL(SQLAdapter): |
---|
9 | dbengine = "mysql" |
---|
10 | drivers = ("MySQLdb", "pymysql", "mysqlconnector") |
---|
11 | commit_on_alter_table = True |
---|
12 | support_distributed_transaction = True |
---|
13 | |
---|
14 | REGEX_URI = ( |
---|
15 | "^(?P<user>[^:@]+)(:(?P<password>[^@]*))?" |
---|
16 | r"@(?P<host>[^:/]*|\[[^\]]+\])(:(?P<port>\d+))?" |
---|
17 | "/(?P<db>[^?]+)" |
---|
18 | r"(\?(?P<uriargs>.*))?$" |
---|
19 | ) # set_encoding and unix_socket |
---|
20 | |
---|
21 | def _initialize_(self): |
---|
22 | super(MySQL, self)._initialize_() |
---|
23 | ruri = self.uri.split("://", 1)[1] |
---|
24 | m = re.match(self.REGEX_URI, ruri) |
---|
25 | if not m: |
---|
26 | raise SyntaxError("Invalid URI string in DAL") |
---|
27 | user = self.credential_decoder(m.group("user")) |
---|
28 | password = self.credential_decoder(m.group("password")) |
---|
29 | host = m.group("host") |
---|
30 | uriargs = m.group("uriargs") |
---|
31 | if uriargs: |
---|
32 | uri_args = split_uri_args(uriargs, need_equal=True) |
---|
33 | charset = uri_args.get("set_encoding") or "utf8" |
---|
34 | socket = uri_args.get("unix_socket") |
---|
35 | else: |
---|
36 | charset = "utf8" |
---|
37 | socket = None |
---|
38 | # NOTE: |
---|
39 | # MySQLdb (see http://mysql-python.sourceforge.net/MySQLdb.html) |
---|
40 | # use UNIX sockets and named pipes by default if no host is given |
---|
41 | # or host is 'localhost'; as opposed to |
---|
42 | # pymysql (see https://pymysql.readthedocs.io/en/latest/modules/connections.html) |
---|
43 | # or mysqlconnector (see https://dev.mysql.com/doc/connectors/en/connector-python-connectargs.html) |
---|
44 | # driver, where you have to specify the socket explicitly. |
---|
45 | if not host and not socket: |
---|
46 | raise SyntaxError("Host or UNIX socket name required") |
---|
47 | db = m.group("db") |
---|
48 | port = int(m.group("port") or "3306") |
---|
49 | self.driver_args.update(user=user, db=db, charset=charset) |
---|
50 | if password is not None: |
---|
51 | self.driver_args["passwd"] = password |
---|
52 | if socket: |
---|
53 | self.driver_args["unix_socket"] = socket |
---|
54 | else: |
---|
55 | self.driver_args.update(host=host, port=port) |
---|
56 | |
---|
57 | def connector(self): |
---|
58 | cursor_buffered = self.driver_args.get("cursor_buffered") |
---|
59 | if cursor_buffered: |
---|
60 | del self.driver_args["cursor_buffered"] |
---|
61 | conn = self.driver.connect(**self.driver_args) |
---|
62 | if cursor_buffered: |
---|
63 | conn.cursor = lambda conn=conn: conn.cursor(buffered=True) |
---|
64 | return conn |
---|
65 | |
---|
66 | def after_connection(self): |
---|
67 | self.execute("SET FOREIGN_KEY_CHECKS=1;") |
---|
68 | self.execute("SET sql_mode='NO_BACKSLASH_ESCAPES';") |
---|
69 | |
---|
70 | def distributed_transaction_begin(self, key): |
---|
71 | self.execute("XA START;") |
---|
72 | |
---|
73 | @with_connection |
---|
74 | def prepare(self, key): |
---|
75 | self.execute("XA END;") |
---|
76 | self.execute("XA PREPARE;") |
---|
77 | |
---|
78 | @with_connection |
---|
79 | def commit_prepared(self, key): |
---|
80 | self.execute("XA COMMIT;") |
---|
81 | |
---|
82 | @with_connection |
---|
83 | def rollback_prepared(self, key): |
---|
84 | self.execute("XA ROLLBACK;") |
---|
85 | |
---|
86 | |
---|
87 | @adapters.register_for("cubrid") |
---|
88 | class Cubrid(MySQL): |
---|
89 | dbengine = "cubrid" |
---|
90 | drivers = ("cubriddb",) |
---|
91 | |
---|
92 | def _initialize_(self): |
---|
93 | super(Cubrid, self)._initialize_() |
---|
94 | del self.driver_args["charset"] |
---|