Arbitrary selects failing using cx_Oracle with instant client - python

I am trying to select data from an ORACLE 12c database using cx_Oracle, but I am getting the exception: "cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel".
My query behaves fine using Pycharm (jdbc:oracle:thin driver). Using cx_Oracle in python 3.6, however, the query fails unless I reduce the number of IDs in the IN clause from 500 to about 250. The Cursor.fetchall() function is what throws the exception. I do not have privileged access to the database in order to check things like locks or load, but could these be the cause of the issue? According to our DBA, there is nothing wrong on the Oracle db server, and since the query works fine otherwise, I am inclined to believe it. I have messed with the client-side sqlnet.ora as well, which has allowed exceptions to eventually be thrown instead of hanging forever, but I still cannot fetch the data.
def select(self, query, *args):
cur = self.dbh.cursor()
cur.prepare(query)
try:
cur.execute(None, args)
return cur.fetchall()
# my attempt to handle the issue
except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError) as e:
# cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel
self.logger.error('Oracle Error: {}'.format(traceback.format_exc()))
raise e
The code calls select like this. For brevity, I've omitted the full string IDs
ids = ['1', '2', '3', ...]
query = """\
select * from my_table where id in(:0,:1,:2,:3,:4, ...)
"""
self.select(query, *ids)
The query fails without the placeholders (with the IDs placed directly in the query) as well.
I expect to be able to run any select query using an IN clause with up to 1000 IDs without receiving the ORA-03113 Exception.
Edit:
I installed oracle-instantclient18.5-basic-18.5.0.0.0-3.x86_64.rpm* on ubuntu 18.04.2, have cx_Oracle version 7.1.2, and I am connecting to Oracle 12.1.0.2.0.
The query is on the underlying tables of BMC Software's ARS. I will start working to try to replicate the problem with a local table structure, but it is a mess and will take some time. If I am able to create a local copy of the tables, I'm not sure I'd be able to replicate the issue, as identical queries with different IDs work fine. That makes it seem data driven, however, after I reduced the query to 250 IDs, I swapped the 250 from the first half to the second half, and got the same success result, so it doesn't seem to be just one bad row.
Is there more helpful logging I can enable on the client side to get more information?
Edit2: I should also add that the issue does not just occur with one query. I've seen the same issue with select queries to completely different tables.
Edit3: I just found out that by commenting out some of the columns that I'm selecting also can make the query work. columns like this:
to_char(to_date('1970-01-01','YYYY-MM-DD') + numtodsinterval(EventStart,'SECOND'),'YYYY-MM-DD HH24:MI:SS')
This may indicate that some kind of timeout is being reached which may or may not be configured in my sqlnet.ora:
DISABLE_OOB=on
SQLNET.RECV_TIMEOUT=60
SQLNET.SEND_TIMEOUT=60
TCP.CONNECT_TIMEOUT=300
SQLNET.OUTBOUND_CONNECT_TIMEOUT=300
ENABLE=BROKEN
TRACE_LEVEL_CLIENT=ADMIN
TRACE_FILE_CLIENT=sqlnet
Edit 4: I've tried some more things.
I installed the same version of instant client, except on a windows 7 machine, and ran the same query against the same db instance. The query succeeded.
I also narrowed down that for this particular query, it will accept 499 IDs, but fails with 500. it doesn't matter which ID I comment out from the query.
I also tried tricking the query into thinking there were fewer IDs by using a sub-select instead:
IN(
select regexp_substr(:0,'[^,]+', 1, level) from dual connect by regexp_substr(:0, '[^,]+', 1, level) is not null
)
I got the error "cx_Oracle.DatabaseError: ORA-01460: unimplemented or unreasonable conversion requested", after which I realized made sense because Oracle will only allow a string to be up to 4000 bytes long.

I think I finally found a way to get everything functioning. I finally came across this link:
https://ardentperf.com/2010/09/08/mysterious-oracle-net-errors/
It turns out that this solved my problem. I am still having trouble getting cx_Oracle to honor a connection string of the same format as the tnsnames.ora file, but I changed my code to refer to the tnsnames.ora for now as follows:
connection_info = {
'user': self.config.get(self.db, 'user'),
'pass': self.config.get(self.db, 'password')
}
connection_string = '{user}/{pass}#TEST'\
.format(**connection_info)
connection = cx_Oracle.connect(connection_string)
where my tnsnames.ora contains the following:
TEST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(Host = myhost.com)(Port = 1521))
)
(SDU=1024)
(CONNECT_DATA =
(SID=mysid)
)
)
The key here is the SDU=1024, which inexplicably fixes this issue.
https://docs.oracle.com/cd/B28359_01/network.111/b28317/sqlnet.htm#NETRF184
Documentation from the above link indicates that the default for SDU is 8192 bytes (8 KB) and my understanding is that there is supposed to be auto-negotiation of this value. This does not appear to be the case, and I don't know what past defaults have been.

Related

How is it possible for this Python query to alternate between two different results?

I am executing the following code,
import psycopg2
import pandas as pd
#contextmanager
def get_psyopg2_connection(server='my_server', port=1234, read_only=True):
try:
conn = psycopg2.connect("<connection_string>")
conn.set_session(readonly=read_only, autocommit=True)
yield conn
except Exception as e:
raise Exception(e) from e
finally:
conn.close()
if __name__=="__main__":
with get_psyopg2_connection() as conn_dev:
df = pd.read_sql("""select last_value from public.lime_boxes_id_seq""", conn_dev)
print(df)
and I receive two different results; last_value = 6 (correct) and 33 (incorrect). When I run the same code several times, I might get a result sequence like 6, 6, 6, 33, 33, 6. It's unpredictable.
When I execute this query in DBeaver, I always get 6.
How could this be happening?
Edit:
This table is a sequence and no one is accessing this schema except for myself. Here is the content
last_value
log_cnt
is_called
6
27
true
There are 3 distinct possibilities that stand out to me:
There are multiple records in public.lime_boxes_id_seq, and since you haven't specified an ORDER BY clause, the database will not guarantee the order in which the records are returned. While this is definitely a low likelihood, it's still worth exploring what data the table being queried actually contains and to work from there.
The database server in question (my_server) is actually some sort of load balancer for a cluster of Postgres nodes behind it, and there is one or more nodes that have fallen out of sync with the rest and is returning old/incorrect data. This would explain entirely the reason your DBeaver client gets the same result each time (it's probably not connecting/disconnecting/reconnecting between queries, potentially to different servers each time and likely maintaining the same connection between queries), but your code (which connects/disconnects/reconnects each execution) does not. To alleviate this, you can connect to a known-good node in the cluster (if your networking setup permits such an arrangement), or fix the replication issue that causes the staleness of the data.
If the value in question was ever 33, it might be possible there is some sort of caching mechanism in the network between your code and the table in Postgres that is returning this erroneous data. If it was cached by this appliance/these appliances and hasn't been flushed since then, this could also be a reason why this data isn't returning what you believe it should, and a cache flush/reconfiguration would be necessary.

python pypyodbc won't select data

I think I'm going mad here... again :). I'm trying to do the most simple thing on the planet and it doesn't work for some reason unknown to me. I have a python script that connects to a mssql database using pypyodbc and does stuff. when I insert data into the database, it works. when I try to extract it, it fails miserably. what am I doing wrong?
import pypyodbc as mssql
msConnErr = None
try:
msconn = mssql.connect('DRIVER={SQL Server};SERVER=server_name;DATABASE=database;TRUSTED_CONNECTION=True')
print('Source server connected')
srcCursor = msconn.cursor()
except:
print('Source server error')
msConnErr = True
srcCursor.execute("SELECT * FROM schema.table")
srcResult = srcCursor.fetchall()
print(srcResult)
the connection works as I'm being given a successful message. I can also see my script using sql server management studio being connected to the correct database, so I know I'm working in the right environment. the error I'm getting is:
UndefinedTable: relation "schema.table" does not exist
LINE 1: SELECT * FROM schema.table
the table exists, I must specify the schema as I have the same table name in different schemas (data lifecycle). I can extract data from it using sql server management studio, yet python fails miserably. it doesn't fail to insert 35 million rows in it using the same driver. no other query works, even SELECT ##VERSION fails, SELECT TOP (10) * FROM schema.table fails etc. ...
any ideas?
basically, I had a piece of code that would rewrite the srcCursor variable with another connection, obviously that relation wouldn't be present on another server. apologies!

psycopg2 each execute yields different table and cursor not scrolling to the beginning

I am querying a postgresql database through python's psycopg2 package.
In short: The problem is psycopg2.fetchmany() yields a different table everytime I run a psydopg2.cursor.execute() command.
import psycopg2 as ps
conn = ps.connect(database='database', user='user')
nlines = 1000
tab = "some_table"
statement= """ SELECT * FROM """ + tab + " LIMIT %d;" %(nlines)
crs.execute(statement)
then I fetch the data in pieces. Running the following executes just fine and each time when I scroll back to the beginning I get the same results.
rec=crs.fetchmany(10)
crs.scroll(0, mode='absolute')
print rec[-1][-2]
However, if I run the crs.execute(statement) again and then fetch the data, it yields a completely different output. I tried running ps.connect again, do conn.rollback(), conn.reset(), crs.close() and nothing ever resulted in consisted output from the table. I also tried a named cursor with scrollable enabled
crs= conn.cursor(name="cur1")
crs.scrollable=1
...
crs.scroll(0, mode= 'absolute')
still no luck.
You don't have any ORDER BY clause in your query, and Postgres does not guarantee any particular ordering without one. It's particularly likely to change ordering for tables which have lots of churn (i.e. lots of inserts/updates/deletes).
See the Postgres SELECT doc for more details, but the most salient snippet here is this:
If the ORDER BY clause is specified, the returned rows are sorted in
the specified order. If ORDER BY is not given, the rows are returned
in whatever order the system finds fastest to produce.
I wouldn't expect any query, regardless of the type of cursor used, to necessarily return the exact same result set given a query of this type.
What happens when you add an explicit ORDER BY?

pymssql ( python module ) unable to use temporary tables

This isn't a question, so much as a pre-emptive answer. (I have gotten lots of help from this website & wanted to give back.)
I was struggling with a large bit of SQL query that was failing when I tried to run it via python using pymssql, but would run fine when directly through MS SQL. (E.g., in my case, I was using MS SQL Server Management Studio to run it outside of python.)
Then I finally discovered the problem: pymssql cannot handle temporary tables. At least not my version, which is still 1.0.1.
As proof, here is a snippet of my code, slightly altered to protect any IP issues:
conn = pymssql.connect(host=sqlServer, user=sqlID, password=sqlPwd, \
database=sqlDB)
cur = conn.cursor()
cur.execute(testQuery)
The above code FAILS (returns no data, to be specific, and spits the error "pymssql.OperationalError: No data available." if you call cur.fetchone() ) if I call it with testQuery defined as below:
testQuery = """
CREATE TABLE #TEST (
[sample_id] varchar (256)
,[blah] varchar (256) )
INSERT INTO #TEST
SELECT DISTINCT
[sample_id]
,[blah]
FROM [myTableOI]
WHERE [Shipment Type] in ('test')
SELECT * FROM #TEST
"""
However, it works fine if testQuery is defined as below.
testQuery = """
SELECT DISTINCT
[sample_id]
,[blah]
FROM [myTableOI]
WHERE [Shipment Type] in ('test')
"""
I did a Google search as well as a search within Stack Overflow, and couldn't find any information regarding the particular issue. I also looked under the pymssql documentation and FAQ, found at http://code.google.com/p/pymssql/wiki/FAQ, and did not see anything mentioning that temporary tables are not allowed. So I thought I'd add this "question".
Update: July 2016
The previously-accepted answer is no longer valid. The second "will NOT work" example does indeed work with pymssql 2.1.1 under Python 2.7.11 (once conn.autocommit(1) is replaced with conn.autocommit(True) to avoid "TypeError: Cannot convert int to bool").
For those who run across this question and might have similar problems, I thought I'd pass on what I'd learned since the original post. It turns out that you CAN use temporary tables in pymssql, but you have to be very careful in how you handle commits.
I'll first explain by example. The following code WILL work:
testQuery = """
CREATE TABLE #TEST (
[name] varchar(256)
,[age] int )
INSERT INTO #TEST
values ('Mike', 12)
,('someone else', 904)
"""
conn = pymssql.connect(host=sqlServer, user=sqlID, password=sqlPwd, \
database=sqlDB) ## obviously setting up proper variables here...
conn.autocommit(1)
cur = conn.cursor()
cur.execute(testQuery)
cur.execute("SELECT * FROM #TEST")
tmp = cur.fetchone()
tmp
This will then return the first item (a subsequent fetch will return the other):
('Mike', 12)
But the following will NOT work
testQuery = """
CREATE TABLE #TEST (
[name] varchar(256)
,[age] int )
INSERT INTO #TEST
values ('Mike', 12)
,('someone else', 904)
SELECT * FROM #TEST
"""
conn = pymssql.connect(host=sqlServer, user=sqlID, password=sqlPwd, \
database=sqlDB) ## obviously setting up proper variables here...
conn.autocommit(1)
cur = conn.cursor()
cur.execute(testQuery)
tmp = cur.fetchone()
tmp
This will fail saying "pymssql.OperationalError: No data available." The reason, as best I can tell, is that whether you have autocommit on or not, and whether you specifically make a commit yourself or not, all tables must explicitly be created AND COMMITTED before trying to read from them.
In the first case, you'll notice that there are two "cur.execute(...)" calls. The first one creates the temporary table. Upon finishing the "cur.execute()", since autocommit is turned on, the SQL script is committed, the temporary table is made. Then another cur.execute() is called to read from that table. In the second case, I attempt to create & read from the table "simultaneously" (at least in the mind of pymssql... it works fine in MS SQL Server Management Studio). Since the table has not previously been made & committed, I cannot query into it.
Wow... that was a hassle to discover, and it will be a hassle to adjust my code (developed on MS SQL Server Management Studio at first) so that it will work within a script. Oh well...

Updating records in MongoDB through pymongo leads to deletion of most of them

Im working with a remote mongodb database in my python code.The code accessing the database and the database itself are on two different machines. The pymongo module version im using is 1.9+.
The script consists of the following code:
for s in coll.find({ "somefield.a_date" : { "$exists":False },
"somefield.b_date" : { "$exists":False }}):
original = s['details']['c_date']
utc = from_tz.localize(original).astimezone(pytz.utc)
s['details']['c_date'] = utc
if str(type(s['somefield'])) != "<type 'dict'>":
s['somefield'] = {}
s['somefield']['b_date'] = datetime.utcnow()
coll.update({ '_id' : s['_id'] }, s );
After running this code, a strange thing happened. There were millions of records in the collection initially and after running the script,just 29% of the total records remained, the rest were automatically deleted. Is there any known issue with PyMongo driver version 1.9+ ?
What could have been other reasons for this and any ways i can find out what exactly happened ?
What could have been other reasons for this and any ways i can find out what exactly happened ?
The first thing to check is "were there any exceptions"?
In coll.update(), you are not setting the safe variable. If there is an exception on the update, it will not be thrown.
In your code you not do not catch exceptions (which is suggested) and your update does not check for exceptions, so you have no way of knowing what's going on.
The second thing to check is "how are you counting"?
The update command can "blank out" data, but it cannot delete data (or change an _id).
Do you have a copy of the original data? Can you run your code on a small number of those 10 or 100 and see what's happening?
What you describe is not normal with any of the MongoDB drivers. We definitely need more data to resolve this issue.

Categories