先日Oracle Database 12c Release 1がリリースされまして、新機能ガイドを眺めていたところ気になる新機能を見つけました。
Oracle Database 12c Release 1にはliboramysql12という共有ライブラリが同梱されていて、これをlibmysqlclientと差し替えるとMySQL向けに書かれたアプリケーションがそのままOracle Databaseに対して動作するのだそうです。ドキュメントから図を引用します。

試してみましょう。MySQLにテスト用のテーブルを作ります。
mysql> CREATE TABLE myora (
-> id INT PRIMARY KEY,
-> data VARCHAR(100)
-> ) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8mb4;
Query OK, 0 rows affected (0.08 sec)
mysql> INSERT INTO myora (id, data) VALUES (1, 'マイエスキューエル');
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM myora;
+----+-----------------------------+
| id | data |
+----+-----------------------------+
| 1 | マイエスキューエル |
+----+-----------------------------+
1 row in set (0.00 sec)Oracle Databaseにも同じものを作ります。
SQL> CREATE TABLE myora (
2 id NUMBER PRIMARY KEY,
3 data VARCHAR2(400)
4 );
Table created.
SQL> INSERT INTO myora (id, data) VALUES (1, 'オラクル');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> COL data FORMAT a40
SQL> SELECT * FROM myora
ID DATA
---------- ----------------------------------------
1 オラクルしばらく試行錯誤していたのですが、PythonとMySQL-Pythonの組み合わせで動作確認がとれました。
#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function from __future__ import unicode_literals import MySQLdb connection = None cursor = None try: connection = MySQLdb.connect( db='scott', user='scott', passwd='tiger', charset='utf8') cursor = connection.cursor() cursor.execute("SELECT data FROM myora WHERE id = 1") result = cursor.fetchall() for record in result: print(record[0]) except MySQLdb.Error as e: print(repr(e)) finally: if cursor: cursor.close() if connection: connection.close()
$ python db1.py マイエスキューエル $ export LD_LIBRARY_PATH=$ORACLE_HOME/lib $ export LD_PRELOAD=$ORACLE_HOME/lib/liboramysql12.so $ python db1.py オラクル
すごいですね。ただし、LD_PRELOADを設定して実行しただけでは以下のエラーが発生していました。
Traceback (most recent call last):
File "db1.py", line 16, in <module>
cursor.execute("SELECT data FROM myora WHERE id = 1")
File "/usr/lib64/python2.6/site-packages/MySQLdb/cursors.py", line 156, in execute
query = query.encode(charset)
LookupError: unknown encoding:これはMySQL C APIのうちmysql_character_set_name()がliboramysql12には実装されておらず、空文字列を返すことによるものです。今回はMySQL-Pythonに以下のパッチをあててしのぎました。
--- cursors.py_orig 2013-07-01 00:57:30.422669501 +0900 +++ cursors.py 2013-06-30 23:01:36.461905484 +0900 @@ -152,6 +152,8 @@ del self.messages[:] db = self._get_db() charset = db.character_set_name() + if charset == '': + charset = 'utf8' if isinstance(query, unicode): query = query.encode(charset) if args is not None:
それから例外ハンドリングの違いが気になりました。以下のスクリプトでわざと一意制約違反を起こしてみます。
#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function from __future__ import unicode_literals import MySQLdb connection = None cursor = None try: connection = MySQLdb.connect( db='scott', user='scott', passwd='tiger', charset='utf8') cursor = connection.cursor() cursor.execute("INSERT INTO myora (id, data) VALUES (1, 'ポストグレス')") connection.commit() except MySQLdb.Error as e: print(repr(e)) finally: if cursor: cursor.close() if connection: connection.close()
$ python db2.py IntegrityError(1062, "Duplicate entry '1' for key 'PRIMARY'") $ export LD_LIBRARY_PATH=$ORACLE_HOME/lib $ export LD_PRELOAD=$ORACLE_HOME/lib/liboramysql12.so $ python db2.py InternalError(1, 'ORA-00001: unique constraint (SCOTT.SYS_C0010064) violated')
このように、残念ながらエラー番号のマッピングはしてくれないようです。MySQL-Pythonはエラー番号を見て例外クラスのインスタンスを生成しているため、生成されたインスタンスも誤ったものとなっています。
- 未実装APIやエラー番号の違いがあるため、MySQL-Pythonなどのドライバにかなりの修正が必要
- SQLの方言を吸収しないため、アプリケーションにもかなりの修正が必要
- MySQL用のアプリケーションをわざわざ高価なOracle Databaseに移行する動機がない
ということで現時点ではあまり実用性がない気もしますが、Oracle Database本体がMySQLに関連する機能を備えたのは初めてだと思いますので、ご紹介しました。