先日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に関連する機能を備えたのは初めてだと思いますので、ご紹介しました。