У меня есть два несвязанных экземпляра базы данных oracle 11g. Мне нужно выбрать значение типа CLOB из одной базы данных и вставить его в другую с помощью python, используя cx_Oracle.
Когда я пытаюсь это сделать в одном направлении, это заканчивается:
Traceback (most recent call last):
File "<input>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
Как ни странно, при попытке в другом направлении это заканчивается:
Traceback (most recent call last):
File "<input>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-00600: internal error code, arguments: [kglslod:subheap], [8], [0x14D9EA270], [], [], [], [], [], [], [], [], []
Выбор и вставка значения CLOB в одной и той же базе данных не проблема.
Я обнаружил, что могу исправить это с помощью специального обработчика вывода:
def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
if defaultType == cx_Oracle.CLOB:
return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)
и настройка
db_connection.outputtypehandler = OutputTypeHandler
Поэтому мне интересно, такое поведение по дизайну или что еще приводит к этому?
Вот некоторые данные для тестирования:
create table test_table_1
(
column_1 clob
);
insert into test_table_1 (column_1) values ('test_data_1');
create table test_table_2
(
column_2 clob
);
insert into test_table_2 (column_2) values ('test_data_2');
import cx_Oracle
db_1 = cx_Oracle.connect(<user>, <passwd>, <host_db1>)
db_2 = cx_Oracle.connect(<user>, <passwd>, <host_db2>)
cursor_1 = db_1.cursor()
cursor_2 = db_2.cursor()
result = cursor_1.execute("select column_1 from test_table_1")
result1 = result.fetchone()
cursor_2.execute("insert into test_table_2(column_2) values (:val)", val=result1[0])
Traceback (most recent call last):
File "<input>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-00600: Interner Fehlercode, Argumente: [kglslod:subheap], [8], [0x14DF5A0B0], [], [], [], [], [], [], [], [], []
Traceback (most recent call last):
File "<input>", line 8, in <module>
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
import cx_Oracle
def OutputTypeHandler(cursor: cx_Oracle.Cursor, name, defaultType, size, precision, scale):
"""
Convertes CLOB type to string on reading.
"""
if defaultType == cx_Oracle.CLOB:
return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)
db_1 = cx_Oracle.connect(<user>, <passwd>, <host_db1>)
db_1.outputtypehandler = OutputTypeHandler
db_2 = cx_Oracle.connect(<user>, <passwd>, <host_db2>)
cursor_1 = db_1.cursor()
cursor_2 = db_2.cursor()
result = cursor_1.execute("select column_1 from test_table_1")
result1 = result.fetchone()
cursor_2.execute("insert into test_table_2(column_2) values (:val)", val=result1[0])
db_2.commit()
Я добавил тестовый пример.
Вы не можете просто передать значение LOB из одного соединения в другое. Они специфичны для конкретного соединения. Я постараюсь предоставить лучшее сообщение об ошибке для этой ситуации.
Как вы отметили в своем собственном вопросе, первое решение - использовать STRING вместо CLOB с помощью обработчика типа вывода. Этот метод работает со значениями CLOB размером до 1 ГБ, но недостатком является то, что все значение CLOB должно находиться в непрерывной памяти.
Если вам нужно передать значения из-за очень большой длины, вам нужно будет использовать что-то вроде этого:
cursor_1 = db_1.cursor()
cursor_2 = db_2.cursor()
cursor_1.execute("select column_1 from test_table_1")
sourceLob, = cursor_1.fetchone()
lobVar = cursor_2.var(cx_Oracle.CLOB)
cursor_2.execute("insert into test_table_2 (column_2) values (empty_clob()) returning column_2 into :1", [lobVar])
destLob = lobVar.getvalue()
offset = 1
numBytesInChunk = 65536
while True:
data = sourceLob.read(offset, numBytesInChunk)
if data:
destLob.write(data, offset)
if len(data) < numBytesInChunk:
break
offset += len(data)
db_2.commit()
Это то, что я имел в виду под «это по замыслу?». Это потому, что оракул просто хранит ссылку на хранилище clob в таблице? Лучшее сообщение об ошибке было бы действительно хорошо. :) Мне потребовались часы, чтобы найти проблему.
Библиотеки Oracle Client не поддерживают эту концепцию, поэтому я думаю, вы можете подумать, что это «по задумке». :-)
Можете ли вы поделиться полностью работоспособным тестовым примером, демонстрирующим проблему, чтобы нам не приходилось строить догадки? Убедитесь, что вы используете последнюю версию cx_Oracle. Последняя версия 7.2.