From: Попов О.Б. <opopov at yandex.ru>
Date: Mon, 17 May 2004 18:21:07 +0000 (UTC)
Subject: Описание Python модуля cx_Oracle
Оригинал: http://www.sama.ru/~popovob/pydba.html
Python - глазами DBA
(Описание модуля cx_Oracle)
Попов О.Б.
opopov@yandex.ru
г.Самара,май-август 2003 года
Содержание
* Почему собственно python
* Часть I. Выполняем простой запрос
* Часть II.Запросы с параметрами
* Часть III.Анонимные блоки PL/SQL
* Ссылки
* Листинги
Почему собственно python.
-------------------------
Администраторы БД очень часто нуждаются в инструментах для
автоматизации разной рутинной работы, например: загрузка,
преобразования данных в разного рода форматы или сбор и анализ
различной статистики.Для меня в свое время такой палочкой-выручалочкой
стал perl.
Через некоторое время я набрел на python.Вот все что я могу сказать
после 1 года знакомства:
Это практичный и полезный инструмент.
Несколько слов об инструментах.Вариантов на самом деле немного:
1)гремучая смесь из разных утилит командной строки (bash+awk+tar+...),
2)языки сценариев(perl,tcl/tk... ), 3)классические языки
программирования(С,C++)
Первый вариант наиболее часто применяется администраторами.По крайней
мере мой собственный опыт говорит о том, что иногда удается получить
неплохое решение, если только задача не слишком сложна.
Третий вариант позволяет получить великолепное решение при
значительных затратах сил и времени на программирование,отладку и
тестирование кода.А если их нет?
Золотой серединой очевидно является вариант номер 2.
К языкам сценариев по функциональным возможностям приближается java,
поскольку фирма Oracle(к моему большому сожалению )взяла главный курс
на использование языка java везде,где это только возможно, теперь java
работает в контексте ядра Oracle и в принципе может быть использована
как инструмент администратора.
Проблемы я вижу следующие:
-слишком сложные API,плохая читабельность кода
-отсутствие встроенных современных типов данных,
- крайняя медлительность выполнения,
-необходимость обновлять программы с Oracle Metalink.
- Java API имеет плохую обратную совместимость с предыдущими версиями
-java -это не собственная разработка Oracle corp., а технология ,
лицензированная у Sun , которая кое-что лицензировала еще у нескольких
фирм.
Недостатки применения java с RDBMS Oracle проявились особенно
отчетливо в следущем эпизоде:на компьютерах с процессорами Pentium IV,
отказался запускатьcя Oracle Universal Installer(речь идет о Oracle
8.1.7), написанный на Java . Причина оказалась в JIT compiler, который
Sun лицензировал у Symantec. Понятно почему, фирма Oracle длительное
время не могла решить эту проблему. Пользователи Oracle решали ее
самостоятельно -путем удаления одной dll. Не предсталяю,что бы делал я
в этой (и многих других)ситуации без интернета и духа открытости
пользователей Oracle-администраторов, программистов...
С perl обычно все намного лучше.Дух открытости (движение Open Source и
лицензия GPL) позволил Perl прогрессировать с космической скоростью:
-огромное количество великолепных библиотек,
-встроенные структуры данных-списки,hash-таблицы
-существование многочисленных книг, списков рассылки и просто
энтузиастов значительно облегчают жизнь профессионалу.
Несмотря на массу положительных свойств, Perl имеет imho ряд
существенных недостатков:
-крайне запутанный и к тому же подверженный изменениям синтаксис.
-значительная интеграция с юниксообразными ОС.
-не очень удачные средства поддержки OOP
Первое затрудняет сопровождение (даже собственных) программ.А второе
ограничивает сферу применимости языка на других платформах.
И здесь важно отметить, что разработчики python в значительной степени
учли достоинства и недостатки различных языков программирования и в
результате появился язык программирования, который почти не содержит в
себе экзотических конструкций и позволяет использовать почти все
известные на сегодня методы создания программ.
Справедливости ради нужно заметить, что python отнюдь не является чем
то уникальным, мне известен еще один язык программирования с похожим
функционалом - ruby.Этот язык несколько моложе чем python и для
серьезного промышленного применения он еще, похоже не готов.Не все в
порядке с документацией и набор библиотек совсем невелик. Ruby
довольно популярен среди американских студентов, поэтому, возможно у
него впереди яркая судьба.
Достоинства языка python
------------------------
Python можно охарактеризовать в нескольких словах следующим образом:
многоплатформенный язык сценариев с динамической
типизацией,встроенными структурами данных и поддержкой трех парадигм
программирования-процедурной,объектной, функциональной.
* Многоплатформенность и масштабируемость-работает на большинстве
известных программно-аппаратных платформ от PDA до CRAY и IBM 390
* Гармоничная архитектура языка:
+ Простой и удобный для понимания логики програм синтаксис
+ Встроенные структуры данных-списки, кортежи,словари
+ Огромное количество библиотек:
-XML парсеры
-GUI(Win32,GTK/GNOME,KDE/QT,TK,FOX,WXWindows)
- Сетевые протоколы
- Разработка Web приложений
+ Переносимость кода между различными платформами
+ Мощные интерфейсы к конкретным ОС(особенно к Linux/Unix и
win32)
+ Поддержка разных стилей
программирования-процедурного,объектного,функционального
+ Встроенная поддержка Unicode и множества национальных
кодировок
(включая 5 русских)
+ Возможность писать самодокументированные программы и
автоматическая генерация документации на модули.
С чем реально сталкивается программист(а админитстратор является
таковым часто, но понемногу ) при использовании python.Это простой и
ясный синтаксис. Я ловлю себя часто на мысли,что просмотр чужого кода
зачастую не требует значительного напряжения.И здесь помогает то, что
отступы являются частью синтаксиса и определяют вложенность
операторов.
Библиотеки зачастую имеют очень лаконичную документацию, но этого
достаточно так как API очень выразительны и внятны. Отрадным явлением
в мире питона, является тот факт, что очень многие библиотеки были
разработаны после того, как были согласованы спецификации на
конкретные API.Одной из таких спецификаций является спецификация на
Python Database API 2.0. В соответствии с этой спецификацией
разрабатываются модули для доступа к конкретным СУБД.
К сожалению сам стандарт(как в свое время и cпецификация на ODBC) не
может учесть все специфические особенности конкретной СУБД, так как
фирмы-производители СУБД, используют свои фирменные расширения как
средство в конкуретной борьбе. Поэтому перенос кода с одной платформы
на другую все-таки очень трудно сделать безболезненным, если только
перед разработчиками не ставилась задача писать приложения,
предназначенное для работы с различными SQL серверами.
Сам процес кодирования и тестирования достаточно комфортен.Здесь
помогает качественная диагностика ошибок.Оттестированные модули очень
легко расширять и складывать из них приложения.
Для работы с RDBMS Oracle существует несколько различных модулей для
Python, использующих разные механизмы:
* ODBC(Win32,Linux)
* DCOM(Win32)
* PERL_DBI(Win32,Linux,Solaris,HP-UX,AIX)
* NATIVE API (Win32,Linux,Solaris,HP-UX,AIX)
Более подробную информацию Вы можете найти на http://www.python.org.
Часть I. Выполняем простой запрос.
----------------------------------
Я предпочитаю использовать модуль cx_Oracle, который был проверен мною
в ОС NT и Linux(автор-Anthony Tuininga утверждает что он работает в
Solaris и похоже, на всех юниксообразных ОС должен также работать ).В
основном этот модуль следует спецификации Python Database API 2.0 и
поддерживает работу с RDBMS Oracle версий 8.1-9.xx.
Модуль написан на языке С, что обеспечивает быструю обработку данных и
позволяет использовать все достоинства языка Python.
Для быстрого погружения в предмет я приведу фрагменты кода,
демонстрирующего основные приемы использования модуля.
try:
import cx_Oracle
except ImportError,info:
print "Import Error:",info
sys.exit()
if cx_Oracle.version<'3.0':
print "Very old version of cx_Oracle :",cx_Oracle.version
sys.exit()
В этом фрагменте демонстрируется безопасная загрузка модуля cx_Oracle
и проверка версии. Надеюсь, элегантность синтаксиса все-таки заметили
:-)
Далее создадим экземпляр класса connect, именно этот объект и
обеспечивает взаимодействие с сервером Oracle
my_cursor=my_connection.cursor()
try:
my_cursor.execute("""
SELECT OWNER,SEGMENT_TYPE,TABLESPACE_NAME,SUM(BLOCKS)SIZE_BLOCKS,
COUNT(*) SIZE_EXTENTS FROM DBA_EXTENTS
GROUP BY OWNER,SEGMENT_TYPE,TABLESPACE_NAME
""")
except cx_Oracle.DatabaseError,info:
print "SQL Error:",info
exit(0)
Динамическая типизация и поддержка сложных структур данных позволяет
легко обработать результаты SQL запроса. В результате вызова
my_cursor.fetchall() возращается список записей,каждая из которых
явлеяется кортежем(неизменяемым списком) полей разного типа. Если вы
знакомы с PL/SQL, то тогда цикл по списку записей будет очевидным:
for record in my_cursor.fetchall():
<обработка записи record,
record содержит кортеж из полей текущей записи>
Существует еще более любопытная возможность "разобрать" текущую запись
по полям:
for OWNER,SEGMENT_TYPE,TABLESPACE_NAME,SIZE_BLOCKS,SIZE_EXTENTS in my_cursor.fe
tchall():
<теперь можно обращаться к переменным, содержащим значения
одноименных полей курсора>
Печатаем результат на stdout. Замечу здесь, что my_cursor.description
возвращает описание столбцов запроса в виде списка кортежей. Для
каждого столбца возвращаются следующие данные:(name, type_code,
display_size, internal_size, precision, scale, null_ok). Далее следует
форматированный вывод на stdout( почти как printf в языке C).
print
print 'Database:',my_connection.tnsentry
print
print "Used space by owner, object type, tablespace "
print "----------------------------------------------------------------------------------"
title_mask=('%-16s','%-16s','%-16s','%-8s','%-8s')
i=0
for column_description in my_cursor.description:
print title_mask[i]%column_description[0],
i=1+i
print ''
print "----------------------------------------------------------------------------------"
row_mask='%-16s %-16s %-16s %8.0f %8.0f '
for record in my_cursor.fetchall():
print row_mask%record
В результате мы увидим что-то вроде:
Database: testdb
Used space by owner, object type, tablespace
--------------------------------------------------------------------------------
OWNER SEGMENT_TYPE TABLESPACE_NAME SIZE_BLOCKS SIZE_EXTENTS
--------------------------------------------------------------------------------
ADU2 INDEX USERS 784 25
ADU2 TABLE USERS 512 24
ADUGKS INDEX DEVELOP_DATA 984 123
ADUGKS TABLE DEVELOP_DATA 664 83
ADUGPA INDEX USERS 784 25
ADUGPA TABLE USERS 496 23
AGNKS_SG INDEX USERS 352 22
AGNKS_SG TABLE USERS 240 15
ATU INDEX USERS 3968 244
ATU TABLE DEVELOP_DATA 8 1
ATU TABLE USERS 2688 160
ATU1 INDEX DEVELOP_DATA 1600 200
ATU1 INDEX USERS 608 38
ATU1 TABLE DEVELOP_DATA 1032 129
ATU1 TABLE USERS 544 34
BUX INDEX DEVELOP_DATA 64 8
BUX TABLE DEVELOP_DATA 1736 217
DISP INDEX USERS 400 25
DISP TABLE USERS 528 33
EPE INDEX USERS 80 5
EPE TABLE USERS 48 3
EXZ INDEX USERS 1088 61
EXZ TABLE DEVELOP_DATA 8 1
EXZ TABLE USERS 832 41
Часть II. Запросы с параметрами.
--------------------------------
Согласно спецификации Python Database API 2.0, для выполнения запросов
с параметрами каждый модуль должен реализовывать переменную
paramstyle, которая определяет каким образом будут передаваться
параметры запросов. Текущая версия cx_Oracle(3.0) поддерживает режим
'named', то есть в модуле установлена переменная
cx_Oracle.paramstyle='named' и можно создавать конструкции в запросах
в виде:
select * from all_users where USERNAME LIKE :S
При этом связывание параметров запроса со значениями можно можно
выполнить двумя способами:
* Именованный параметeр метода execute:
cursor2.execute("select * from all_users where USERNAME LIKE :S ",S='S%')
* Словарь {':переменная':значение,...}
cursor2.execute("select * from all_users where USERNAME LIKE :S ",{':S':'S%'})
Часть III. Анонимные блоки PL/SQL
---------------------------------
Несмотря на существавания стандартов на язык SQL, реальные потребности
администратора часто требуют использования нестандартных средств
сервера(более того, написать приложение, работающее с сервером на
стандартном SQL возможно только для весьма тривиальных приложений ),
для Oracle таким нестандартным, но очень удобным механизмом является
возможность исполнения анонимных блоков PL/SQL. Модуль сx_Oracle
реализует этот механизм, который естественно не описан в спецификации
Python Database API 2.0 .
Чтобы связать переменные блока PL/SQL c переменными языка PYTHON, в
модуле сx_Oracle реализован класс var.Эекземпляр можно создать
следующим образом:
var=my_cursor.var(cx_Oracle.DATETIME)
Конструктор my_cursor.var(...) в качестве параметра требует указать
тип создаваемой переменной. Варианты:
* BINARY
* DATETIME
* FIXEDCHAR
* LONGBINARY
* LONGSTRING
* NUMBER
* ROWID
* STRING
var=my_cursor.var(cx_Oracle.DATETIME)
try:
my_cursor.execute("""begin
SELECT SYSDATE INTO :p_Value from dual;
end;""",p_Value = var)
Этот пример демонстрирует также и форматирование даты и времени для
экземпляра var. В результате напечатается нечто вроде:
Date: 12/05/2003
Time: 16:42:54
Всвязи с тем, что работа с типами времени и даты внутри сервера Oracle
реализованы особенным образом ( независимо от ОС), модуль сx_Oracle
реализует следующие функции для для преобразования значений дат и
времени :
* Date( year, month, day)
* DateFromTicks( ticks)
* Time( hour, minute, second)
* TimeFromTicks( ticks)
* Timestamp( year, month, day, hour, minute, second)
* TimestampFromTicks( ticks)
if cx_Oracle.version<'3.0':
print "Very old version of cx_Oracle :",cx_Oracle.version
sys.exit()
try:
my_connection=cx_Oracle.connect('system/gasdba@sqlmt')
except cx_Oracle.DatabaseError,info:
print "Logon Error:",info
exit(0)
my_cursor=my_connection.cursor()
try:
my_cursor.execute("""
SELECT OWNER,SEGMENT_TYPE,TABLESPACE_NAME,SUM(BLOCKS)SIZE_BLOCKS,
COUNT(*) SIZE_EXTENTS FROM DBA_EXTENTS
GROUP BY OWNER,SEGMENT_TYPE,TABLESPACE_NAME
""")
except cx_Oracle.DatabaseError,info:
print "SQL Error:",info
exit(0)
print
print 'Database:',my_connection.tnsentry
print
print "Used space by owner, object type, tablespace "
print "-----------------------------------------------------------"
title_mask=('%-16s','%-16s','%-16s','%-8s','%-8s')
i=0
for column_description in my_cursor.description:
print title_mask[i]%column_description[0],
i=1+i
print ''
print "------------------------------------------------------------"
row_mask='%-16s %-16s %-16s %8.0f %8.0f '
for record in my_cursor.fetchall():
print row_mask%record
for column_description in my_cursor.description:
print column_description
Листинг 2.
Запрос с параметрами
_________________________________________________________________
""
cx_Oracle demo
query with parameters
"""
__AUTHOR__='POPOV O.'
__COPYRIGHT__='POPOV O. 2002 Samara, Russia'
from sys import exit
try:
import cx_Oracle
except ImportError,info:
print "Import Error:",info
sys.exit()
if cx_Oracle.version<'3.0':
print "Very old version of cx_Oracle :",cx_Oracle.version
sys.exit()
try:
my_connection=cx_Oracle.connect('system/manager@test_db')
except cx_Oracle.DatabaseError,info:
print "Logon Error:",info
exit(0)
my_cursor=my_connection.cursor()
try:
my_cursor.execute("""
SELECT OWNER,SEGMENT_TYPE,TABLESPACE_NAME,SUM(BLOCKS)SIZE_BLOCKS,
COUNT(*) SIZE_EXTENTS FROM DBA_EXTENTS
WHERE OWNER LIKE :S
GROUP BY OWNER,SEGMENT_TYPE,TABLESPACE_NAME
""",S='SYS%')
except cx_Oracle.DatabaseError,info:
print "SQL Error:",info
exit(0)
print
print 'Database:',my_connection.tnsentry
print
print "Used space by owner, object type, tablespace "
print "-----------------------------------------------------------"
title_mask=('%-16s','%-16s','%-16s','%-8s','%-8s')
i=0
for column_description in my_cursor.description:
print title_mask[i]%column_description[0],
i=1+i
print ''
print "------------------------------------------------------------"
row_mask='%-16s %-16s %-16s %8.0f %8.0f '
for record in my_cursor.fetchall():
print row_mask%record