В настоящее время я занимаюсь оценкой SQLAlchemy для нового проекта. При попытке отразить таблицы существующей базы данных (postgres, oracle) у меня возникают некоторые проблемы.
Для подключения к базе данных Oracle используется следующий код:
from sqlalchemy import create_engine, MetaData, Table, inspect, select
import pandas as pd
import keyring
dbtype = 'Oracle'
dbenv = 'LOCAL'
dbname = 'MYDB'
dbsys = '%s%s' % (dbtype, dbenv)
dbusr = 'myusr'
dbpwd = keyring.get_password(dbsys, dbusr)
dbhost = 'mydbhost'
dbport = 1521
dbconstr = 'oracle+cx_oracle://%s:%s@%s:%s/%s' % (dbusr, dbpwd, dbhost, dbport, dbname)
engine = create_engine(dbconstr)
После некоторых проб и ошибок относительно того, как собрать строку подключения (в моей системе нет tnsnames.ora
), мне удалось успешно установить sh соединение с удаленным Oracle database.
При проверке базы данных и попытке извлечь имена доступных таблиц я получил пустой список.
inspector = inspect(engine)
instbls = inspector.get_table_names()
print('instbls: %s' % instbls)
# instbls: []
На самом деле я ожидал, что системные таблицы или таблицы publi c были бы перечислены.
Далее я попытался отразить таблицы базы данных схемы SYS
.
md01 = MetaData(bind=engine, schema=['SYS', 'SYSTEM'], reflect=True)
# Warning: sqlalchemy_oracle.py:37: SADeprecationWarning: The MetaData.reflect flag is deprecated and will be removed in a future release. Please use the MetaData.reflect() method.
print('type(md01): %s' % type(md01))
# type(md01): <class 'sqlalchemy.sql.schema.MetaData'>
md01tbls = md01.tables
print('md01tbls: %s' % md01tbls)
# md01tbls: immutabledict({})
Поэтому я изменил код соответствующим образом, чтобы получить избавиться от предупреждения
md02 = MetaData(bind=engine, schema=['SYS', 'SYSTEM']).reflect()
print('type(md02): %s' % type(md02))
# type(md02): <class 'NoneType'>
md02tbls = md02.tables
# Exception: Traceback (most recent call last):
# ...
# File "sqlalchemy_oracle.py", line 44, in <module>
# AttributeError: 'NoneType' object has no attribute 'tables'
print('md02tbls: %s' % md02tbls)
Нет предупреждения, но есть исключение. : (
После некоторых исследований Net выглядело так, как будто требуется дальнейшая модификация кода.
md03 = MetaData().reflect(bind=engine, schema=['SYS', 'SYSTEM'])
# Exception: Traceback (most recent call last):
# ...
# File "/site-packages/sqlalchemy/engine/default.py", line 637, in denormalize_name
# name_lower = name.lower()
# AttributeError: 'list' object has no attribute 'lower'
print('type(md03): %s' % type(md03))
md03tbls = md03.tables
print('md03tbls: %s' % md03tbls)
Даже менее успешный, чем предыдущая попытка. Преобразование имен схем, перечисленных ниже буквы в регистре приводят к одинаковому результату.
Так что, может быть, reflect
может обрабатывать только strings
, а не lists
?
md05a = MetaData().reflect(bind=engine, schema='sys')
print('type(md05a): %s' % type(md05a))
# type(md05a): <class 'NoneType'>
md05b = MetaData().reflect(bind=engine, schema='system')
print('type(md05b): %s' % type(md05b))
# type(md05b): <class 'NoneType'>
md05atbls = md05a.tables
# Exception: Traceback (most recent call last):
# ...
# File "sqlalchemy_oracle.py", line 44, in <module>
# AttributeError: 'NoneType' object has no attribute 'tables'
print('md05atbls: %s' % md05atbls)
md05btbls = md05b.tables
print('md05btbls: %s' % md05btbls)
Там тоже не повезло!
Может быть, невозможно отобразить системные таблицы? Итак, давайте попробуем указать пользовательские c объекты.
md06 = MetaData().reflect(bind=engine, schema='MYUSR')
print('type(md06): %s' % type(md06))
# type(md06): <class 'NoneType'>
md06tbls = md06.tables
# Exception: Traceback (most recent call last):
# ...
# File "sqlalchemy_oracle.py", line 44, in <module>
# AttributeError: 'NoneType' object has no attribute 'tables'
print('md06tbls: %s' % md06tbls)
Снова уже известное исключение.
Возвращение к устаревшему вызову версия.
md07 = MetaData(bind=engine, schema=['MYUSR', 'MYUSR2'], reflect=True)
print('type(md07): %s' % type(md07))
# type(md07): <class 'sqlalchemy.sql.schema.MetaData'>
md07tbls = md07.tables
print('md07tbls: %s' % md07tbls)
# md07tbls: immutabledict({})
Не исключение, но и таблиц тоже нет, несмотря на то, что в схемах MYUSR
и MYUSR2
.
. *1046*
есть несколько разделений list
на strings
при указании схемы.
md08a = MetaData(bind=engine, schema='MYUSR', reflect=True)
# Warning: sqlalchemy_oracle.py:37: SADeprecationWarning: The MetaData.reflect flag is deprecated and will be removed in a future release. Please use the MetaData.reflect() method.
print('type(md08a): %s' % type(md08a))
# ype(md08a): <class 'sqlalchemy.sql.schema.MetaData'>
md08b = MetaData(bind=engine, schema='MYUSR2', reflect=True)
# Warning: sqlalchemy_oracle.py:40: SADeprecationWarning: The MetaData.reflect flag is deprecated and will be removed in a future release. Please use the MetaData.reflect() method.
print('type(md08b): %s' % type(md08b))
# type(md08b): <class 'sqlalchemy.sql.schema.MetaData'>
md08atbls = md08a.tables
print('md08atbls: %s' % md08atbls)
# md08atbls: immutabledict({'MYUSR.table01': Table(...)})
md08btbls = md08b.tables
print('md08btbls: %s' % md08btbls)
# md08btbls: immutabledict({'MYUSR2.table01': Table(...)})
Таким образом, эта попытка, наконец, дала ожидаемый результат.:)
Из всего этого все выглядит так, как будто
- системные / публичные c таблицы (объекты базы данных) не могут быть отражены;
- могут отображаться только пользовательские таблицы c;
- работает только устаревшая версия отражения;
- такое поведение можно наблюдать как с Oracle, так и с PostgreSQL баз данных.
Есть что-то, что я упустил? Что-то я неправильно понял? Сделано неправильно, в отличие от того, как оно на самом деле задумано?
Моя среда состоит из
- Ubuntu linux 16.04LTS;
- Python 3.8;
- SQLAlchemy 1.3.16;
- cx_ Oracle 7.3.0;
- psycopg2 2.8.5;
- локальный Oracle 18 c Мгновенный клиент ;
- удаленная Oracle 19 c база данных;
- локальная PostgreSQL 9,5 база данных.