programing

SQLChemy와 함께 Oracle 서비스 이름 사용

lastcode 2023. 7. 21. 21:40
반응형

SQLChemy와 함께 Oracle 서비스 이름 사용

서비스 이름을 사용하여 SQLAlchemy를 통해 Oracle 스키마에 연결하는 데 심각한 문제가 발생했습니다.여기 대본으로 제 코드가 있습니다.(각 괄호 사이의 괄호는 보안상의 이유로 실제 값에 대한 자리 표시자입니다.)

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이고 서비스 이름이 아닌 경우 이 예제는 제대로 작동합니다. 다른 연결 문자열의 순열을 시도했지만 제대로 작동하는 항목을 찾지 못했습니다.또한 SQL 계획 설명서에는 Oracle 연결에 대한 SID의 Version 서비스 이름을 처리하는 방법을 명시적으로 설명하는 내용이 없습니다.

저는 당신이 tnsnames.나 '@' 뒤에 있는 연결 문자열에 사용될 것과 같은 연결 문자열을 사용해야 하는 답을 찾았습니다.

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")

이 예제는 정상적으로 실행되며, 드롭 문을 주석 처리하고 DB를 확인하여 테이블이 작성되었는지 확인할 수 있습니다.

import cx_Oracle
dsnStr = cx_Oracle.makedsn('myhost','port','MYSERVICENAME')
connect_str = 'oracle://user:password@' + dsnStr.replace('SID', 'SERVICE_NAME')

Makedn은 다음과 같은 TNS를 생성합니다.

(설명=(ADDRESSION=(ADDRESS_LIST=(PROTOCOL=DICOM)(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)

이제 sqlalchemy 모듈에서 oracle service_names를 처리할 수 있습니다.확인:

from sqlalchemy.engine import create_engine

DIALECT = 'oracle'
SQL_DRIVER = 'cx_oracle'
USERNAME = 'your_username' #enter your username
PASSWORD = 'your_password' #enter your password
HOST = 'subdomain.domain.tld' #enter the oracle db host url
PORT = 1521 # enter the oracle port number
SERVICE = 'your_oracle_service_name' # enter the oracle db service name
ENGINE_PATH_WIN_AUTH = DIALECT + '+' + SQL_DRIVER + '://' + USERNAME + ':' + PASSWORD +'@' + HOST + ':' + str(PORT) + '/?service_name=' + SERVICE

engine = create_engine(ENGINE_PATH_WIN_AUTH)


#test query
import pandas as pd
test_df = pd.read_sql_query('SELECT * FROM global_name', engine)

cx_Oracle은 service_name을 makedsn 함수로 전달하는 기능을 지원합니다.

http://cx-oracle.sourceforge.net/html/module.html?highlight=makedsn#cx_Oracle.makedsn

create_engine() API가 service_name을 통해 sn에 대한 기본 호출로 전달하면 좋을 것입니다.다음과 같은 것:

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.

언급URL : https://stackoverflow.com/questions/14140902/using-oracle-service-names-with-sqlalchemy

반응형