,

Зависший job (hanging job)

вторник, 15 декабря 2009 г. 0 коммент.

В одной из баз попросили разобраться почему не работает job. По всем признакам он был валидным: флажок broken имел значение “N”, количество неудачных попыток (failures) было равно 0. Смущало только одно значение, следующее дата и время выполнения (next_date, next_time), оно было меньше текущей даты на 10 дней, хотя job должен был выполняться через каждые 5 минут. Вывод был один, джоб не завершил предыдущее задание, и поэтому не может дальше продолжить свою работу.

Чтобы найти работающие джобы нужно обратиться к вьюшке dba_jobs_running или v$lock.

SQL>
SQL> select v$lock.sid as sid,
2 v$lock.type as lock_type,
3 v$lock.id1,
4 v$lock.id2 as job
5 from v$lock
6 where v$lock.type = 'JQ';

SID LOCK_TYPE ID1 JOB
---------- --------- ---------- ----------
171 JQ 0 405

SQL>
SQL> select dba_jobs_running.sid,
2 dba_jobs_running.job,
3 dba_jobs_running.failures,
4 dba_jobs_running.last_date,
5 dba_jobs_running.last_sec
6 from dba_jobs_running;

SID JOB FAILURES LAST_DATE LAST_SEC
---------- ---------- ---------- ----------- --------------------------------
171 405 0 01.12.2009 17:11:53

SQL>
Да это мой зависший job c номером 405. Посмотрим, что делает сессия с номером 171.
SQL>
SQL>
SQL> select v$session_wait.event
from v$session_wait
where v$session_wait.sid = 171;

EVENT
---------------------------
SQL*Net message from dblink

SQL>
Сессия ожидает ответа по DBLink. Сессия “не убиваемая”, пришлось убивать процесс с помощью orakill. Читать полностью

, ,

Зависающая активити Wait (hanging activity wait)

среда, 25 ноября 2009 г. 0 коммент.

Во время возросшей нагрузки, подвисла активити Wait. Поиск по интернету и чтение документации помогли найти решение.

Необходимо изменить значение параметра com.oracle.bpel.expirationAgent.threadCount с 10 до 150.

Параметр находится в файле resources-quartz.properties по следующему пути ${BPEL_HOME}/bpel/domains/{name of domain}/config.

Подробнее, какие еще параметры можно настроить можно почитать в BPEL Performance Tuning: Quartz Scheduling. Как работает активити Wait можно узнать в BPEL Performance Tuning: Quartz Scheduling.

Читать полностью

, , ,

Как получить доступ к Oracle SOA Suite Worklist’у с помощью удаленного Java-клиента (Часть 2)

пятница, 20 ноября 2009 г. 1 коммент.

В первой части я рассмотрел способ соединения SOAP_CLIENT, теперь рассмотрим способ соединения REMOTE_CLIENT. Собственно все тоже, за исключением некоторых особенностей. В файле wf_client_config.xml необходимо, чтобы содержимое секции ejb было корректным. Почему нужно проверить секцию ejb можно прочитать в Oracle® BPEL Process Manager Developer's Guide, в главе 16.6 Building Clients for Workflow Services.

Следует убедиться, что содержимое элемента serverurl корректное. Правильное значение можно найти в элементе jndi.url в файле:

SOA_Oracle_Home\bpel\utilities\ant-orabpel.properties

Подробнее об этом можно почитать здесь. Секция ejb в файле должна иметь такой вид:

  opmn:ormi://soahost:6006:oc4j_soa/hw_services
oc4jadmin
welcome1
oracle.j2ee.rmi.RMIInitialContextFactory

Также следует изменить способ подключения.
// ...
IWorkflowServiceClient wfSvcClient = WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT);
// ...

На этом все изменения закончены, можно пользоватся новым подключением.
Читать полностью

, , ,

Как получить доступ к Oracle SOA Suite Worklist’у с помощью удаленного Java-клиента (Часть 1)

пятница, 9 октября 2009 г. 0 коммент.

Сначала показалось, что это простая задача: есть примеры в интернете, в руководстве Oracle, на блогах технических специалистов. Но, оказалось, что количество препонов настолько велико, что наскока решить эту задачу не удалось.

Итак существует несколько способов соединения:

  • JAVA_CLIENT – доступ к Workflow сервису напрямую с помощью Java (устаревший способ для версии 11.1.1);
  • LOCAL_CLEINT – доступ к Workflow сервису напрямую с помощью EJB;
  • REMOTE_CLIENT – доступ к Workflow сервису удаленно с помощью EJB;
  • SOAP_CLIENT – доступ к Workflow сервису удаленно с помощью SOAP;
  • WSIF_CLIENT – доступ к Workflow сервису удаленно с помощью WSIF (устаревший способ для версии 11.1.1);

Рассмотрим подробно способ соединения SOAP_CLIENT.

Для проекта мне понадобились библиотеки (вроде бы порядок важен):

  • bpm-infra.jar
  • orabpel-common.jar
  • orabpel-thirdparty.jar
  • orabpel.jar
  • oc4jclient.jar
  • jazncore.jar
  • xml.jar
  • xmlparserv2.jar
  • orasaaj.jar
  • soap.jar
  • orabpel-boot.jar
  • bpm-services.jar
  • wsclient_extended.jar (это файл надо взять с OTN)

Кроме библиотек понадобиться файл wf_client_config.xml. Данный файл необходимо включить в class-path вашего приложения. Файл можно скопировать с сервера, где установлен BPEL по следующему пути:

SOA_Oracle_Home\bpel\system\services\config\wf_client_config.xml

Следует убедиться, что содержимое элемента taskService корректное, указанный url доступен, и открывается форма для вызова веб-сервиса:

SOA_Oracle_Home\bpel\utilities\ant-orabpel.properties

Подробнее об этом можно почитать здесь. Секция taskService в файле должна иметь такой вид:



http://soahost:7777/integration/services/TaskService/TaskServicePort

Одного файла wf_client_config.xml достаточно для запуска приложения, но при этом будут выдаваться предупреждения:

<::> ORABPEL-30028
<::>
<::> Invalid configuration file wf_config.xml
<::> The configuration file wf_config.xml not be read.
<::> Make sure that the configuration file wf_config.xml is available and is a valid XML document. Contact oracle support if error is not fixable.

Чтобы избежать не нужных предупреждений необходимо в приложение добавить следующие файлы, и включить их в class-path:

  • SOA_Oracle_Home\bpel\system\services\config\wf_config.xml
  • SOA_Oracle_Home\bpel\system\services\schema\wf_config.xsd

При соединение к сервису workflow необходимо правильно указать параметры аутентификации. В моем случае это:

  • Логин: bpeladmin
  • Пароль: welcome1
  • Рилм(Realm): myrealm

Правильно значение realm можно подсмотреть в атрибуте realmName элемента configuration секции ISConfigaration\configurations файле:

SOA_Oracle_Home\bpel\system\services\config\is_config.xml

При запуске программы можно получить ошибку:

java.lang.SecurityException: class "com.collaxa.cube.LoggerException"'s signer information does not match signer information of other classes in the same package

или

java.lang.SecurityException: class "com.collaxa.cube.ExceptionIndex"'s signer information does not match signer information of other classes in the same package

или похожую.

Все дело в версии Java. Ошибка возникает только в версии 1.5 и связано с багом, возникающим, когда классы в одном пакете подписаны по разному или не подписаны вообще. Для запуска необходимо использовать версию 1.4 или 1.6.

Ну вот наконец-то добрались до самого кода:

package wl_test;

import java.util.ArrayList;
import java.util.List;

import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.model.Task;

import java.util.Iterator;

import oracle.bpel.services.workflow.repos.Ordering;
import oracle.bpel.services.workflow.repos.Column;
import oracle.bpel.services.workflow.repos.Predicate;
import oracle.bpel.services.workflow.repos.TableConstants;
import oracle.bpel.services.workflow.repos.table.WFTaskConstants;
import oracle.bpel.services.workflow.task.ITaskService;

import oracle.bpel.services.workflow.task.model.IdentityType;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

public class app {

private static final String WF_MANAGER = "bpeladmin";
private static final String WF_PASSWORD = "welcome1";
private static final String WF_REALM = "myrealm";

public static void main(String[] args) {

try {
System.out.println("Пытаемся создать WorkflowServiceClient");
IWorkflowServiceClient wfSvcClient = WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.SOAP_CLIENT);
System.out.println("Создали WorkflowServiceClient");

System.out.println("Получаем TaskQueryService");
ITaskQueryService querySvc = wfSvcClient.getTaskQueryService();
System.out.println("Получили TaskQueryService");

System.out.println("Пытаеся пройти аутенсификацию");
IWorkflowContext ctx = querySvc.authenticate
(WF_MANAGER, // администратор
WF_PASSWORD,// пароль
WF_REALM, // realm, ldap каталог
null); // Работать из под другого пользователя

//Задаем колонки для выборки
List displayColumnsList = new ArrayList();
displayColumnsList.add("TASKNUMBER");
displayColumnsList.add("TITLE");
displayColumnsList.add("PRIORITY");
displayColumnsList.add("STATE");
displayColumnsList.add("OUTCOME");
displayColumnsList.add("EXPIRATIONDATE");
displayColumnsList.add("UPDATEDDATE");
displayColumnsList.add("UPDATEDBY");
displayColumnsList.add("CREATEDDATE");
displayColumnsList.add("CREATOR");
displayColumnsList.add("ASSIGNEEUSERS");
displayColumnsList.add("ASSIGNEEGROUPS");
displayColumnsList.add("ACQUIREDBY");
displayColumnsList.add("IDENTIFICATIONKEY");
displayColumnsList.add("PROCESSNAME");


//Сериализуем предикаты
Predicate.enableXMLSerialization(true);
// Добавляем условия
// 1. Ищем по названию процесса
Predicate filterPredicate = new Predicate(
TableConstants.WFTASK_PROCESSNAME_COLUMN,
Predicate.OP_EQ,
"test_wf");
// 2. Ищем по номеру задачи
filterPredicate.addClause(
Predicate.AND,
TableConstants.WFTASK_TASKNUMBER_COLUMN,
Predicate.OP_EQ,
"10431");

// Добавляем условия для сортировки
Column sortFieldColumn = Column.getColumn(WFTaskConstants.TEXTATTRIBUTE2_COLUMN);
boolean isAscending = false;
// Nulls в конец...
boolean isNullFirst = false;
Ordering taskOrdering = new Ordering(sortFieldColumn,isAscending,isNullFirst);

// Запрашиваем задачи
List tasks = querySvc.queryTasks
(ctx, // workflow context
displayColumnsList, // колонки для выборки
null, // Не запрашивать доп. информацию
ITaskQueryService.ASSIGNMENT_FILTER_ADMIN,
null, // Нет ключевого слова
filterPredicate, // Условия для выборки
taskOrdering, // Условие сортировки
0, // Не задаем сколько записей хотим получить
0 // Не задаем сколько записей хотим получить
);

// Получить доступ к сервису задач
ITaskService taskSvc = wfSvcClient.getTaskService();

// Посмотрим сколько у нас записей
System.out.println("tasks.size()=" + tasks.size() + "=");
for (int i=0;i<tasks.size();i++)
{
Task thisTask = (Task) tasks.get(i);
List assigneeUsers = thisTask.getSystemAttributes().getAssigneeUsers();
Iterator valueUsers = (assigneeUsers).iterator();

// Перезапросим задачу, чтобы получить полную информацию о задаче
thisTask = querySvc.getTaskDetailsById(ctx, thisTask.getSystemAttributes().getTaskId());

// Посмотрим результат
System.out.println("getSystemAttributes().getOutcome()="+
thisTask.getSystemAttributes().getOutcome() + "=" );
// Посмотрим состояние
System.out.println("getSystemAttributes().getState()="+
thisTask.getSystemAttributes().getState() + "=" );
// Посмотрим ID задачи
System.out.println("getSystemAttributes().getTaskId()="+
thisTask.getSystemAttributes().getTaskId() + "=" );
// Посмотрим номер задачи
System.out.println("getSystemAttributes().getTaskNumber()="+
thisTask.getSystemAttributes().getTaskNumber() + "=" );
// Посмотри название процесса
System.out.println("getProcessName()="+
thisTask.getProcessInfo().getProcessName()+ "=" );

// Посмотрим пользователей, которые участвуют в этой задаче
while (valueUsers.hasNext())
{
String listItem = ((IdentityType) valueUsers.next()).getId();
System.out.println("User=" + listItem);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

За дополнительной информацией можно обратиться сюда:


Читать полностью

, , ,

Удаление BPEL Instance’ов и Human Task’ов.

четверг, 8 октября 2009 г. 0 коммент.

Иногда надо почистить BPEL инстансы и очень хочется, чтобы это происходило автоматически. Для ручного удаления можно воспользоваться BPEL консолью, но там есть большой недостаток – не возможно удалить все дерево связанных процессов. Чтобы это можно было сделать я написал небольшой пакетик. Одна процедура удаляет Human Task’и, другая связанные BPEL инстансы и Human Task’и.


create or replace package bpel_enhancement is

procedure delete_ci( p_cikey in cube_instance.cikey%type
,p_ignore_root in boolean default false
);

procedure delete_wt(p_taskid in wftask.taskid%type
);

end bpel_enhancement;
/
create or replace package body bpel_enhancement is

procedure delete_ci( p_cikey in cube_instance.cikey%type
,p_ignore_root in boolean default false
)
is
vi pls_integer;
begin

-- Проверим сначала, что инстанс не имеет родителей
select count(1)
into vi
from cube_instance
start with cube_instance.cikey = p_cikey
connect by to_char(cube_instance.cikey) = prior to_char(cube_instance.parent_id);

if (vi = 0) then
raise_application_error(-20000, 'Не найден инстанс "' || p_cikey || '"');
elsif (vi > 1) then
if not p_ignore_root then
raise_application_error(-20000, 'Найдены родительские процессы для инстанса "' || p_cikey || '"');
end if;
end if;

-- начинаем удалять иерархически в обратном порядке
for vCur in (
select cube_instance.*
from cube_instance
start with cube_instance.cikey = p_cikey
connect by prior to_char(cube_instance.cikey) = to_char(cube_instance.parent_id)
order by level desc
) loop
-- Удаляем инстанс
dbms_output.put_line('Удаление истанса: "' || vCur.Cikey || '" -- "' || vCur.Title || '"');
collaxa.delete_ci(p_cikey => vCur.cikey);

-- Ищем связанные задачи
for vCurTask in (
select *
from wftask
where wftask.instanceid = vCur.cikey
) loop

dbms_output.put_line('Удаление задачи: "' || vCurTask.Taskid || '" -- "' || vCurTask.Tasknumber || '"');
delete_wt(vCurTask.Taskid);
end loop;
end loop;

end delete_ci;

procedure delete_wt(p_taskid in wftask.taskid%type
)
is
begin
delete from wftaskhistory where taskid = p_taskid;
delete from wfassignee where taskid = p_taskid;
delete from wfattachment where taskid = p_taskid;
delete from wfcomments where taskid = p_taskid;
delete from wfmessageattribute where taskid = p_taskid;
delete from wfnotification where taskid = p_taskid;
delete from wfnotificationmessages where taskid = p_taskid;
delete from wfroutingslip where taskid = p_taskid;
delete from wftasktimer where taskid = p_taskid;

delete from wftask where taskid = p_taskid;
end delete_wt;

end bpel_enhancement;
/

Читать полностью

,

Результат Select’а в виде одной строки и одна строка в виде набора строк

четверг, 1 октября 2009 г. 0 коммент.

Часто бывает необходимо получить результат выборки в виде одной строки, разделенных каким-нибудь разделителем. Обычно всегда пишем для этого свои агрегирующие функции. Но бывает, что так, что запрещено создавать свои типы и пакеты, и результат необходимо получить чистым select’ом с использованием стандартных функций.

Вот пару способов это сделать. Примеры выполнены в схеме SCOTT.

SQL> select ltrim(max(sys_connect_by_path(ename, ',')), ',')
2 from (
3 select emp.ename,
4 row_number() over (order by emp.ename) as rn
5 from emp
6 where emp.job = 'SALESMAN'
7 )
8 start with rn = 1
9 connect by prior rn = rn-1;

LTRIM(MAX(SYS_CONNECT_BY_PATH(
--------------------------------------------------------------------------------
ALLEN,MARTIN,TURNER,WARD

SQL>

Еще один способ.

SQL> select rtrim(extract(xmlagg(xmlelement(x, ename||',') order by emp.ename),'//X/text()').getstringval(), ',')
2 from emp
3 where emp.job = 'SALESMAN';

RTRIM(EXTRACT(XMLAGG(XMLELEMEN
--------------------------------------------------------------------------------
ALLEN,MARTIN,TURNER,WARD

SQL>

Еще пару раз приходилось делать наоборот, есть строка, со значениями, и их надо превратить в набор строк со значениями. Ниже несколько способов, как это сделать.


SQL> select emp.empno, emp.ename
2 from emp
3 where emp.ename in
4 (
5 with t as (
6 select ltrim('ALLEN,MARTIN,TURNER,WARD'/*:p_list*/, ',') || ',' as s,
7 rownum as rn
8 from emp /* или all_objects, здесь можно emp*/
9 )
10 select substr(t.s,
11 lag(instr(t.s, ',', 1, rn), 1, 0) over (order by instr(t.s, ',', 1, rn)) + 1,
12 instr(t.s, ',', 1, rn) - lag(instr(t.s, ',', 1, rn), 1, 0) over (order by instr(t.s, ',', 1, rn)) - 1
13 ) as str
14 from t
15 where instr(t.s, ',', 1, rn) != 0
16 );

EMPNO ENAME
----- ----------
7499 ALLEN
7521 WARD
7654 MARTIN
7844 TURNER

SQL>

Еще один способ.


SQL> select emp.empno, emp.ename
2 from emp
3 where emp.ename in
4 (
5 with t as (
6 select '' || replace('ALLEN,MARTIN,TURNER,WARD', ',', '') || '' s
7 from dual
8 )
9 select extractvalue(value(row_data), '/X')
10 from t,
11 table(xmlsequence(extract(xmltype(t.s), '/Y/X'))) row_data
12 );

EMPNO ENAME
----- ----------
7499 ALLEN
7654 MARTIN
7844 TURNER
7521 WARD

SQL>

Читать полностью

, ,

Заполнение коллекции

четверг, 24 сентября 2009 г. 0 коммент.

Очередной раз при работе с коллекциями наткнулся на несколько возможностей для их заполнения и решил их рассмотреть.

Сначала создадим коллекции, основанные на на простых типах varchar2, integer и на основе записи (record). Тестирование выполняем в схеме SCOTT.

SQL> create or replace type array_varchar as table of varchar2(4000);
2 /

Type created

SQL> create or replace type array_integer as table of integer;
2 /

Type created

SQL>
SQL> create or replace type param as object
2 (
3 param_varchar varchar2(4000),
4 param_integer integer
5 );
6 /

Type created

SQL> create or replace type array_param as table of param;
2 /

Type created

SQL>

Рассмотрим сначала самый простой, но некрасивый и медленный способ.

SQL> set serveroutput ON
SQL>
SQL> declare
2 -- декларируем переменные для коллекции
3 -- и сразу же их инициалзируем
4 v_array_varchar array_varchar := array_varchar();
5 v_array_integer array_integer := array_integer();
6 v_array_param array_param := array_param();
7 begin
8 for vCur in (
9 select emp.empno, emp.ename
10 from emp
11 ) loop
12 -- заполняем коллекцию строк
13 v_array_varchar.extend();
14 v_array_varchar(v_array_varchar.count) := vCur.ename;
15 -- заполняем коллекцию чисел
16 v_array_integer.extend();
17 v_array_integer(v_array_integer.count) := vCur.empno;
18 -- заполняем коллекцию записей
19 v_array_param.extend();
20 v_array_param(v_array_param.count) := param(vCur.ename, vCur.empno);
21 end loop;
22 -- посмотрим количесто записей
23 dbms_output.put_line('v_array_varchar.count=' || v_array_varchar.count);
24 dbms_output.put_line('v_array_integer.count=' || v_array_integer.count);
25 dbms_output.put_line('v_array_param.count=' || v_array_param.count);
26 end;
27 /

v_array_varchar.count=14
v_array_integer.count=14
v_array_param.count=14

PL/SQL procedure successfully completed

SQL>

Правильный способ – это использовать BULK COLLECT INTO.

SQL> set serveroutput ON
SQL>
SQL> declare
2 -- Инициализировать уже не надо
3 v_array_varchar array_varchar;
4 v_array_integer array_integer;
5 v_array_param array_param;
6 begin
7 select emp.ename, emp.empno , param(emp.ename, emp.empno)
8 bulk collect into v_array_varchar, v_array_integer, v_array_param
9 from emp;
10 -- посмотрим количесто записей
11 dbms_output.put_line('v_array_varchar.count=' || v_array_varchar.count);
12 dbms_output.put_line('v_array_integer.count=' || v_array_integer.count);
13 dbms_output.put_line('v_array_param.count=' || v_array_param.count);
14 end;
15 /

v_array_varchar.count=14
v_array_integer.count=14
v_array_param.count=14

PL/SQL procedure successfully completed

SQL>

Еще один способ использовать функцию COLLECT.

SQL> set serveroutput ON
SQL>
SQL> declare
2 -- Инициализировать уже не надо
3 v_array_varchar array_varchar;
4 v_array_integer array_integer;
5 v_array_param array_param;
6 begin
7 select cast(collect(emp.ename) as array_varchar),
8 cast(collect(emp.empno) as array_integer),
9 cast(collect(param(emp.ename, emp.empno)) as array_param)
10 into v_array_varchar, v_array_integer, v_array_param
11 from emp;
12 -- посмотрим количесто записей
13 dbms_output.put_line('v_array_varchar.count=' || v_array_varchar.count);
14 dbms_output.put_line('v_array_integer.count=' || v_array_integer.count);
15 dbms_output.put_line('v_array_param.count=' || v_array_param.count);
16 end;
17 /

v_array_varchar.count=14
v_array_integer.count=14
v_array_param.count=14

PL/SQL procedure successfully completed

SQL>

И еще один способ использовать функцию MULTISET.


SQL> set serveroutput ON
SQL>
SQL> declare
2 -- Инициализировать уже не надо
3 v_array_varchar array_varchar;
4 v_array_integer array_integer;
5 v_array_param array_param;
6 begin
7 select cast(multiset(select emp.ename from emp) as array_varchar),
8 cast(multiset(select emp.empno from emp) as array_integer),
9 cast(multiset(select param(emp.ename, emp.empno) from emp) as array_param)
10 into v_array_varchar, v_array_integer, v_array_param
11 from dual;
12
13 -- посмотрим количесто записей
14 dbms_output.put_line('v_array_varchar.count=' || v_array_varchar.count);
15 dbms_output.put_line('v_array_integer.count=' || v_array_integer.count);
16 dbms_output.put_line('v_array_param.count=' || v_array_param.count);
17 end;
18 /

v_array_varchar.count=14
v_array_integer.count=14
v_array_param.count=14

PL/SQL procedure successfully completed

SQL>

Читать полностью

Функции FIRST и LAST

понедельник, 10 августа 2009 г. 1 коммент.

Очень редко пользуюсь этими функциями, так как область применения их достаточно узкая. Но появился случай, где эти функции можно применить. Особенность этих функций является то, что они как бы создают свое "подокно", внутри "окна" данных, которое определено для этих функций. Давай-те рассмотри на примере. В примере смотрим на табличку EMP в схему SCOTT. Задача такая: в каждом отделе нужно найти сотрудника с максимальным окладом (поле SAL имеет максимальное значение), который дольше всех работает на предприятии (поле HIREDATE имеет минимальное значение).


select emp.deptno, emp.ename, emp.sal, emp.hiredate,
first_value(emp.hiredate) over (partition by emp.deptno order by emp.sal desc, emp.hiredate asc rows between unbounded preceding and unbounded following) as fv1,
min(emp.hiredate)
keep (dense_rank last order by emp.sal) over (partition by emp.deptno) as hd1,
count(1)
keep (dense_rank last order by emp.sal) over (partition by emp.deptno) as hd2
from emp;


DEPTNOENAMESAL HIREDATE FV1 HD1 HD2
1 10 KING 5000,00 17.11.1981 17.11.1981 17.11.1981 1
2 10 CLARK 2450,00 09.06.1981 17.11.1981 17.11.1981 1
3 10 MILLER 1300,00 23.01.1982 17.11.1981 17.11.1981 1
4 20 FORD 3000,00 03.12.1981 03.12.1981 03.12.1981 2
5 20 SCOTT 3000,00 19.04.1987 03.12.1981 03.12.1981 2
6 20 JONES 2975,00 02.04.1981 03.12.1981 03.12.1981 2
7 20 ADAMS 1100,00 23.05.1987 03.12.1981 03.12.1981 2
8 20 SMITH 800,00 17.12.1980 03.12.1981 03.12.1981 2
9 30 BLAKE 2850,00 01.05.1981 01.05.1981 01.05.1981 1
10 30 ALLEN 1600,00 20.02.1981 01.05.1981 01.05.1981 1
11 30 TURNER 1500,00 08.09.1981 01.05.1981 01.05.1981 1
12 30 WARD 1250,00 22.02.1981 01.05.1981 01.05.1981 1
13 30 MARTIN 1250,00 28.09.1981 01.05.1981 01.05.1981 1
14 30 JAMES 950,00 03.12.1981 01.05.1981 01.05.1981 1

C помощью partition by emp.deptno создаем окно по подразделениям (окно, когда когда подразделения равен 20, выделено серым цветом). Внутри этого окна функция dense_rank last order by emp.sal формирует ранк по величине оклада и создает свое “подокно” (оно выделено жирным шрифтом). Уже внутри этого окна применяется функция min(emp.hiredate). Тот же результат можно получить с помощью функции first_value.



Почитать про эти функции можно здесь FIRST и LAST.



P.S. Редкое использование этих функций привело к тому, что Oracle не исправил такой интересный баг. Вместо слова order можно написать любую ерунду.



select emp.deptno, emp.ename, emp.sal, emp.hiredate,
first_value(emp.hiredate) over (partition by emp.deptno order by emp.sal desc, emp.hiredate asc rows between unbounded preceding and unbounded following) as fv1,
min(emp.hiredate)
keep (dense_rank last TRAMPARARAM by emp.sal) over (partition by emp.deptno) as hd1,
count(1)
keep (dense_rank last QQ by emp.sal) over (partition by emp.deptno) as hd2
from emp;

Читать полностью

,

Отладка с помощью DBMS_PIPE

четверг, 6 августа 2009 г. 0 коммент.

Для отладки и логирования я достаточно часто применяю пакет dbms_pipe, этот способ имеет свои преимущества и недостатки. Обычно для отладки применяется пошаговое выполнение, это удобно, но отладка должна быть разрешена, пакет должен быть скомпилирован с флажком debug, и код можно выполнить в своей сессии.

Для отладки можно использовать dbms_output, но результат виден только после выполнения всего кода, и также должна быть возможность выполнить код из своей сессии.

Также можно использовать любую таблички и в автономной транзакции писать туда служебную отладочную информацию – чаще всего так и поступаем. Единственное, чем мне не нравится этот способ – надо постоянно селектить табличку на предмет появления там новых строк.

Поэтому я достаточно часто использую передачу данных через pipe. Данные передаются не транзакционно, мы их получаем в реальном времени. Но есть и недостатки: dbms_pipe нельзя использовать в RAC, и возможны случаи, когда буфер может быть переполнен.

Сначала надо дать гранты на выполнения пакета dbms_pipe.


grant execute on dbms_pipe to myuser;

Создадим 2 метода для отсылки и приема сообщений.



create or replace package custom_pipe is

-- Author : USER
-- Purpose : работаем с pipe
-- отправить сообщение через pipe
procedure SendMessage( psPipe in varchar2
,psMessage in varchar2
);

-- получить сообщение через pipe
function ReceiveMessage( psPipe in varchar2
,psKey in varchar2
,psMessage out varchar2
) return integer;

end custom_pipe;

/



create or replace package body custom_pipe is

procedure SendMessage( psPipe in varchar2
,psMessage in varchar2
)
is
i integer;
begin
dbms_pipe.pack_message(psMessage);
i := dbms_pipe.send_message(psPipe, 30);
if i != 0 then
raise_application_error(-20101, 'Ошибка при посылке сообщения клиенту!');
end if;
end SendMessage;

function ReceiveMessage( psPipe in varchar2
,psKey in varchar2
,psMessage out varchar2
) return integer
is
call_stat integer := 0;
next_item integer := 0;
temp_varchar2 VARCHAR2(2000);
temp_date DATE;
temp_number NUMBER;
temp_rowid ROWID;
temp_raw RAW(2000);
vnResult integer := 0;
begin
psMessage := '';
call_stat := DBMS_PIPE.RECEIVE_MESSAGE(psPipe, 0);
if (call_stat = 0) then
next_item := DBMS_PIPE.NEXT_ITEM_TYPE;
while next_item > 0 loop
if next_item = 9 then
DBMS_PIPE.UNPACK_MESSAGE(temp_varchar2);
elsif next_item = 6 then
DBMS_PIPE.UNPACK_MESSAGE(temp_number);
temp_varchar2 := TO_CHAR(temp_number);
elsif next_item = 11 then
DBMS_PIPE.UNPACK_MESSAGE_ROWID(temp_rowid);
temp_varchar2 := ROWIDTOCHAR(temp_rowid);
elsif next_item = 12 then
DBMS_PIPE.UNPACK_MESSAGE(temp_date);
temp_varchar2 := TO_CHAR(temp_date,'DD.MM.YYYY HH24:MI:SS');
elsif next_item = 23 then
DBMS_PIPE.UNPACK_MESSAGE_RAW(temp_raw);
temp_varchar2 := RAWTOHEX(temp_raw);
else
temp_varchar2 := '';
-- temp_varchar2 := 'Не верный тип элемента: '||TO_CHAR(next_item);
end if;
psMessage := substr(psMessage || temp_varchar2, 1, 4000);
next_item := DBMS_PIPE.NEXT_ITEM_TYPE;
if (upper(temp_varchar2) = upper(psKey) ) then
vnResult := 1;
exit;
end if;
end loop;
elsif (call_stat = 1) then
null;
elsif (call_stat in (2,3) ) then
raise_application_error(-20100, 'Ошибка при получении сообщения!');
end if;
return(vnResult);
end ReceiveMessage;

end custom_pipe;

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



SQL> declare
vspipe varchar2(30) := 'TEST_PIPE';
begin
for i in 1 .. 20
loop
custom_pipe.sendmessage(pspipe => vspipe,
psmessage => 'Текущее время: ' || to_char(sysdate, 'dd.mm.yyyy hh24:mi:ss'));
dbms_output.put_line('Текущее время: ' || to_char(sysdate, 'dd.mm.yyyy hh24:mi:ss'));
-- задержка 4 секунды
dbms_lock.sleep(3);
end loop;
end;
/

Для получения сообщении можно воспользоваться функцией ReceiveMessage, я так и делаю, когда надо передать сообщение в другую сессию. Но для отладки я пользуюсь возможностью, которая предоставляется средой разработки “PL/SQL Developer” от Allround Automations. Выбираем в меню “Tools->Event Monitor…” и настраиваем на прослушивание своего pipe.


image


Читать полностью

, ,

KILL SESSION и DISCONNECT SESSION

среда, 22 июля 2009 г. 0 коммент.

Решил разобраться каким способом более “правильно” убивать сессию, и какую при этом получит ошибку клиент.

  с активной транзакцией без активной транзакции

ALTER SYSTEM KILL SESSION 'integer1, integer2';

Сессия помечается, как killed.

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


ORA-00028: your session has been killed
ORA-01012: not logged on

Сессия помечается, как killed.

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


ORA-00028: your session has been killed
ORA-01012: not logged on

ALTER SYSTEM KILL SESSION 'integer1, integer2' IMMEDIATE; Сессия убивается.
Если клиент проявляет активность, то ему возвращаются ошибки.

ORA-03113: end-of-file on communication channel
ORA-03114: not connected to ORACLE

Сессия убивается.
Если клиент проявляет активность, то ему возвращаются ошибки.

ORA-03113: end-of-file on communication channel
ORA-03114: not connected to ORACLE

ALTER SYSTEM DISCONNECT SESSION 'integer1, integer2' POST_TRANSACTION; Ожидается ответ пользователя. Клиент может продолжить работать в той сессии с той же транзакцией(продолжить изменять данные). Если клиент выполнит commit, то данные будут зафиксированы. После этого клиент будет отсоединен, сессия будет завершена.
Если клиент после этого проявит активность, то ему будут возращены ошибка.

ORA-03114: not connected to ORACLE

Сессия помечается, как killed.

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


ORA-00028: your session has been killed
ORA-01012: not logged on

ALTER SYSTEM DISCONNECT SESSION 'integer1, integer2' IMMEDIATE; Клиент будет отсоединен, сессия будет завершена.
Если клиент проявляет активность, то ему возвращаются ошибки.

ORA-03113: end-of-file on communication channel
ORA-03114: not connected to ORACLE

Клиент будет отсоединен, сессия будет завершена.
Если клиент проявляет активность, то ему возвращаются ошибки.

ORA-03113: end-of-file on communication channel
ORA-03114: not connected to ORACLE

ALTER SYSTEM DISCONNECT SESSION 'integer1, integer2' POST_TRANSACTION IMMEDIATE; Опция IMMEDIATE игнорируется. Ожидается ответ пользователя. Клиент может продолжить работать в той сессии с той же транзакцией(продолжить изменять данные). Если клиент выполнит commit, то данные будут зафиксированы. После этого клиент будет отсоединен.
Если клиент после этого проявит активность, то ему будут возращены ошибка.

ORA-03114: not connected to ORACLE
Клиент будет отсоединен, сессия будет завершена.
Если клиент проявляет активность, то ему возвращаются ошибки.

ORA-03113: end-of-file on communication channel
ORA-03114: not connected to ORACLE

Одним цветом выделены ячейки, когда результат команды одинаков.

Подробнее можно почитать здесь.SQL Statements: ALTER SESSION to ALTER SYSTEM, 3 of 3

Резюме: если надо быстро и надежно убить сессию, то для этого идеально подходит команда ALTER SYSTEM KILL SESSION 'integer1, integer2' IMMEDIATE;



Читать полностью

, , ,

Номер дня недели

понедельник, 6 июля 2009 г. 2 коммент.

Достаточно часто необходимо получить номер дня в недели. Для этого обычно пользуемся функцией to_char и маской 'D'. Но возращаемое значение зависит от территории, а не от языка и поэтому часто возращается не правильное значение (обычно день недели, как принято в США). Если есть возможность, то можно установить территорию перед вызовом функции.


alter session set nls_territory = 'CIS';

Но тогда, нужно предварильно сохранить территорию, поменять, применить функцию, и востановить. Я пошел другим путем, создал свою функцию, которая считает день недели, от заданного дня.

create or replace function day_of_week(pd in date)
return integer
is
--monday
vd_start date := to_date('01.01.2001','dd.mm.yyyy');
vi_result integer;
begin
vi_result := mod(trunc(pd) - vd_start, 7);
if (vi_result >= 0) then
vi_result := vi_result + 1;
else
vi_result := vi_result + 8;
end if;

return(vi_result);
end day_of_week;

Теперь проверим.


SQL> alter session set nls_territory = 'CIS';

Session altered

SQL>
SQL> with t as
2 (
3 select to_date('08.06.2000','dd.mm.yyyy') as d from dual
4 union all
5 select to_date('01.01.2001','dd.mm.yyyy') as d from dual
6 union all
7 select trunc(sysdate) + 1 as d from dual
8 )
9 select t.d, to_char(t.d, 'dy', 'NLS_DATE_LANGUAGE = RUSSIAN') as d_day, to_char(t.d, 'd') as d_st, day_of_week(t.d) as d_fn
10 from t;

D D_DAY D_ST D_FN
----------- ----- ---- ----------
08.06.2000 чт 4 4
01.01.2001 пн 1 1
07.07.2009 вт 2 2

SQL> alter session set nls_territory = 'AMERICA';

Session altered

SQL>
SQL> with t as
2 (
3 select to_date('08.06.2000','dd.mm.yyyy') as d from dual
4 union all
5 select to_date('01.01.2001','dd.mm.yyyy') as d from dual
6 union all
7 select trunc(sysdate) + 1 as d from dual
8 )
9 select t.d, to_char(t.d, 'dy', 'NLS_DATE_LANGUAGE = RUSSIAN') as d_day, to_char(t.d, 'd') as d_st, day_of_week(t.d) as d_fn
10 from t;

D D_DAY D_ST D_FN
----------- ----- ---- ----------
08.06.2000 чт 5 4
01.01.2001 пн 2 1
07.07.2009 вт 3 2


Результат функции от смены территории, не меняется.

Читать полностью

,

Пребразование long в varchar2

четверг, 2 июля 2009 г. 0 коммент.

Понадобилось поработать с метаданными, часть полей еще имеют тип long.
Для использования типа long пришлось написать функцию, которая преобразовывает long в varchar2. Возращаеются только первые 4000 символов.


create or replace function long_to_varchar(ps_sql in varchar2
) return varchar2
is
vs_into varchar2(32000);
begin
execute immediate ps_sql
into vs_into;

return substr(vs_into, 1, 4000);
end long_to_varchar;

Пример использования:

select table_name,
index_name,
column_position,
replace(long_to_varchar( 'select column_expression from all_ind_expressions where index_owner=''' || index_owner || ''' and index_name=''' || index_name || ''''), '"')
from all_ind_expressions;


Читать полностью

,

Использование RECYCLE BIN в ORACLE DB

вторник, 9 июня 2009 г. 0 коммент.

В 10 версии добавилась возможность востанавливать удаленные таблицы и индексы.

Для того что бы включить\выключить корзину:



-- Отключить корзину для сессии
ALTER SESSION SET RECYCLEBIN=OFF;
-- Отключить корзину
ALTER SYSTEM SET RECYCLEBIN=OFF SCOPE=SPFILE;

-- Включить корзину для сессии
ALTER SESSION SET RECYCLEBIN=ON;
-- Включить корзину
ALTER SYSTEM SET RECYCLEBIN=ON SCOPE=SPFILE;



По умолчанию корзина включена.

Чтобы посмотреть удаленные объеты можно воспользовать командой:


-- выбор удаленных объектов пользователя
select *
from user_recyclebin;
-- выбор всех удаленных объектов
select *
from dba_recyclebin;


Чтобы удалить объеты из корзины:


-- удалить все реинкарнации table1 из корзины
purge table table1;
-- удалить table1 из корзины по системному имени
purge table "BIN$0Vq5kNlvTS6G/uyKOlzdAw==$0";
-- удалить все реинкарнации index1 из корзины
purge table table1;
-- удалить index1 из корзины по системному имени
purge table "BIN$0Vq5kNlvSD6G/uyKOlzdAw==$0";
-- удалить все объекты пользователя "FK" из тайблспейса ts_fk.
purge tablespace ts_fk user fk;
-- удалить все объекты из тайблспейса ts_fk.
purge tablespace ts_fk;
-- удалить все объекты из корзины текущего пользователя
purge recyclebin;
-- удалить все объекты из корзины, включая системные объеткты
purge dba_recyclebin;


Подробнее о команде PURGE можно посмотреть здесь: Oracle® Database SQL Reference 10g Release 2 (10.2) PURGE

Для восстановления удаленных таблиц нужно воспользоваться командами:


-- востановить последению удаленную версию table1
flashback table table1 to before drop;
-- востановить УКАЗАННУЮ удаленнную версию table1
flashback table "BIN$CxaooGNESZa4GThzDSX5SQ==$0" to before drop;



Подробнее о корзине можно почитать тут: Using Oracle's recycle bin

Читать полностью

,

Иерархический xml в ORACLE DB

пятница, 5 июня 2009 г. 0 коммент.

Обычно функций xmlelement, xmlagg, xmlattributes и других подобных хватает на все случаи жизни. Но тут понадобилось создать иерархический xml на основе существующий иерархии.

Возьмем для примера существующую иерархию из схемы SCOTT:


select emp.empno, emp.ename, emp.job
from emp
start with emp.mgr is null
connect by prior emp.empno = emp.mgr;

Для построения иерархии воспользуеся пакетом DBMS_XMLGEN. Примеры использования можно посмотреть здесь: Generating XML Using DBMS_XMLGEN. Для этого создадим функцию.

create or replace package body xml_api is

function get_xml_hierarchy( ps_sql varchar2
) return xmltype
is
qryctx dbms_xmlgen.ctxhandle;
vclob clob;
vresult xmltype;
begin
qryctx := dbms_xmlgen.newcontextFromHierarchy(ps_sql);
vclob := dbms_xmlgen.getxml(qryctx);
vresult := xmltype(vclob);
dbms_xmlgen.closecontext(qryctx);
return vresult;
end get_xml_hierarchy;

end xml_api;

Функция использует функцию dbms_xmlgen.newcontextFromHierarchy, которая принимает один параметр, это иерархический запрос. Для этого запроса есть несколько условий:

  1. еще раз, это должен быть иерархический запрос;

  2. результат запроса должен состоять из 2 колонок;

  3. первая колонка должна быть уровнем, LEVEL

  4. вторая колонка должна быть типа xmltype;


Строим xml:

select xml_api.get_xml_hierarchy(
'
select level,
xmlelement(employee,
xmlattributes(emp.empno, emp.ename, emp.job)) as xmlelem
from emp
start with emp.mgr is null
connect by prior emp.empno = emp.mgr
')
from dual;

И смотрим результат:

<?xml version="1.0"?>
<EMPLOYEE EMPNO="7839" ENAME="KING" JOB="PRESIDENT">
<EMPLOYEE EMPNO="7566" ENAME="JONES" JOB="MANAGER">
<EMPLOYEE EMPNO="7788" ENAME="SCOTT" JOB="ANALYST">
<EMPLOYEE EMPNO="7876" ENAME="ADAMS" JOB="CLERK"/>
</EMPLOYEE>
<EMPLOYEE EMPNO="7902" ENAME="FORD" JOB="ANALYST">
<EMPLOYEE EMPNO="7369" ENAME="SMITH" JOB="CLERK"/>
</EMPLOYEE>
</EMPLOYEE>
<EMPLOYEE EMPNO="7698" ENAME="BLAKE" JOB="MANAGER">
<EMPLOYEE EMPNO="7499" ENAME="ALLEN" JOB="SALESMAN"/>
<EMPLOYEE EMPNO="7521" ENAME="WARD" JOB="SALESMAN"/>
<EMPLOYEE EMPNO="7654" ENAME="MARTIN" JOB="SALESMAN"/>
<EMPLOYEE EMPNO="7844" ENAME="TURNER" JOB="SALESMAN"/>
<EMPLOYEE EMPNO="7900" ENAME="JAMES" JOB="CLERK"/>
</EMPLOYEE>
<EMPLOYEE EMPNO="7782" ENAME="CLARK" JOB="MANAGER">
<EMPLOYEE EMPNO="7934" ENAME="MILLER" JOB="CLERK"/>
</EMPLOYEE>
</EMPLOYEE>
Читать полностью

, ,

Формирование заголовок для XML в ORACLE DB

Столкнулся с проблемой, что в ORACLE DB нет "правильной" функции для формирования xml заголовка. Кажется в 10.2 появилась функция XMLRoot , но в фукции нельзя указать кодировку xml, а хотелось бы добавить заголовок подобного типа.


<?xml version="1.0" encoding="windows-1251"?>

Заголовок можно конкатенировать к xml, но существуют ограничения по длине в 32K.
Поэтому пришлось написать свою функцию для добавления заголовка.

create or replace package body xml_api is

function add_xml_root( ps_root varchar2
,pcl_body clob
) return clob
is
vcl_temp clob;
begin
dbms_lob.createtemporary(vcl_temp, true, dbms_lob.session);

dbms_lob.open(vcl_temp, DBMS_LOB.LOB_READWRITE);
dbms_lob.writeappend(vcl_temp, length(ps_root), ps_root);
dbms_lob.append(vcl_temp, pcl_body);
dbms_lob.close(vcl_temp);

return vcl_temp;
end add_xml_root;

end xml_api;

Посмотрим результат на данных их схемы SCOTT.

select xml_api.add_xml_root('<?xml version="1.0" encoding="windows-1251"?>',
xmlelement(employee_list,
xmlagg(xmlelement(employee,
xmlattributes(emp.empno, emp.ename)))).getclobval())
from emp;



<?xml version="1.0" encoding="windows-1251"?>
<EMPLOYEE_LIST>
<EMPLOYEE EMPNO="7369" ENAME="SMITH"/>
<EMPLOYEE EMPNO="7499" ENAME="ALLEN"/>
<EMPLOYEE EMPNO="7521" ENAME="WARD"/>
<EMPLOYEE EMPNO="7566" ENAME="JONES"/>
<EMPLOYEE EMPNO="7654" ENAME="MARTIN"/>
<EMPLOYEE EMPNO="7698" ENAME="BLAKE"/>
<EMPLOYEE EMPNO="7782" ENAME="CLARK"/>
<EMPLOYEE EMPNO="7788" ENAME="SCOTT"/>
<EMPLOYEE EMPNO="7839" ENAME="KING"/>
<EMPLOYEE EMPNO="7844" ENAME="TURNER"/>
<EMPLOYEE EMPNO="7876" ENAME="ADAMS"/>
<EMPLOYEE EMPNO="7900" ENAME="JAMES"/>
<EMPLOYEE EMPNO="7902" ENAME="FORD"/>
<EMPLOYEE EMPNO="7934" ENAME="MILLER"/>
</EMPLOYEE_LIST>
Читать полностью

, ,

DML Error Logging

вторник, 26 мая 2009 г. 0 коммент.

В 10.2 наконец-то появилось логирование ошибок при выполнение dml команд insert, update, delete и merge.

Почитать хорошую статью можно тут: DML Error Logging in Oracle 10g Database Release 2
В документации пример можно посмотреть здесь: Inserting Data with DML Error Logging

Единственно что нужно, это уточнить пару моментов:

  1. Команда REJECT LIMIT указывает на максимально количество ошибок, которое может произойти, прежде чем statement "отвалится". Представим, что у нас при выполнении statement произойдет 2 ошибки, что будет происходить, если при разных значениях REJECT LIMIT.

  2. REJECT LIMITОшибка?ЛогированиеТранзакция
    0отрайзится ошибкав логе будет 1 строкапроизойдет автоматический rollback
    1отрайзится ошибкав логе будет 2 строкипроизойдет автоматический rollback
    2, больше и UNLIMITEDошибки не будет!в логе будет 2 записинужно будет подтвердить или откатить транзацию.

    Строки в табличке для логирования появятся в любом случае, так как они добавляются в автономной транзакции.
  3. Можно использовать 'simple_expression' для последующей выборки из таблицы логирования по колонке ORA_ERR_TAG$. Особенно это полезно, при параллельном выполнении команд.
  4. Логирование не работает в следующих случаях:

    • есть отложенное ограничение;
Читать полностью

, , ,

Изменение размера tablespace'а (resize tablespace)

четверг, 16 апреля 2009 г. 4 коммент.

Очередной раз закончилось место на диске, и в результате поиска свободного места пришла идея уменьшить размер разросшегося tablespace'а.

Смотрим размер tablespace'ов:


select a.tablespace_name ,
round(a.bytes_alloc / 1024 / 1024, 2) m_alloc,
round(nvl(b.bytes_free, 0) / 1024 / 1024, 2) m_free,
round((a.bytes_alloc - nvl(b.bytes_free, 0)) / 1024 / 1024, 2) m_used,
round(maxbytes/1048576,2) Max
from ( select f.tablespace_name,
sum(f.bytes) bytes_alloc,
sum(decode(f.autoextensible, 'YES',f.maxbytes,'NO', f.bytes)) maxbytes
from dba_data_files f
group by tablespace_name) a,
( select f.tablespace_name,
sum(f.bytes) bytes_free
from dba_free_space f
group by tablespace_name) b
where a.tablespace_name = b.tablespace_name (+)

Результат:
TABLESPACE_NAMEM_ALLOCM_FREEM_USEDMAX
1UNDOTBS1335323,6911,3132767,98
2SYSAUX36019,38340,6332767,98
3USERS54,560,4432767,98
4SYSTEM6805,56674,4432767,98
5TS_TEST3272032607,69112,3132767,98

Оказалось, что TS_TEST лежит в одном файле и занимает 32720Mb, а использует 112,31Mb. Поэтому следущим желанием было уменьшить размер, хотя бы до 1000MB.

ALTER DATABASE DATAFILE 'D:\ORACLE\ORADATA\FKHD\TS_STATDEMO01.DBF' RESIZE 1000M;

Видим ошибку:

ORA-03297: file contains used data beyond requested RESIZE value

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

select dba_data_files.file_name,
dba_data_files.file_id,
dba_data_files.tablespace_name,
ceil((nvl(hwm, 1) * db_block_size) / 1024 / 1024) smallest,
ceil(blocks * db_block_size / 1024 / 1024) currsize,
ceil(blocks * db_block_size / 1024 / 1024) -
ceil((nvl(hwm, 1) * db_block_size) / 1024 / 1024) savings
from dba_data_files,
(select file_id,
max(block_id + blocks - 1) hwm
from dba_extents
group by file_id) b,
(select value db_block_size from v$parameter where name = 'db_block_size') c
where dba_data_files.file_id = b.file_id(+);

Результат:
 FILE_NAMEFILE_IDTABLESPACE_NAMESMALLESTCURRSIZESAVINGS
1D:\ORACLE\ORADATA\TEST\SYSTEM01.DBF1SYSTEM6766804
2D:\ORACLE\ORADATA\TEST\UNDOTBS01.DBF2UNDOTBS1140335195
3D:\ORACLE\ORADATA\TEST\USERS01.DBF4USERS154
4D:\ORACLE\ORADATA\TEST\TS_TEST01.DBF5TS_TESTSTATDEMO30119327202601
5D:\ORACLE\ORADATA\TEST\SYSAUX01.DBF3SYSAUX3513609

Получется что уменьшить можно только до 30119Mb.
А это значит, что таблички нужно переносить в другой tablespace. Можно перенести все сразу таблички, уменьшить размер, и вернуть таблички на место. А можно найти объекты, которые раскиданы по tablespace, и переместить только их.

select dba_extents.owner,
dba_extents.segment_name,
dba_extents.segment_type,
dba_extents.tablespace_name,
dba_extents.file_id,
dba_extents.block_id
from dba_extents,
(select file_id,
max(block_id) max_block_id
from dba_extents
group by file_id) b
where dba_extents.file_id = b.file_id and
dba_extents.block_id = b.max_block_id;

Результат:
 OWNERSEGMENT_NAMESEGMENT_TYPETABLESPACE_NAMEFILE_IDBLOCK_ID
1SYSC_OBJ#CLUSTERSYSTEM186281
2SYS_SYSSMU7$TYPE2 UNDOUNDOTBS1217833
3SYSSYS_IOT_TOP_8797INDEXSYSAUX344905
4SCOTTSALGRADETABLEUSERS449
5TESTTMP_TESTTABLETS_TEST5681

Переносим табличку другой tablespace.

-- Переносим табличку
alter table test.tmp_test move tablespace ts_test2;
-- Переносим индекс
alter index test.tmp_index rebuild tablespace ts_test2;

Также определяем и переносим другие таблики или индексы,и пробуем снова уменьшить tablespace.
Опять таже ошибка, такое ощущение, что что-то забыли проверить. И действительно. Забыли про RECYCLEBIN.
Так как объекты переименовывыются, при попадании в корзину, то поищем эти объекты. Все они начинаются с "BIN$"

select decode(partition_name, null,
segment_name,
segment_name || ':' || partition_name) objectname,
segment_type object_type,
owner,
tablespace_name,
header_block
from dba_segments
where tablespace_name = 'TS_TEST' and
segment_name like 'BIN$%';

Результат:
OBJECTNAMEOBJECT_TYPEOWNERTABLESPACE_NAMEHEADER_BLOCK
1BIN$0IlaH5/6SyGv+h8B8BzJzQ==$0TABLETESTTS_TEST1010955
2BIN$RX30mLpdRd6nHwalSESbuA==$0TABLETESTTS_TEST3854995
3BIN$9aT/N+UpQ3+03oi6GC6dYA==$0INDEXTESTTS_TEST3855203
4BIN$QgU0TDpdQVen+1ciGtl63g==$0TABLETESTTS_TEST1010971

Мне не нужны эти объекты, я их удаляю:

purge table test."BIN$0IlaH5/6SyGv+h8B8BzJzQ==$0";
--...

Все. Уменьшаем tablespace и переносим таблички и индексы обратно.
UPD: Decrease HWM (high watermark) of a tablespace Читать полностью

,

Размер Clob

среда, 1 апреля 2009 г. 0 коммент.

Понадобилось определить размер clob в байтах. В базе установлена многобайтовая кодировка AL32UTF8. Функция length и dbms_lob.getlength возращают результат в символах, функция lengthb не применима для clob, если используется многобайтовая кодировка (функция возращает ошибку "ORA-22998: CLOB и NCLOB в многобайтовых кодовых таблицах не поддерживается").

Для определения размера пришлось сначала конвертировать clob в blob, а потом уже определить размер blob в байтах.


function clob_to_blob(p_clob in clob
) return blob
is
v_blob blob := null;


v_in pls_integer := 1;
v_out pls_integer := 1;

v_lang pls_integer := dbms_lob.default_lang_ctx;
v_warning pls_integer := dbms_lob.no_warning;
begin
if (p_clob is not null) then
dbms_lob.createtemporary(v_blob, true, dbms_lob.session);

dbms_lob.convertToBlob( dest_lob => v_blob
,src_clob => p_clob
,amount => dbms_lob.getlength(p_clob)
,dest_offset => v_in
,src_offset => v_out
,blob_csid => dbms_lob.default_csid
,lang_context => v_lang
,warning => v_warning
);
end if;
return v_blob;
end clob_to_blob;

function get_clob_size_in_bytes(p_clob in clob
) return number
is
v_blob blob;
v_result number := null;
begin
v_blob := clob_to_blob(p_clob => p_clob);
if (v_blob is not null) then
v_result := dbms_lob.getlength(v_blob);

if (dbms_lob.istemporary(lob_loc => v_blob) = 1) then
dbms_lob.freetemporary(v_blob);
end if;
end if;
return v_result;
end get_clob_size_in_bytes;

Подсказка взята отсюда How to find the size of clob cloumn when the character set is AL32UTF8??

UPD: Size of CLOB or BLOB in Oracle DB


Читать полностью

, , , ,

Таймаут при вызове bpel-процесса

среда, 18 марта 2009 г. 0 коммент.

При вызове сервиса в Oracle BPEL cтолкнулся с такой ошибкой "java.lang.IllegalStateException: Cannot call setRollbackOnly() current thread is NOT associated with a transaction".

Ошибку также можно найти в логе по следующему пути:
"SOA_Oracle_Home\opmn\logs\".
Смотрим файл лога для контейнера в котором крутиться BPEL.

Поиск по документации привел к следующему решению.
A.1.1 Setting Properties for BPEL Processes to Successfully Complete and Catch Exception Errors

Следует обратить внимание не следующие особенности:
1. Параметр transaction-timeout в файле "SOA_Oracle_Home\j2ee\home\config\transaction-manager.xml".
1.1. На самом деле необходимо изменять файл, который повлияет на контейнер в котором крутиться bpel, т.е. правильный путь будет "SOA_Oracle_Home\j2ee\КОНТЕЙНЕР_ГДЕ_КРУТИТЬСЯ_BPEL\config\transaction-manager.xml"
1.2. Для исталяции одинокого Oracle BPEL Process Manager файл надо искать здесь "SOA_Oracle_Home\bpel\system\appserver\oc4j\j2ee\home\config".
2. Параметр transaction-timeout в файле "SOA_Oracle_Home\j2ee\home\application-deployments\orabpel\ejb_ob_engine\orion-ejb-jar.xml".
2.1. Также надо искать по другому пути.
"SOA_Oracle_Home\j2ee\КОНТЕЙНЕР_ГДЕ_КРУТИТЬСЯ_BPEL\application-deployments\orabpel\ejb_ob_engine\orion-ejb-jar.xml".
2.2. Обязательно поменять параметр во всем файле, он встречается несколько раз, для каждого бина.
3. Параметр syncMaxWaitTime в "SOA_Oracle_Home\bpel\domains\domain_name\config\domain.xml"
3.1. Не забываем поменять параметр для каждого домена.

Перестартовываем Application Server.

Иногда при длительном ожидании ответа от bpel-процесса, можно поймать ошибку "500 Server Error". В данном случае ругается Apache.
Что бы увеличить таймаут ожидания, находим параметр Timeout в файле "SOA_Oracle_Home\Apache\Apache\conf\httpd.conf" и увеличиваем значение. Перестартовываем Apache. Читать полностью

, , ,

Инсталляция ORACLE 10g. ORA-12547: TNS:lost contact

пятница, 20 февраля 2009 г. 0 коммент.

Проблема возникла при создании экземпляра сразу после инсталляции ORACLE 10g Release 2 (10.2) for Linux x86 на операционную систему openSUSE 10.2 2.6.18.2-34-default.

Начнем проверку с запускаемого файла.


> cd $ORACLE_HOME/bin
> ldd ./oracle
linux-gate.so.1 => (0xffffe000)
libskgxp10.so => /oracle/product/db1020/lib/libskgxp10.so (0xb7f75000)
libhasgen10.so => /oracle/product/db1020/lib/libhasgen10.so (0xb7e91000)
libskgxn2.so => /oracle/product/db1020/lib/libskgxn2.so (0xb7e8f000)
libocr10.so => /oracle/product/db1020/lib/libocr10.so (0xb7e21000)
libocrb10.so => /oracle/product/db1020/lib/libocrb10.so (0xb7ddc000)
libocrutl10.so => /oracle/product/db1020/lib/libocrutl10.so (0xb7d81000)
libjox10.so => /oracle/product/db1020/lib/libjox10.so (0xb7662000)
libclsra10.so => /oracle/product/db1020/lib/libclsra10.so (0xb7659000)
libdbcfg10.so => /oracle/product/db1020/lib/libdbcfg10.so (0xb7641000)
libnnz10.so => /oracle/product/db1020/lib/libnnz10.so (0xb743c000)
libaio.so.1 => not found
libdl.so.2 => /lib/libdl.so.2 (0xb741e000)
libm.so.6 => /lib/libm.so.6 (0xb73f8000)
libpthread.so.0 => /lib/libpthread.so.0 (0xb73e0000)
libnsl.so.1 => /lib/libnsl.so.1 (0xb73c9000)
libc.so.6 => /lib/libc.so.6 (0xb729b000)
/lib/ld-linux.so.2 (0xb7f99000)

Библиотека libaio.so.1 на найдена, проверим установлен ли пакет.

> rpm -q libaio
пакет libaio не установлен

Устанавливаем пакет. После этого экземпляр можно успешно создать. За помощью можно также обратиться на метелинк: Database Configuration Assistant (DBCA) reports ORA-12547 creating a new database on Linux Читать полностью

, , ,

Как получить userName из WS-Security soap-заголовка, способ 2

вторник, 17 февраля 2009 г. 0 коммент.

Я уже писал тут, как можно получить userName из soap-заголовка.
Теперь это можно сделать еще одним способом, зарегистрировав свой custom step в wsm.
Для этого в wsm идем по этой дорожке "Policy Management > Manage Policies > Steps", нажимаем "Add New Step" и указываем путь к конфигурационному xml-файлу своего custom step'а.

В файле указываем название шага, java-пакет, который реализует наш шаг, идентификатор, и одно свойство "Enabled" - разрешен или запрещен шаг.


<csw:StepTemplate
xmlns:csw="http://schemas.confluentsw.com/ws/2004/07/policy"
name="owsmcustomstep"
package="test_owsmcustomstep"
timestamp="Oct 15, 2007 05:00:00 PM"
version="1"
id="200902051">

<csw:Description>Custom step test</csw:Description>

<csw:Implementation>test_owsmcustomstep.CustomTestStep</csw:Implementation>

<csw:PropertyDefinitions>
<csw:PropertyDefinitionSet name="Basic Properties">
<csw:PropertyDefinition name="Enabled" type="boolean">
<csw:Description>If set to true, this step is enabled</csw:Description>
<csw:DefaultValue>
<csw:Absolute>true</csw:Absolute>
</csw:DefaultValue>
</csw:PropertyDefinition>
</csw:PropertyDefinitionSet>
</csw:PropertyDefinitions>
</csw:StepTemplate>

Ниже представлен класс, который реализует custom step.
Сначала получаем сообщение, возвращаем его в виде XML-документа. Находим имя пользователя и элемент, куда запишем это имя. После этого создаем новое сообщение на основе измененного XML-документа и возвращаем его.

package test_owsmcustomstep;

import com.cfluent.policysteps.sdk.AbstractStep;
import com.cfluent.policysteps.sdk.IMessageContext;
import com.cfluent.policysteps.sdk.IResult;
import com.cfluent.policysteps.sdk.Result;
import com.cfluent.policysteps.sdk.Fault;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;

import org.apache.axis.message.SOAPEnvelope;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class CustomTestStep extends AbstractStep {
private PrintWriter out = null;

public CustomTestStep() {
}

public void init() throws IllegalStateException {
try {
out = new PrintWriter(new BufferedWriter(new FileWriter("log/CustomTestStep.log", true)));
}
catch (Exception e) {
String errMsg = "Error in creating log file for custom step:" + e.getMessage();
System.err.println(errMsg);
e.printStackTrace();
throw new IllegalStateException(errMsg);
}
}

/**
* Used for cleaning up any resources created in the init() method
* It is called during server shutdown or when the policy is updated.
*/
public void destroy() {
out.close();
}

private void log(String str) {
try {
Date d = new Date();
DateFormat df = new SimpleDateFormat("HH:mm:ss yyyy/MM/dd");

out.println(df.format(d) + ":1158: " +str);
out.flush();
} catch (Exception ex) {
System.err.println("Exception encountered when writing to file");
ex.printStackTrace();
}
}

private void logSOAPMessage(SOAPMessage soapMsg) {
String msg = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
soapMsg.writeTo(baos);
msg = baos.toString();
log("CustomTestStep: Request SOAP message is " + msg);
} catch (Exception ex) {
System.err.println("Exception encountered while converting SOAP message to a String");
ex.printStackTrace();
}
}

public IResult execute(IMessageContext messageContext) throws Fault {
log("CustomTestStep: Entering");

IResult resultStatus = new Result();
resultStatus.setStatus(IResult.FAILED);

try {
//Getting the SOAPMessage object from the context
SOAPMessage soapMessage = messageContext.getRequestMessage();
// loging request
logSOAPMessage(soapMessage);

SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = (SOAPEnvelope)soapPart.getEnvelope();

String namespaceURI_soap = "http://schemas.xmlsoap.org/soap/envelope/";
String namespaceURI_wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

// get XML-Document
Document document = soapEnvelope.getAsDocument();
Element rootElement = document.getDocumentElement();
// element, where put username
Element elementInput = null;
String username = null;

// search username
if(rootElement != null) {
Element header = getToken(rootElement, "Header", namespaceURI_soap);
if(header != null) {
Element securityHeader = getToken(header, "Security", namespaceURI_wsse);
if(securityHeader != null) {
Element usernameTokenElement = getToken(securityHeader, "UsernameToken", namespaceURI_wsse);
if(usernameTokenElement != null) {
Element usernameElement = getToken(usernameTokenElement, "Username", namespaceURI_wsse);
if(usernameElement != null) {
username = usernameElement.getTextContent();
}
}
}
}
}

// search element
if(rootElement != null) {
String namespaceURI_ns1 = "http://xmlns.oracle.com/test_soap";
Element body = getToken(rootElement, "Body", namespaceURI_soap);
if(body != null) {
elementInput = getToken(body, "input", namespaceURI_ns1);
}
}

if (username != null && elementInput != null) {
elementInput.setTextContent(username);
}

//Convert the Document object to a String Object
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StringWriter strWriter = new StringWriter();
StreamResult streamResult = new StreamResult(strWriter);
transformer.transform(domSource, streamResult);
String xmlString = strWriter.toString();

//Convert this string object to a SOAPMessage object
MessageFactory messageFactory = MessageFactory.newInstance();
MimeHeaders mimeheaders = new MimeHeaders();
mimeheaders.addHeader("Content-Type", "text/xml");

// IMPORTANT : The following is very important especially if your
// SOAP message would contain UTF-8 data like European characters etc.
ByteArrayInputStream soapByteArrayInputStream = new ByteArrayInputStream(xmlString.getBytes("UTF-8"));
SOAPMessage newSoapMessage = messageFactory.createMessage(mimeheaders, soapByteArrayInputStream);

//Set the SOAP Request back into the context
messageContext.setRequestMessage(newSoapMessage);

// logging new request
logSOAPMessage(messageContext.getRequestMessage());
log("CustomTestStep: Exiting");
resultStatus.setStatus(IResult.SUCCEEDED);

} catch (Exception ex) {
String errMsg = ex.getMessage();
log("CustomTestStep: Error:" + errMsg);
generateFault(errMsg);
}

return resultStatus;
}

private Element getToken(Element parentElement, String tokenName, String tokenNameSpaceURI)
{
Element token = null;
NodeList nodeList = parentElement.getElementsByTagNameNS(tokenNameSpaceURI, tokenName);
if (nodeList != null) {
token = (Element)nodeList.item(0);
}
return token;
}

}

Примеры для создания custom step в WSM можно найти на metalink:How To read a SOAP Message into an XML Document & back into a SOAP Message?

Читать полностью

, , ,

Соответсвие WSDL рекомендациям WS-I версии 1.1

понедельник, 26 января 2009 г. 0 коммент.

Обычная ситуация: делаешь сервисы, они хорошо работают.
Но тут приходит время использование сервисов внешними системами и оказывается, что сделанные сервисы не соответсвуют рекомендациям WS-I и хоть это и рекомендации, но внешние системы все эти рекомендации соблюдают, а твои сервисы нет. :(

Рассмотрим один случай. Есть WSDL.


<?xml version="1.0" encoding="UTF-8"?>
<definitions
name="test_wsdl"
targetNamespace="http://xmlns.oracle.com/test_wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:ns1="http://xmlns.oracle.com/test_scheme"
xmlns:client="http://xmlns.oracle.com/test_wsdl">
<!--> start cut
...
end cut </!-->
<types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://xmlns.oracle.com/test_scheme"
schemaLocation="test_scheme.xsd"/>
</xsd:schema>
<!--> start cut
...
end cut </!-->
</types>
<message name="test_wsdl_getdata1RequestMessage">
<part name="payload" element="ns1:Request"/>
</message>
<message name="test_wsdl_getdata1ResponseMessage">
<part name="payload" element="ns1:Response"/>
</message>
<message name="test_wsdl_getdata2RequestMessage">
<part name="payload" element="ns1:Request"/>
</message>
<message name="test_wsdl_getdata2ResponseMessage">
<part name="payload" element="ns1:Response"/>
</message>
<!--> start cut
...
end cut </!-->
<portType name="test_wsdl">
<operation name="getdata1">
<input message="tns:test_wsdl_getdata1RequestMessage"/>
<output message="tns:test_wsdl_getdata2ResponseMessage"/>
</operation>
<operation name="getdata2">
<input message="tns:test_wsdl_getdata2RequestMessage"/>
<output message="tns:test_wsdl_getdata2ResponseMessage"/>
</operation>
<!--> start cut
...
end cut </!-->
</portType>
<binding name="test_wsdlBinding" type="tns:test_wsdl">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getdata1">
<soap:operation style="document" soapAction="subscribe"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="getdata2">
<soap:operation style="document" soapAction="getdata"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<!--> start cut
...
end cut </!-->
</binding>
<service name="test_wsdl">
<port name="test_wsdlPort" binding="tns:test_wsdlBinding">
<soap:address
location="http://myhost:80/orabpel/domain_test/test_wsdl/1.0"/>
</port>
</service>
<plnk:partnerLinkType name="test_wsdl">
<plnk:role name="test_wsdlProvider">
<plnk:portType name="tns:test_wsdl"/>
</plnk:role>
</plnk:partnerLinkType>
</definitions>

Проверяем wsdl программкой взятой с родного сайта: Interoperability Testing Tools 1.1

Assertion: BP2120
Failure Detail Message
getdata1,
getdata2
Element Location: lineNumber=96

В логе видим, что пункт "BP2120" не выполнен. Ошибка вот в этой строке:

<binding name="test_wsdlBinding" type="tns:test_wsdl">

Читаем про это: Test Assertion: BP2120
Хм, в байдинге не уникальные операции. Читаем дальше требования:
R2710

4.7.6 Operation Signatures

Definition: operation signature

The profile defines the "operation signature" to be the fully qualified name of the child element of SOAP body of the SOAP input message described by an operation in a WSDL binding.

In the case of rpc-literal binding, the operation name is used as a wrapper for the part accessors. In the document-literal case, since a wrapper with the operation name is not present, the message signatures must be correctly designed so that they meet this requirement.

An endpoint that supports multiple operations must unambiguously identify the operation being invoked based on the input message that it receives. This is only possible if all the operations specified in the wsdl:binding associated with an endpoint have a unique operation signature.

R2710The operations in a wsdl:binding in a DESCRIPTION MUST result in operation signatures that are different from one another.


Оказывается все просто. Точка входа (вызова) должна быть однозначно определена, в нашем случае part и element одинаковые для операций getdata1 и getdata2.

<message name="test_wsdl_getdata1RequestMessage">
<part name="payload" element="ns1:Request"/>
</message>

<message name="test_wsdl_getdata2RequestMessage">
<part name="payload" element="ns1:Request"/>
</message>

Изменеяем схему, добавляем новый элемент Request2, копию Request1 и используем его.

<message name="test_wsdl_getdata1RequestMessage">
<part name="payload" element="ns1:Request"/>
</message>
<message name="test_wsdl_getdata2RequestMessage">
<part name="payload" element="ns1:Request2"/>
</message>
Читать полностью

, , ,

Как получить userName из WS-Security soap-заголовка

четверг, 22 января 2009 г. 0 коммент.

Иногда в BPEL-процессе необходимо знать, какой пользователь вызвал этот web-сервис. Хорошо, когда имя пользователя передается в структуре сообщения, но когда имя пользователя необходимо получить из заголовка soap-сообщенини, то возникают небольшие трудности. Дело осложняется, если сервис защищен, например OWSM, с авторизацией в каком-нибудь LDAP-каталоге. Посмотрим как их можно решить.

Посмотрим на рисунок.



В этом "PipeLine" выполняются шаги по извлечению информации для авторизации "Extract Credentials", аутентификации "Ldap Authenticate" и авторизации "Ldap Authorize".
После выполнения шага "Extract Credentials" из сообщения удаляется информация о пользователе и пароле, поэтому чтобы получить необходимую информацию, добавим шаг "XML Transform", где укажем свою xsl-трансформацию.


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:ns1="http://xmlns.oracle.com/test_soap"
version="1.0" >
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns1:input">
<xsl:element name="{name()}">
<xsl:value-of select="/soap:Envelope/soap:Header/wsse:Security/wsse:UsernameToken/wsse:Username"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Трансформация копирует сообщение один в один за исключением одного элемента "ns1:input", в этом элемент мы копируем имя пользователя. Читать полностью