Python - sqlalchemy - oracle connection fails to connect to SID - python

I am trying to use sqlalchemy to connect to an oracle DB. I was expecting the following to work given that it appears the exact syntax is shown in the sqlalchemy documentation.
oracle_db = sqlalchemy.create_engine('oracle://user:pass#server:1521/dev')
but this results in the error:
dsn = self.dbapi.makedsn(url.host, port, **makedsn_kwargs)
TypeError: makedsn() takes no keyword arguments
The following call initially works without the service name
oracle_db = sqlalchemy.create_engine('oracle://user:pass#server:1521')
But when trying to connect it fails with an error complaining that the SERVICE_NAME was not provided.
ORA-12504: TNS:listener was not given the SERVICE_NAME in CONNECT_DATA
Oddly this works with cx_Oracle directly:
con = cx_Oracle.connect('user/pass#server:1521/dev')
How am I supposed to connect to the specific service?
Attempts
I have tried to use cx_Oracle.makedsn() explicitly from this question with no luck as well.
Trying to use ? options in the connection string
oracle_db = sqlalchemy.create_engine('oracle://user:pass#server:1521/?sid=dev')
works initially but when I try oracle_db.connect() I get the same ORA-12504 error shown above.

Based on the documentation at Sqlalchemy Documentation, you should probably use the cx_oracle engine. The connect string is:
oracle+cx_oracle://user:pass#host:port/dbname[?key=value&key=value...]
with an option of service_name or sid as follows:
oracle+cx_oracle://user:pass#host:1521/?service_name=hr
oracle+cx_oracle://user:pass#host:1521/?sid=hr

Try using this connection string:
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)))")
It worked for me.

Related

Sybase Connection with Flask sqlAlchemy

Im trying to connect Sybase database with flask SQLalchemy using a ODBC connection .
My connection string :
'SQLALCHEMY_DATABASE_URI' = "sybase+pyodbc://username:passw#rd#host:port/dbname?driver=Adaptive+Server+Enterprise"
Getting this Error :
But I'm pretty sure i'm using the right port in the connection string. But when i try connect to connect to the same instance using this method .
Second method :
con = pyodbc.connect(server=server ,port=port ,username=username ,password=password ,driver=driver)
The connection works perfectly fine now with the same connection details.
Can anyone help me in building the connection string URL and help me fix this. Cause I want to use the "db Object" instead of "cursor Object ".
As noted in the Getting Connected wiki page, "Hostname Connections" are not supported. You can either create an ODBC DSN, or use your pyodbc connection string with an ODBC direct pass-through connection:
import urllib
from sqlalchemy import create_engine
connection_string = (
'DRIVER=SAP ASE ODBC driver;'
'SERVER=centos7-vm01;'
'PORT=5000;'
'UID=scott;PWD=tiger;'
'DATABASE=mydatabase;'
'charset=utf8;'
)
connection_uri = f"sybase+pyodbc:///?odbc_connect={urllib.parse.quote_plus(connection_string)}"
engine = create_engine(connection_uri)
After so much research I found that, this error is due incorrect parameter in the connection string.
Invalid port number error will be also fixed with this block of code .
Code here :
import urllib
connection_string = (
'DRIVER=Adaptive Server Enterprise;'
'SERVER=server;'
'PORT=port;'
'UID=username;PWD=password;'
'DATABASE=dbname;'
)
connection_uri = f"sybase+pyodbc:///?odbc_connect={urllib.parse.quote_plus(connection_string)}"
SQLALCHEMY_DATABASE_URI = connection_uri # connection string for SQLALchemy
This code is best solution .

sqlalchemy connect "ERROR:function schema_name does not exist"

I have the following python code
cred_dict = load_credentials()
user = cred_dict["user"]
pwd = cred_dict["pwd"]
host = cred_dict["host"]
port = cred_dict["port"]
db = cred_dict["db"]
schema = cred_dict["schema"]
con_string = f'Driver={{PostgreSQL Unicode}};Server={host};Database={db};Port={port};UID={user};PWD={pwd};'
params = urllib.parse.quote_plus(con_string)
con = sqlalchemy.create_engine("mssql:///?odbc_connect=%s" % params,fast_executemany=True)
con = con.connect()
but I keep getting ERROR: function schema_name() does not exist.
I have tested different drivers but im using the PostgreSQL Unicode for my DSN setting in ODBC, and it is working w/o any issues. I have also tried mssql+pyodbc but it does not do any difference.
Note, I cannot use pyodbc.connect("DSN="+DSN) since I have to use an sqlalchemy-connection for our production-environment
I managed to work around it by installing psycopg2 and then do
con = create_engine(f'postgresql://{self.user}:{self.pwd}#{self.host}:{self.port}/{self.db}')
which then is passed to
df = pd.read_sql(query=query, con=con)

Python ORA-12504 error when using EZConnect syntax

I'm using the python Oracle driver and Easy Connection syntax, but receiving an error:
DatabaseError: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
import cx_Oracle as cx_Oracle
def query_route():
con = cx_Oracle.connect('user/pwd#10.1.1.24:1521/service')
The server is version 11.2.0.4.0.
Following Christopher Jones's answer below, I tried:
con = cx_Oracle("oracle+cx_oracle://user:pwd#(DESCRIPTION = (LOAD_BALANCE=on) (FAILOVER=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = 10.1.1.24)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = service)))")
which failed with the same error
I found a link that suggested I need the service name, but I'm using the service after the slash.
What am I doing wrong?
Update: as Christoper Jones said, the problem was the service name, and my mistake was assuming I knew how that was determined. I had copied from a jdbc datasource:
jdbc:oracle:thin:#10.1.1.24:1521:foo
however, in the python code, I needed to use:
mgowner/mgowner#10.1.1.24/foo.mycompany.com
The hostname and port sections are OK: you're getting through to the Oracle Network listener. Somehow you need to find the correct service name, perhaps by running lsnrctl status on the machine hosting the database, or executing show parameter service_names in SQL*Plus (as a privileged user). If you have PDBS, query one of the PDB views.
If your DB is very old, it may be using a SID, not a service name so you will have to construct the long connection string like "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 10.1.1.24)(PORT = 1521)) (CONNECT_DATA = (SID = whatever) ) )"

Sqlalchemy pyodbc DNS-less URL connection string does not seem to work

I am trying to connect to a Sybase ASE 15 database on windows using sqlalchemy (1.0.9) and pyodbc. If I use a DNS url everything works as expected:
url = r'sybase+pyodbc://usename:password#dns'
engine = create_engine(url, echo=True)
Session = sessionmaker(bind=engine)
sess = Session()
conn = sess.connection()
However, if I avoid the DNS I get an error message:
url = 'sybase+pyodbc://username:password#host:port/database?driver=Adaptive Server Enterprise'
, I get an error:
DBAPIError: (pyodbc.Error) ('01S00', '[01S00] [SAP][ASE ODBC
Driver]Invalid port number (30011) (SQLDriverConnect)')
The port number is correct and it is the same port as specified in the DNS.
Any ideas?
It may be worth trying to work with some version of the semicolon-separated DSN-less format used by pyODBC (and ODBC in general). Some examples here:
http://www.connectionstrings.com/adaptive-server-enterprise-odbc-driver/
This question tackles a similar issue with FreeTDS, but the concept is the same, as the connect string is basically passed through to the low-level ODBC connect:
SqlAlchemy equivalent of pyodbc connect string using FreeTDS
The URL is being parsed down to this type of string for ultimate connection by pyodbc through to SQLDriverConnect (in the ODBC API), so specifying the ASE ODBC DSN-less connection string directly may work better.
Update: Ran a quick test to see what connect arguments are produced for this URL:
from sqlalchemy.engine.url import *
from sqlalchemy.connectors.pyodbc import *
connector = PyODBCConnector()
url = make_url("sybase+pyodbc://username:password#host:5555/database?driver=Adaptive Server Enterprise")
print connector.create_connect_args(url)
This results in:
[['DRIVER={Adaptive Server Enterprise};Server=host,5555;Database=database;UID=username;PWD=password'], {}]
Note that the hostname and port are separated by a comma, per http://www.connectionstrings.com/adaptive-server-enterprise-odbc-driver/tds-based-odbc-driver-from-sybase-ocs-125/, this format works for TDS-based ODBC for Sybase 12.5:
Driver={Sybase ASE ODBC Driver};NetworkAddress=myServerAddress,5000;
Db=myDataBase;Uid=myUsername;Pwd=myPassword;
However, the ASE 15 format (http://www.connectionstrings.com/adaptive-server-enterprise-odbc-driver/adaptive-server-enterprise-150/) specifies server=myServerAddress;port=myPortnumber with port as a key passed in the semicolon-delimited string:
Driver={Adaptive Server Enterprise};app=myAppName;server=myServerAddress;
port=myPortnumber;db=myDataBase;uid=myUsername;pwd=myPassword;
If you "cheat" on the port spec by using host;port=5555, you get:
[['DRIVER={Adaptive Server Enterprise};Server=host;port=5555;Database=database;UID=username;PWD=password'], {}]
But this just feels like a Bad Idea™, even if it works. I'd also note that the generated string is using Database as the key vs. Db in the Sybase connection string reference. This may prove to be an issue as well.
Using ?odbc_connect as in the linked question is probably your best option for controlling the exact connect arguments being sent to ODBC.

cx_Oracle doesn't connect when using SID instead of service name on connection string

I have a connection string that looks like this
con_str = "myuser/mypass#oracle.sub.example.com:1521/ora1"
Where ora1 is the SID of my database. Using this information in SQL Developer works fine, meaning that I can connect and query without problems.
However, if I attempt to connect to Oracle using this string, it fails.
cx_Oracle.connect(con_str)
DatabaseError: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
This connection string format works if the ora1 is a service name, though.
I have seen other questions that seem to have the reverse of my problem (it works with SID, but not Service name)
Using Oracle Service Names with SQLAlachemy
Oracle SID and Service name; connection problems
cx_Oracle & Connecting to Oracle DB Remotely
What is the proper way to connect to Oracle, using cx_Oracle, using an SID and not a service name? How do I do this without the need to adjust the TNSNAMES.ORA file? My application is distributed to many users internally and making changes to the TNSNAMES file is less than ideal when dealing with users without administrator privileges on their Windows machines. Additionally, when I use service name, I don't need to touch this file at all and would like it keep it that way.
I a similar scenario, I was able to connect to the database by using cx_Oracle.makedsn() to create a dsn string with a given SID (instead of the service name):
dsnStr = cx_Oracle.makedsn("oracle.sub.example.com", "1521", "ora1")
This returns something like
(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle.sub.example.com)(PORT=1521)))(CONNECT_DATA=(SID=ora1)))
which can then be used with cx_Oracle.connect() to connect to the database:
con = cx_Oracle.connect(user="myuser", password="mypass", dsn=dsnStr)
print con.version
con.close()
For those looking for how to specify service_name instead of SID.
From changelog for SQLAlchemy 1.0.0b1 (released on March 13, 2015):
[oracle] [feature] Added support for cx_oracle connections to a
specific service name, as opposed to a tns name, by passing
?service_name=<name> to the URL. Pull request courtesy Sławomir
Ehlert.
The change introduces new, Oracle dialect specific option service_name which can be used to build connect string like this:
from sqlalchemy import create_engine
from sqlalchemy.engine import url
connect_url = url.URL(
'oracle+cx_oracle',
username='some_username',
password='some_password',
host='some_host',
port='some_port',
query=dict(service_name='some_oracle_service_name'))
engine = create_engine(connect_url)
If you are using sqlalchemy and ORACLE 12, the following seems to work.
from sqlalchemy import create_engine
con='oracle://user:password#hostname:1521/?service_name=DDDD'
engine = create_engine(con)
Note, you have to use the service name and not the SID. I don't know why, but the simple connection string that uses SID does not work.
It still may not work. You need to take the output of dsnStr and modify the string by replacing SID with SERVICE_NAME and use that variable in the con string. This procedure worked for me.
SID's may not be easily accessible or you might not have it created for your database.
In my case, I'm working from the client side requesting access to a cloud database so creating an SID didn't really make sense.
Instead, you might have a string that looks similar to this:
"(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = something.cloud.company)
(PORT = 12345)) (ADDRESS = (PROTOCOL = TCP)(HOST = something.cloud.company)
(PORT = 12345)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME =
something.company)))"
You can use it in replacement of the SID.
connection = cx_Oracle.connect("username", "pw", "(DESCRIPTION = (ADDRESS =
(PROTOCOL = TCP)(HOST = something.cloud.company)(PORT = 12345)) (ADDRESS =
(PROTOCOL = TCP)(HOST = something.cloud.company)(PORT = 12345))
(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = something.company)))")
I thought during a while that I would not be able to use Magic SQL (%sql, %%sql) because of service name issue in connection that would force to use the alternative way described above with cx_Oracle.connect(), cx_Oracle.makedsn()... I finally found a solution working for me: declare and set a variable for the service name first and then use it in the command (since not working if literal string for service name put in the command !)
import cx_Oracle
user='youruser'
pwd='youruserpwd'
dbhost='xx.xx.xx.xx'
service='yourservice'
%load_ext sql
%sql oracle+cx_oracle://$user:$pwd#$dbhost:1521/?service_name=$service
output (what you get in successful connection):
u'Connected: youruser#'
If someone is looking to set oracle.jdbc.proxyClientName property for cx_oracle, to connect using proxyClient, they can use -
cx_Oracle.init_oracle_client("../../oracle_local_client", config_dir= "../../oracle_local_client/network/admin")
connectDsn = cx_Oracle.makedsn('db.svr.net', 'portNumberHere',service_name="TEST_READWRITE")
#replace all prams above
pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, dsn = connectDsn)
connection = pool.acquire(user="[PROXY_CLIENT_NAME]")
Notice the use of '[' braces to depict that the user is proxyClient.
I am using Kerberos authentication for this and my SQLNET.ora file contains the below properties.
NAMES.DIRECTORY_PATH=(TNSNAMES,HOSTNAME,EZCONNECT)
SQLNET.AUTHENTICATION_SERVICES = (BEQ,KERBEROS5PRE,KERBEROS5)
SQLNET.AUTHENTICATION_KERBEROS5_SERVICE=oracle
SQLNET.KERBEROS5_CC_NAME=OSMSFT:
SQLNET.KERBEROS5_CONF_MIT=TRUE
SQLNET.KERBEROS5_CONF=I:\projects\poc\resources\krb5.conf # krb5 config file complete path.
For more information, refer the video embedded in this article.
I also met this issue.
The solution is:
1: get the service name at tnsnames.ora
2: put the service name in
con_str = "myuser/mypass#oracle.sub.example.com:1521/ora1"

Categories