Oracle DB, Oracle APEX, Linux etc.

воскресенье, 22 февраля 2015 г.

Oracle APEX: разбивка на страницы интерактивных отчётов

Благодаря дискуссии на SQL.RU, решил проверить, как на самом деле происходит выдача страницы данных для интерактивного отчёта в Oracle APEX. Использовал версию 4.2.5, но не думаю, что что-то будет сильно отличаться вплоть до 4.1 включительно. Создал приложение с простым интерактивным отчётом, у которого в качестве источника данных был следующий запрос:

select * from emp

Изначально я ставил на то, что движок выполнения отчётов переписывает указанный разработчиком запрос и помимо прочей обёртки добавляет к нему один из известных приёмов по выдаче части отсортированных данных. Например:

select {select list}
  from ( select {select list}, row_number() over( order by {ordering} ) apx$rn 
           from ( {filtering, computing etc.} ) 
          order by {ordering} )
 where apx$rn between :page_min_row and :page_max_row

Результаты отладки меня слегка удивили:


Была слабая надежда, что это не окончательный запрос, и где-то там его ещё дополнительно обернут… Однако, после поиска в исходниках и вдумчивого чтения разврапленного wwv_flow_worksheet.plb я обнаружил, что всё намного печальней: в WWV_FLOW_WORKSHEET.get_report открывается курсор по тексту запроса, в котором нет никакого намёка на первую и последнюю строки страницы. Вместо этого курсор в цикле вхолостую считывается, пока не доходит до первой строки страницы. Затем начинается вывод строк, пока не дойдёт до последней.

Какие из полученных знаний можно сделать выводы:
  1. Каждая следующая страница интерактивного отчёта, к которой перейдёт пользователь, будет требовать чтения всё большего объёма данных. Пользователей нужно учить пользоваться всеми инструментами ИО, такими как фильтры, сортировки и т.п., а не рассматривать его как большую решётку Excel, в которой вместо Page Down можно жать на ссылку к следующей странице отчёта.
  2. Пользоваться настройкой максимального отображаемого количества строк в интерактивных отчётах всё-таки необходимо. Собственно, этому и учит нас документация APEX и другие материалы, как теперь видно — обоснованно. В отличие от границ страницы, ограничение числа строк в текст запроса добавляется и влияет на план выполнения.
  3. Оптимизатор может решить выполнять Ваш тяжёлый запрос для отчёта в режиме ALL_ROWS, несмотря на необходимость для вывода на страницу всего лишь в первых паре сотен строк. Уговорить его этого не делать Вы сможете с трудом, например созданием дополнительного процесса Before Header, в котором будет выставляться явно соответствующий режим:
    execute immediate 'alter session set optimizer_mode = all_rows';
    Кроме того, в WWV_FLOW_WORKSHEET.get_worksheet_report_query при оборачивании текста запроса используется поле WWV_FLOW_WORKSHEETS.sql_hint, название которого намекает на возможное светлое будущее, где разработчику APEX предоставят интерфейс для указания хинтов запросу отчёта.

Читать далее

воскресенье, 19 октября 2014 г.

JasperReports Integration + GlassFish

Делюсь инструкцией по установке сервлета, позволяющего использовать JasperReports из APEX. В качестве контейнера используется GlassFish. По ходу описания я расскажу, как обновить в сервлете JasperReports Library и настроить соединение с Oracle в GlassFish.

Потребуются:
  • установленный APEX (4.2.6);
  • установленный GlassFish (4.1), каталог установки в инструкции обозначен как GLASSFISH_ROOT;
  • JasperReports Library (5.6.0), далее JRL;
  • JasperReports Integration (2.1.0), далее JRI.

Установка и настройка:

  1. Распаковать JRI в какой-либо пустой каталог (далее $JRI_ROOT).
  2. Переписать файлы *.jar из папки $JRI_ROOT/lib в каталог $GLASSFISH_ROOT/glassfish/domains/domain1/lib:
    cp $JRI_ROOT/lib/*.jar $GLASSFISH_ROOT/glassfish/domains/domain1/lib
  3. Извлечь из веб-приложения JRI файл web.xml для указания каталога, в котором будут расположены каталог конфигурационных файлов, каталог отчётов, каталог файлов протокола работы приложения:
    cd $JRI_ROOT
    java -jar bin/truezip.jar cp webapp/JasperReportsIntegration.war/WEB-INF/web.xml web.xml
  4. Изменить в файле web.xml параметр oc.jasper.config.home (тег param-value):
    
      oc.jasper.config.home/path/to/jasperreports/config/dir
    

    Каталог в настройке (далее $JRI_CONF_HOME) должен давать права на чтение и запись пользователю ОС, под которым запускается GlassFish.
  5. Обновить файл web.xml в архиве веб-приложения:
    cd $JRI_ROOT
    java -jar bin/truezip.jar cp web.xml webapp/JasperReportsIntegration.war/WEB-INF/web.xml
  6. Создать каталог $JRI_CONF_HOME, если он ещё не создан, настроить права.
  7. Переписать в этот каталог $JRI_CONF_HOME папки $JRI_ROOT/{conf,logs,reports}.
  8. Зайти в веб-консоль GlassFish и установить приложение: Applications→Deploy. Выбрать архив с обновлённым web.xml.
    Context Root=/jri
  9. Создать JDBC Connection Pool с произвольным именем (далее $POOL_NAME). JDBC→JDBC Connection Pools→New:
    Resource Type: javax.sql.DataSource
    Datasource Classname: oracle.jdbc.pool.OracleDataSource
    Properties→User: DB_USER
    Properties→Password: db_password
    Properties→URL: jdbc:oracle:thin:@your.db.host:1521:oracle_sid
    
  10. Проверить пул, выбрав его и нажав Ping.
  11. Создать JDBC Resource, с которым будет работать приложение. JDBC→JDBC Resource→New:
    JNDI Name: Строка из букв и чисел с префиксом, отделённым косой чертой (например, jdbc/default, jdbc/mydbuser1)
    Pool Name: $POOL_NAME
  12. В файле $JRI_CONF_HOME/conf/application.properties настроить JNDI-префикс и подключения:
    ...
    jndiPrefix=jdbc/
    ...
    [datasource:default]
    type=jndi
    name=default
    # Для ресурса JNDI убирается префикс и указывается оставшаяся строка
    
  13. Распаковать JasperReports Library в какой-либо каталог (далее $JRL_ROOT);
  14. Остановить домен GlassFish в консоли:
    asadmin stop-domain domain1
  15. Обновить библиотеку JasperReports в составе JRI. Для этого перейти в $GLASSFISH_ROOT/glassfish/domains/domain1/applications/JasperReportsIntegration/WEB-INF/lib и выполнить для удаления старой библиотеки:
    chmod u+x _jasper-reports-delete-libs-5.1.0.sh
    ./_jasper-reports-delete-libs-5.1.0.sh
  16. Переписать в $GLASSFISH_ROOT/glassfish/domains/domain1/applications/JasperReportsIntegration/WEB-INF/lib все файлы из каталога $JRL_ROOT/lib и файл $JRL_ROOT/dist/jasperreports-5.6.0.jar.
    cp $JRL_ROOT/lib/* $JRL_ROOT/dist/jasperreports-5.6.0.jar $GLASSFISH_ROOT/glassfish/domains/domain1/applications/JasperReportsIntegration/WEB-INF/lib
  17. Запустить домен GlassFish в консоли:
    asadmin start-domain domain1
  18. Выполнить скрипт $JRI_ROOT/sql/sys_install.sql. Потребуется указать схему БД, в которой будет размещаться API для формирования отчётов из PL/SQL.
  19. Выполнить скрипт $JRI_ROOT/sql/sys_install_acl.sql. Потребуется указать схему БД, в которой будет размещаться API для формирования отчётов из PL/SQL.
  20. Выполнить скрипт $JRI_ROOT/sql/user_install.sql от имени пользователя, в схеме которого будет размещаться API для формирования отчётов из PL/SQL.
  21. Для проверки работоспособности веб-приложения открыть http://<сервер с GlassFish>:<настроенный для приложений порт>/
  22. Для проверки обращений и примера использования JRI из APEX установить приложение из $JRI_ROOT/apex.

Читать далее

понедельник, 19 августа 2013 г.

Плагин TabGrid

Небольшая вариация на тему грида Ext JS в APEX: http://apex.oracle.com/pls/otn/apex/f?p=40749:1
Логин/пароль: demo/demo

Направление развития: фильтры, работающие на сервере.

PS: Есть желание замахнуться на небольшой фреймворк в качестве альтернативы FOEX.
Читать далее

пятница, 31 мая 2013 г.

Oracle: запрос для отображения комментариев к таблице в виде DDL

Простой запрос для получения готовых DDL-операторов COMMENT с комментариями к таблице и её столбцам:

with par as (
  select nvl('&owner', user) owner -- Владелец схемы с таблицей
       , '&table_name' table_name  -- Таблица
    from dual
)
select ddl_stmt 
  from (select p.owner
             , p.table_name
             , '' column_name
             , comments
             , 'comment on table "'||p.owner||'"."'||p.table_name||'" is ''' || comments ||''';'  ddl_stmt
          from par p 
            left join 
               all_tab_comments tc on p.owner = tc.owner and p.table_name = tc.table_name 
         union all
        select c.owner
             , c.table_name
             , c.column_name
             , tc.comments
             , 'comment on column "'||c.owner||'"."'||c.table_name||'"."'||c.column_name||'" is ''' || comments ||''';'  ddl_stmt
          from (select owner, table_name, column_name 
                  from par p natural join all_tab_columns c) c
            left join 
                all_col_comments tc on c.owner = tc.owner and c.table_name = tc.table_name and c.column_name = tc.column_name)
 order by owner, table_name, column_name nulls first
/

Полезен как заготовка в ситуациях, когда нужно создать скрипт добавления или изменения комментариев к таблице.
Читать далее

четверг, 14 марта 2013 г.

Oracle APEX: Remote Debug

Случайно узнал, что можно включить удалённую отладку установкой в URL значения REMOTE для аргумента Debug. При этом подключение для удалённой отладки происходит в самом начале этапов Page Processing и Page Rendering и создаётся к удалённому хосту из переменной REMOTE_ADDR окружения CGI на порт 4000.

В документации об этом почему-то ни слова.

Пользуясь случаем, напоминаю, что уровнем отладки (и количеством выдаваемой информации) можно управлять, используя значения LEVELn для аргумента Debug, где n -- уровень отладки от 1 до 9. По умолчанию, для значения YES используется уровень 4.

Читать далее

четверг, 14 февраля 2013 г.

Oracle APEX: Report Template Preview

Да, я знаю, что обещал заметку о том, как создавать плагин-регион. Но пока я её готовлю, могу предложить описание поиска ошибки в APEX Application Builder и её исправления.

Ошибка заключается в том, что в APEX уже довольно давно поломался предпросмотр шаблонов отчётов.


Для шаблонов страниц работает, для регионов тоже, а отчёты показывают крайне информативное сообщение:
report error: 
ORA-01002: fetch out of sequence

Как всё поправить? Для начала, нужно разобраться, что выполняется на странице. Смотрим в адресную строку:
/apex/f?p=4000:245:7846987198961::::F4000_P245_ID:2451412072364242

Заглядываем в словарь APEX, что там за регионы на странице 245 приложения 4000:
column region_name format a20
column region_id format 999999999999999999999999
column source_type format a15
column region_source format a80
set long 700
select region_name, region_id, source_type, region_source 
  from apex_040200.apex_application_page_regions 
 where application_id = 4000 and page_id = 245
/
REGION_NAME                          REGION_ID SOURCE_TYPE     REGION_SOURCE                                                                  
-------------------- ------------------------- --------------- --------------------------------------------------------------------------------
Breadcrumb                    6539000463209372 Breadcrumb                                                                                       
Template Preview             14562627207747006 PL/SQL          wwv_render_report3.show(                                                         
                                                                   p_query => '                                                                 
                                                               select 1 COL1, ''[...]'' COL2, ''[...]                                           

Template Preview             14573702151806062 HTML/Text       

This page provides a preview of the identified report template.

Template Details 82014114667920662 Report select ROW_TEMPLATE_NAME N, ROW_TEMPLATE_TYPE T from WWV_FLOW_ROW_TEMPLATES preview-report 88449328191587806 Report select 1 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual union select

Судя по названиям и содержимому, нас интересуют регионы с REGION_ID 14562627207747006 и 88449328191587806. Первый выполняет обращение к wwv_render_report3.show, второй, как мы сейчас увидим, служит для хранения настроек, которые используются в этом вызове:
column region_source format a700
set long 700
select region_id, region_source 
  from apex_040200.apex_application_page_regions 
 where region_id in (14562627207747006, 88449328191587806)   
/
REGION_ID REGION_SOURCE
------------------------- --------------------------------------------------------------------------------
        14562627207747006 wwv_render_report3.show(
                              p_query => '
                          select 1 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union
                          select 2 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union
                          select 3 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union
                          select 4 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union
                          select 5 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union
                          select 6 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual
                          order by 1',
                              p_row_template_id => :f4000_p245_id,
                              p_region_id => 88449328191587806
                            );

        88449328191587806 select 1 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual union
                          select 2 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual union
                          select 3 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual union
                          select 4 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual union
                          select 5 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual union
                          select 6 COL1, '[...]' COL2, '[...]' COL3, sysdate COL4 from dual
                          order by 1
Значит, путь наш лежит в wwv_render_report3.show. Чтобы посмотреть, что именно там происходит, потребуется разврапировать тело пакета и установить его в схему APEX. Тело находится в файле reports3.plb.

Затем включить удалённую отладку, для чего потребуется скомпилировать этот пакет с поддержкой отладки. Ещё в рамках подготовки к удалённой отладке потребуется временно изменить содержимое региона 14562627207747006, окружив вызов обращениями к dbms_debug_jdwp:
update wwv_flow_page_plugs 
   set plug_source = q'{dbms_debug_jdwp.connect_tcp('localhost',50001);  
wwv_render_report3.show(
    p_query => '
select 1 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 2 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 3 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 4 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 5 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 6 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual 
order by 1',
    p_row_template_id => :f4000_p245_id,
    p_region_id => 88449328191587806
  );
dbms_debug_jdwp.disconnect;}'
 where id = 14562627207747006
/
Я использую localhost как имя удалённого хоста, потому что пробросил через SSH свой порт 4000, на котором работает Debug Listener, на порт 50001 сервера с Oracle. Если файерволы Вам позволяют, можете вместо localhost указывать IP машины, на которой работает OSD с удалённой отладкой, тогда порт необходимо указать тот, который выбрали при запуске Remote Debug.


Итак, подготовились к отладке, запустили Remote Debug, зашли на страницу с предпросмотром шаблона отчёта. Началась отладка. За несколько итераций становится ясно, что ошибка возникает при попытке фетчить открытый курсор с запросом отчёта, который ещё не был выполнен. А происходит это, потому что в вызове wwv_render_report3.show не указано значение параметра p_plug_source_type. Исправляем:
update wwv_flow_page_plugs 
   set plug_source = q'{wwv_render_report3.show(
    p_query => '
select 1 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 2 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 3 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 4 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 5 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual union 
select 6 COL1, ''[...]'' COL2, ''[...]'' COL3, sysdate COL4 from dual 
order by 1',
    p_row_template_id => :f4000_p245_id,
    p_region_id => 88449328191587806,
    p_plug_source_type => 'SQL_QUERY'
  );}'
 where id = 14562627207747006
/

Проверяем:


Вот так ищутся и правятся небольшие баги в APEX. Не забывайте делиться своими решениями.

Читать далее

понедельник, 15 октября 2012 г.

APEX 4.2 ушёл в печать!

http://www.oracle.com/technetwork/developer-tools/apex/downloads/index.html

PS: Что-то я редко стал сюда что-нибудь добавлять... Потому анонс: в следующих сообщениях расскажу о создании плагинов. Начнём с плагина-региона.
Читать далее