Используя названия сервиса Oracle с с SQLAlchemy
я столкнулся с неприятной маленькой проблемой подключения к схеме Oracle через SQLAlchemy с использованием имени службы. Вот мой код как сценарий. (элементы между угловыми скобками являются держателями для реальных значений по соображениям безопасности)
from sqlalchemy import create_engine
if __name__ == "__main__":
engine = create_engine("oracle+cx_oracle://<username>:<password>@<host>/devdb")
result = engine.execute("create table test_table (id NUMBER(6), name VARCHAR2(15) not NULL)")
result = engine.execute("drop table test_table")
где "devdb" - это имя службы, а не SID. Результатом выполнения этого сценария является трассировка стека.
(oracle-test)[1]jgoodell@jgoodell-MBP:python$ python example.py
Traceback (most recent call last):
File "example.py", line 8, in <module>
result = engine.execute("create table test_table (id NUMBER(6), name VARCHAR2(15) not NULL)")
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/base.py", line 1621, in execute
connection = self.contextual_connect(close_with_result=True)
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/base.py", line 1669, in contextual_connect
self.pool.connect(),
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 272, in connect
return _ConnectionFairy(self).checkout()
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 425, in __init__
rec = self._connection_record = pool._do_get()
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 777, in _do_get
con = self._create_connection()
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 225, in _create_connection
return _ConnectionRecord(self)
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 318, in __init__
self.connection = self.__connect()
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 368, in __connect
connection = self.__pool._creator()
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/strategies.py", line 80, in connect
return dialect.connect(*cargs, **cparams)
File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/default.py", line 279, in connect
return self.dbapi.connect(*cargs, **cparams)
sqlalchemy.exc.DatabaseError: (DatabaseError) ORA-12505: TNS:listener does not currently know of SID given in connect descriptor
None None
Если бы "devdb" был SID, а не именем службы, этот пример работал бы просто отлично, я пытался по-другому перестановки строки соединения, но не нашли ничего, что работает. В документации SQLAlchemy также нет ничего, что явно объясняет, как обрабатывать имена служб Sid verses для подключений Oracle.
3 ответов
Я нашел ответ, вы должны использовать ту же строку подключения, которая будет использоваться в файл tnsnames.файл ora в строке подключения после ' @ " like so
from sqlalchemy import create_engine
if __name__ == "__main__":
engine = create_engine("oracle+cx_oracle://<username>:<password>@(DESCRIPTION = (LOAD_BALANCE=on) (FAILOVER=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = <host>)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = devdb)))")
result = engine.execute("create table test_table (id NUMBER(6), name VARCHAR2(15) not NULL)")
result = engine.execute("drop table test_table")
этот пример работает просто отлично, и вы можете прокомментировать оператор drop и проверить DB, чтобы увидеть, что таблица была создана.
import cx_Oracle
dsnStr = cx_Oracle.makedsn('myhost','port','MYSERVICENAME')
dsnStr = dsnStr.replace('SID', 'SERVICE_NAME')
connect_str = 'oracle://user:password@' + dnsStr
makedns создаст TNS, как это:
(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=myhost)(PORT=1530)))(CONNECT_DATA=(SID=MYSERVICENAME)))
замена "SID" на "SERVICE_TYPE" заставила его работать на меня.
Если вы используете колбу, sqlalchemy и oracle:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import cx_Oracle
app = Flask(__name__)
dnsStr = cx_Oracle.makedsn('my.host.com', '1530', 'my.service.name')
dnsStr = dnsString.replace('SID', 'SERVICE_NAME')
app.config['SQLALCHEMY_DATABASE_URI'] = 'oracle://myschema:mypassword@' + dnsStr
db = SQLAlchemy(app)
cx_Oracle поддерживает передачу service_name функции makedsn.
http://cx-oracle.sourceforge.net/html/module.html?highlight=makedsn#cx_Oracle.makedsn
было бы неплохо, если бы API create_engine() передал service_name до базового вызова, который он делает для makedsn...что-то вроде этого:--2-->
oracle = create_engine('oracle://user:pw@host:port', service_name='myservice')
TypeError: Invalid argument(s) 'service_name' sent to create_engine(), using configuration OracleDialect_cx_oracle/QueuePool/Engine.
Please check that the keyword arguments are appropriate for this combination of components.