![]() ![]() ![]() ![]() |
この章では、手順の構成を XQuery ベースのデータ サービスに追加できる BEA XQuery Scripting Extension (XQSE) について説明します。この章では、言語の拡張と文の文法についていくつかの例を挙げて説明します。
ALDSP データ サービスは、XQuery 言語に基づいており、データに対するクエリを表現するために XML の構成を使用することができます。XQuery スクリプト拡張は、基本文、コントロール フローおよびユーザ定義プロシージャなど手順の構成を XQuery に追加して、このベースに構築します。
このように、XQSE は XQuery のスーパーセットで、XML および XQuery のコンテキスト内で動作する、充実した複雑なデータサービスを作成できる追加の機能が拡張されています。Oracle PL/SQL が SQL の拡張であるのと同様に XQSE は XQuery の拡張と考えることができます。
XQSE は、手順の機能を XQuery の宣言型クエリ機能に追加して XQuery を拡張します。XQuery では、クエリが、プロローグの後に XQuery 式を追加して構成されます。クエリのプロローグは、ネームスペース、外部変数および関数など他の情報を定義して式の環境を設定します。
XQSE については、プロローグにはプロシージャおよび XQSE 関数の定義も含まれます。プロシージャおよび XQSE 関数は、それぞれ、XQSE で記述された実行の副作用呼び出し可能な単位および非副作用呼び出し可能な単位です。
Prolog ::= ((DefaultNamespaceDecl | Setter | NamespaceDecl | Import)
Separator)* ((VarDecl | FunctionDecl | ProcedureDecl |
XQSEFunctionDecl | OptionDecl) Separator)*
QueryBody ::= Expr | Block
XQSE では、最上位レベルクエリの本体は、XQuery 式または XQSE ブロックのいずれかとなる可能性があります。ブロックは、順番に実行された文のシーケンスです。
XQSE は、ALDSP データ サービスのプロローグにプロシージャを宣言することができます。XQSE プロシージャは、XQuery 関数と同じですが、関数とは異なり、プロシージャには、1 つまたは複数の副作用が含まれています。もう一つ違いは、XQuery 関数が宣言型であり、XQuery 関数の本文は XQuery の式であることです。
対照的に、XQSE プロシージャの本文はブロックです。このブロックは、プロシージャを呼び出したときに、順番に実行された文のリストのことです。または、XQuery では、プロシージャを外部プロシージャとして宣言できます。この場合、プロシージャには本文が存在しなく、ALDSP によって、リレーショナル ストアド プロシージャまたは Web サービスなど外部データ ソースからプロシージャをインポートして実装されます。
ProcedureDecl ::= "declare" "procedure" QName "(" ParamList?")"
("as" SequenceType)?(Block | ("external") )
プロシージャは、必須ではありませんが、値を返す場合があります。個々の XQSE 文自体が戻り値を持たないので、プロシージャはその本文に明示的な return 文が含まれている場合にのみ値を返します。プロシージャの戻り値型が指定されていない場合は、その戻り値型はデフォルトで item()*
です。
注意 : | 値を返すのは任意なので、プロシージャの本文に return 文は不要です。return 文が存在しない場合、プロシージャの戻り値は、空のシーケンスであり、戻り値型は empty() です。 |
プロシージャでは、再帰を使用することができます。これは、ALDSP の XQuery 関数と XQSE プロシージャにおけるもう一つの違いです。
(: Procedure to delete an employee given just their employee ID :)
(: Calls the default delete function on the data service after retrieving the employee object using the ID :)
declare procedure tns:deleteByEmployeeID($id as xs:string?) as empty() {
declare $emp as element(empl:Employee)?:= tns:getByEmployeeID($id);
tns:delete($emp);
};
XQSE は、プロシージャに加えて、XQSE ベースの関数を宣言できるように XQuery 関数宣言の構文を拡張します。XQSE 関数は本質的に、副作用のない XQSE に記述された読取専用のプロシージャです。
プロシージャと同様に、XQSE 関数の本体は、文のリストであるブロックから構成されています。次に、XQSE 関数宣言の文法を示します。
XQSEFunctionDecl ::= "declare" xqse "function" QName "(" ParamList?")"
("as" SequenceType)?(Block | ("external") )
XQSE 関数は、副作用を持たないため、通常 XQuery 関数を呼び出すの同様に XQuery 式内のどこからでも呼び出すことができます。
XQuery 関数は、宣言型関数でコンパイル時のクエリの最適化に適切なため、データ サービス関数をできるだけ XQSE ではなく、XQuery 関数を使用して記述する必要があります。ただし、特定の読み取り専用計算を手順に表現する必要がある場合(または少なくとも概念的にさらに便利な場合) もあります。この場合は、XQSE 関数が最適です。
たとえば、XQSE 関数を使用して計算を行うことができます。XQuery の場合は、その代わりに、末尾再帰を使用する必要があります。これは、ALDSP では、XQuery 関数での再帰の使用が許可されていないためです。
(: Procedure to compute the level of an employee in the org tree :)
declare xqse function tns:distanceFromTop($id as xs:string?) as xs:integer?{
declare $mgrCnt as xs:integer := 0;
declare $curEmp as element(empl:Employee)?:=
tns:getByEmployeeID($id);
declare $mgrId as xs:string?:= fn:data($curEmp/ManagerID);
if (fn:empty($curEmp)) then return value ();
while (fn:not(fn:empty($mgrId))) {
set $mgrCnt := $mgrCnt + 1;
set $curEmp := tns:getByEmployeeID($mgrId);
set $mgrId := fn:data($curEmp/ManagerID);
};
return value ($mgrCnt);
};
XQSE は、関数とプロシージャの呼び出しを区別するために Value 文とプロシージャの呼び出しを提供します。
注意 : | 式を使用できる場所であればどこでも関数を呼び出すことができます。ただし、プロシージャは、副作用を持つので、XQSE の一部の場所にのみ呼び出すことができます。 |
次に、XQSE の Value 文およびプロシージャの呼び出し文の文法を示します。
ValueStatement := ExprSingle | ProcedureCall
ProcedureCall ::= FunctionCall
Statement := SimpleStatement | BlockStatement
SimpleStatement ::= SetStatement | IfStatement | ReturnStatement |
ProcedureCall
XQSE ブロックには、文のリストが含まれます。ブロックを使用して、可変変数 (宣言句を使用する) を宣言することができます。これらの変数を以降の文で操作し順番に実行することができます。次に、XQSE の block
文の文法を示します。
Block ::= {" ((BlockDecl ";")* StatementSequence "}"
BlockDecl ::= "declare" "$" VarName TypeDeclaration?
(":=" ValueStatement)?
StatementSequence := ((SimpleStatement ";") | (BlockStatement (";")?))*
BlockStatement := WhileStatement | IterateStatement | TryStatement |
Block
XQuery 式には、値がある場合、文が、(Return 文に記載した「return
」文を除く) 値を返しません。したがって、ブロック自体が複合文であるためブロックには戻り値が存在しません。
ブロックの各変数は、それを使用する前に宣言する必要があります。変数を明示的に型を指定しないで宣言した場合、デフォルトの型は item()*
となります。
注意 : | ブロック内の変数が変更可能です。XQuery 式で表われる、値に対して名前の変更不可能なバインディングである、let および for 変数とは異なり、XQSE ブロック内の変数を割り当てることができます。(Java および C++ など他の言語の変数と同じ)。 |
ネストされたブロックを定義することができます。この場合は、通常のスコープ ルールを適用します。たとえば、内部ブロックで宣言された特定の完全修飾名を持つ変数は、まったく同じ完全修飾名を持つ外部ブロックの変数をシャドウ (再定義し非表示) します。
(: Procedure to compute the level of an employee in the org tree :)
declare xqse function tns:distanceFromTop($id as xs:string?)
as xs:integer?{
declare $mgrCnt as xs:integer := 0;
declare $curEmp as element(empl:Employee)?:=
tns:getByEmployeeID($id);
declare $mgrId as xs:string?:= fn:data($curEmp/ManagerID);
if (fn:empty($curEmp)) then return value ();
while (fn:not(fn:empty($mgrId))) {
set $mgrCnt := $mgrCnt + 1;
set $curEmp := tns:getByEmployeeID($mgrId);
set $mgrId := fn:data($curEmp/ManagerID);
}
return value ($mgrCnt);
};
XQSE の set
文は ValueStatement
によって指定された値に VarName
変数を設定します。次に、XQSE set
文の文法を示します。
SetStatement ::= "set" "$" VarName ":=" ValueStatement
set
文を使用する前に、declare
文を使用して VarName
変数を宣言する必要があります。上記のように宣言された変数のみが変更可能なので、set
文を使用して変更することができます。
注意 : | set 文には、コピー セマンティクスがあります。次のインスタンスを見てみましょう。 |
set $z := ($x, $y)
$x
と $y
は、変更可能な変数であり、$x
と $y
については、後で変更した場合、$z には、$x
と $y
の元のコピーされた値が保持されます。
(: Procedure to compute the level of an employee in the org tree :)
declare xqse function tns:distanceFromTop($id as xs:string?)
as xs:integer?{
declare $mgrCnt as xs:integer := 0;
declare $curEmp as element(empl:Employee)?:=
tns:getByEmployeeID($id);
declare $mgrId as xs:string?:= fn:data($curEmp/ManagerID);
if (fn:empty($curEmp)) then return value ();
while (fn:not(fn:empty($mgrId))) {
set $mgrCnt := $mgrCnt + 1;
set $curEmp := tns:getByEmployeeID($mgrId);
set $mgrId := fn:data($curEmp/ManagerID);
};
return value ($mgrCnt);
};
XQSE while
文は、条件の有効なブール値が true に評価されるまで、ループしてブロックでアクションを実行します。次に、XQSE while
文の文法を示します。
WhileStatement ::= "while" "(" Expr ")" Block
while 文は、各ループの前に、条件の式を再評価します。通常、条件はブロックで操作された可変変数に依存します。したがって、ループが、条件の有効なブール値が true に評価されると、中止されます。
(: Procedure to compute the level of an employee in the org tree :)
declare xqse function tns:distanceFromTop($id as xs:string?)
as xs:integer?{
declare $mgrCnt as xs:integer := 0;
declare $curEmp as element(empl:Employee)?:=
tns:getByEmployeeID($id);
declare $mgrId as xs:string?:= fn:data($curEmp/ManagerID);
if (fn:empty($curEmp)) then return value ();
while (fn:not(fn:empty($mgrId))) {
set $mgrCnt := $mgrCnt + 1;
set $curEmp := tns:getByEmployeeID($mgrId);
set $mgrId := fn:data($curEmp/ManagerID);
};
return value ($mgrCnt);
};
XQSE return
文は、ValueStatement
によって表示された式を計算し、現在のプロシージャから終了するときに結果の値を返します。
ReturnStatement ::= "return" "value" ValueStatement
return 文にあるブロックがメイン クエリの本体である場合、return 文は、呼び出し環境に値を返します。
(: Procedure to compute the level of an employee in the org tree :)
declare xqse function tns:distanceFromTop($id as xs:string?)
as xs:integer?{
declare $mgrCnt as xs:integer := 0;
declare $curEmp as element(empl:Employee)?:=
tns:getByEmployeeID($id);
declare $mgrId as xs:string?:= fn:data($curEmp/ManagerID);
if (fn:empty($curEmp)) then return value ();
while (fn:not(fn:empty($mgrId))) {
set $mgrCnt := $mgrCnt + 1;
set $curEmp := tns:getByEmployeeID($mgrId);
set $mgrId := fn:data($curEmp/ManagerID);
};
return value ($mgrCnt);
};
XQSE は、XQuery の for
句に相当する iterate
文を提供し、XQSE 文のブロックのデータ駆動型ループを行うことができます。これにより、XQuery 式の結果を通じて繰り返すことができます。たとえば、
IterateStatement ::= "iterate" "$" VarName PositionalVar?"over"
ValueStatement Block
反復変数 VarName
は、ValueStatement
を評価して作成されたシーケンス内の項目ごとにバインドされています。この ValueStatement は、XQuery 式かプロシージャの呼び出しです。必要に応じて、PositionalVar
変数は、シーケンス内の現在の項目の目次を表します。
注意 : | VarName 変数も PositionalVar 変数も可変変数ですが、この機能を利用することはお勧めしません。 |
(: Procedure to allow only updates that don't violate the salary change business rules :)
declare procedure tns:updateChecked($changedEmps as
changed-element(empl:Employee)*) {iterate $sourceEmp over $changedEmps {
tns:update($changedEmps);
if (tns:invalidSalaryChange($sourceEmp)) then
fn:error(xs:QName("INVALID_SALARY_CHANGE"), ":
Salary change exceeds the limit.");
};
};
(: Procedure to perform "lite ETL", copying and transforming data from one source to another :)
declare procedure tns:copyAllToEMP2() as xs:integer {
declare $backupCnt as xs:integer := 0;
declare $emp2 as element(emp2:EMP2)?;iterate $emp1 over ens1:getAll() {
return value ($backupCnt);
set $emp2 := tns:transformToEMP2($emp1);
emp2:createEMP2($emp2);
set $backupCnt := $backupCnt + 1;
}
};
try
文を使用して、XQSE 内の手順のエラー処理を行うことができます。たとえば、XQuery の fn:error
関数によって発生したエラー処理です。try
文は、Java または C++ 言語などの従来の try/catch 文と同じように機能します。
TryStatement ::= "try" Block CatchClauseStatement+
CatchClauseStatement ::= catch "(" NameTest ("into" VarNameExpr (("," VarNameExpr)?"," VarNameExpr)?)?")" Block
XQSE では、XQuery エラーをすべて取得することができます。XQuery エラーに関連付けられる QName
があり、XQuery の NameTest
を使用して、特定の catch
句によって処理されるエラーを限定できます。また、catch
句の次の変数は、XQuery の fn:error
関数の引数と同じように機能します。
他の言語の例外と同じように、fn:error
関数を使用して XQSE にエラーを再スローすることができます。この場合、名前、説明およびエラーのオブジェクトなどエラーのすべてのコンポーネントが新しい fn:error
呼び出しに適切に渡されたかどうかを確認する必要があります。
(: Procedure to create a replicated employee and return an appropriately specific error message if it fails :)
declare procedure tns:create($newEmps as element(empl:Employee)*)
as element(empl:ReplicatedEmployee_KEY)* {
iterate $newEmp over $newEmps {
declare $newEmp2 as element(emp2:EMP2)?:=
bns:transformToEMP2($newEmp);
try { tns:createEmployee($newEmp); }
catch (* into $err, $msg) {
fn:error(xs:QName("PRIMARY_CREATE_FAILURE"),
fn:concat("Create failed on primary copy due to: ", $err,
$msg));
};
try { emp2:createEMP2($newEmp2); }
catch (* into $err, $msg) {
fn:error(xs:QName("SECONDARY_CREATE_FAILURE"),
fn:concat("Create failed on backup copy due to: ", $err,
$msg));
};
}
};
XQSE は、XQuery IfExpr
式に相当する if
文を提供します。以下に示すように、XQSE if
文は、XQuery IfExpr
式と異なります。
IfStatement ::= "if" "(" Expr ")" "then" Statement ("else" Statement)?
(: Procedure to compute the level of an employee in the org tree :)
declare xqse function tns:distanceFromTop($id as xs:string?)
as xs:integer?{
declare $mgrCnt as xs:integer := 0;
declare $curEmp as element(empl:Employee)?:=
tns:getByEmployeeID($id);
declare $mgrId as xs:string?:= fn:data($curEmp/ManagerID);
if (fn:empty($curEmp)) then return value ();
while (fn:not(fn:empty($mgrId))) {
set $mgrCnt := $mgrCnt + 1;
set $curEmp := tns:getByEmployeeID($mgrId);
set $mgrId := fn:data($curEmp/ManagerID);
};
return value ($mgrCnt);
};
XQSE 言語は、ALDSP に更新し、再送信された要素についての情報で XQuery データ モデルを拡張します。これにより、XQSE では、SDO クラエント更新および関連付けられるサーバ側の更新ロジックをサポートできます。
変更を持つ XML ノードは、XQSE 型が changed-element
です。次に、XQSE changed-element
型の文法を示します。
ItemType := AtomicType | KindTestType | <"item" "(" ")"> |
ChangedElementType
ChangedElementType := "changed-element" "(" ElementNameOrWildcard ")"
XQSE では、 fn-bea:old-value
および fn-bea:current-value
の 2 つの組み込み関数があり、変更された XML ノードの前更新コンテンツおよび更新後コンテンツにそれぞれアクセスします。
注意 : | XQSE には、プロシージャおよび関数の引数として、changed-element のインスタンスのみ渡すことができます。changed-element のインスタンスは、XQSE で作成または段階的に変更できません。 |
changed-element
インスタンスを使用して、変数を宣言し、割り当てることもできます。
(: function to determine whether or not a given salary change is legal according to business rules :)
declare function tns:invalidSalaryChange($emp as
changed-element(empl:Employee)) as xs:boolean {
let $newSalary := fn:data(fn-bea:current-value($emp)/Salary)
let $oldSalary := fn:data(fn-bea:old-value($emp)/Salary)
return (100.0 * fn:abs($newSalary - $oldSalary) div $oldSalary)
gt 10.0
};
Prolog ::= ((DefaultNamespaceDecl | Setter | NamespaceDecl | Import) Separator)*
((VarDecl | FunctionDecl | ProcedureDecl | XQSEFunctionDecl| OptionDecl) Separator)*
XQSEFunctionDecl ::= "declare" xqse "function" QName "(" ParamList?")" ("as" SequenceType)?(Block | ("external") )
ProcedureDecl ::= "declare" "procedure" QName "(" ParamList?")" ("as" SequenceType)?(Block | ("external") )
QueryBody ::= Expr | Block
Statement := SimpleStatement | BlockStatement
SimpleStatement ::= SetStatement | IfStatement | ReturnStatement | ProcedureCall
BlockStatement := WhileStatement | IterateStatement | TryStatement | Block
ValueStatement := ExprSingle | ProcedureCall
ReturnStatement ::= "return" "value" ValueStatement
Block ::= {" ((BlockDecl ";")* StatementSequence "}"
StatementSequence := ((SimpleStatement ";") | (BlockStatement (";")?))*
BlockDecl ::= "declare" "$" VarName TypeDeclaration?(":=" ValueStatement)?
SetStatement ::= "set" "$" VarName ":=" ValueStatement
WhileStatement ::= "while" "(" Expr ")" Block
IterateStatement ::= "iterate" "$" VarName PositionalVar?"over" ValueStatement Block
ProcedureCall ::= FunctionCall
TryStatement ::= "try" Block CatchClauseStatement+
CatchClauseStatement ::= catch "(" NameTest ("into" VarNameExpr (("," VarNameExpr)?"," VarNameExpr)?)?")" Block
IfStatement ::= "if" "(" Expr ")" "then" Statement ("else" Statement)?
ItemType := AtomicType | KindTestType | <"item" "(" ")"> | ChangedElementType
ChangedElementType := "changed-element" "(" ElementNameOrWildcard ")"
![]() ![]() ![]() |