APEXアプリケーションを開発していると、フロントエンドに実装するJavaScriptやCSS、またはバックエンドで実行するSQLやPL/SQLを少し変更して、アプリケーションを実行したいということがあります。
APEXアプリケーションにコードを埋め込んでいる場合は、アプリケーションのコピーを作成してコードを改変することで対応できます。ただし、アプリケーションにコードを埋め込んでいると、単体テストはほぼできません。また、そのままでもCI/CDに乗せるのは大変なのに、さらに大変になります。
海外のパートナーさんでは、レポートのソースとして必ずビューを作りSQLの埋め込みは禁止する、PL/SQLは必ずパッケージを作りコードの埋め込みは禁止する、といった開発標準を採用しているところもあります。
以下よりGitHub、VS Code、APEXのセッション・オーバーライド、Oracle Databaseのエディションの機能を使って、バージョンの異なるコードをAPEXアプリケーションで実行してみます。
初期状態のmainブランチ
以下のアプリケーションを作成します。APEXアプリケーションの作成には、Always FreeのAutonomous Databaseを使用します。
上記のアプリケーションのエクスポートは以下にあります。
https://github.com/ujnak/apexapps/blob/master/exports/edition_app.zip
アプリケーションを作ること自体が目的ではないので、作り方は省略します。
ボタンSet Textをクリックすると、テキスト・フィールドTextにThis is MAINという文字列を挿入します。
これは静的アプリケーション・ファイルに作成したアクションset-my-textを呼び出しています。
この静的アプリケーション・ファイルは、ページ・プロパティのJavaScriptのファイルURLに設定しています。
ボタンSET_TEXTの詳細のカスタム属性として以下を記述し、アクションset-my-textを呼び出します。
data-action="#action$set-my-text?target=P1_TEXT"
動的アクションとしても実装できますが、コード埋め込みを避けています。
ボタン
Set Editionをクリックすると、テキスト・フィールド
Editionに
MAINという文字列を挿入します。
これはパッケージTEST_PACKAGEに実装したファンクションSHOW_EDITIONを実行しています。パッケージのソースは以下になります。
このファンクションは、ボタンSET_EDITIONを押したときに実行されるプロセスに設定します。プロセス・タイプはAPIの呼出しです。
パラメータの
ファンクションの結果をページ・アイテム
P1_EDITIONに設定します。
この場合も、コードの埋め込みは避けています。
クラシック・レポートのソースとしてビューEMP_Vを使用します。ビューの定義は以下になります。サンプル・データセットのEMP/DEPTに含まれる表EMPを、そのままビューにしています。
ここでも、直接SELECT文を記述しないようにしています。
ビュー定義にeditonableと付けていますが(デフォルトでeditonableなので付けなくても良い)、エディショニング・ビュー(これにはeditioningと付ける)ではありません。異なるオブジェクト・タイプなので注意が必要です。
以上の3つの実装、フロント・エンドのJavaScript、バックエンドのパッケージTEST_PACKAGE、ビューEMP_Vをブランチmainの実装を維持しつつ、新しくブランチを作って改変してみます。
VS Codeの設定
開発にはVS Codeを使用します。以下の拡張機能をインストールします。
Oracle Developer Tools for VS Code (SQL and PLSQL)
Minify
Live Server
GitHub Repositories
および上記に依存関係のある拡張機能をインストールします。
普段VS Codeを使っていないので、ほとんどの時間はVS Codeの使い方を調べること費やしてしまいました。SQL DeveloperのPMのJeff Smithさんが昨年アナウンスしていた、
VS CodeベースのSQL Developerの次世代版が早くリリースされることを祈るばかりです。
VS Codeを使おうと思ったのは、そうすればGitHub Copilotといった生成AIの恩恵に預かるチャンスがあると思ったためです。特にSQLのSELECT文は、生成AIと相性が良いように思います。PL/SQLはマイナーな言語なので恩恵は少なそうですが、DB 23cからはサーバー側もJavaScriptでコーディングできます。
接続先をAutonomous Databaseとした場合、インスタンス・ウォレットのファイルをダウンロードします。ダウンロードしたウォレットはWallet_インスタンス名.zipとなります。このZIPファイルを展開すると、以下のようにTNSエイリアスが定義されているtnsnames.ora、および自動ログイン・ウォレット・ファイルcwallet.ssoが含まれています。
APEXDEV2 % ls -l
total 96
-rw-r--r--@ 1 ********** staff 3029 5 16 06:23 README
-rw-r--r--@ 1 ********** staff 6701 5 16 06:23 cwallet.sso
-rw-r--r--@ 1 ********** staff 6656 5 16 06:23 ewallet.p12
-rw-r--r--@ 1 ********** staff 7475 5 16 06:23 ewallet.pem
-rw-r--r--@ 1 ********** staff 3190 5 16 06:23 keystore.jks
-rw-r--r--@ 1 ********** staff 691 5 16 06:23 ojdbc.properties
-rw-r--r--@ 1 ********** staff 114 5 16 06:23 sqlnet.ora
-rw-r--r--@ 1 ********** staff 1290 5 16 06:23 tnsnames.ora
-rw-r--r--@ 1 ********** staff 3378 5 16 06:23 truststore.jks
APEXDEV2 %
TNS Admin LocationおよびWallet File Locationに、Wallet_インスタンス名.zipを展開したディレクトリを指定します。また、Use Wallet File(ここでいうWallet Fileはcwallet.ssoのこと)にチェックを入れます。
接続に使用するユーザー名(User name)およびパスワード(Password)は、APEXのワークスペース・スキーマのものを指定します。
APEXのワークスペースを作成した時点では、ワークスペース・スキーマは外部から接続できるように構成されていません。
ワークスペース・スキーマWKSP_APEXDEVにパスワードを設定し、RESTfulサービスを有効化する必要があります。
alter user wksp_apexdev identified by <パスワード>;
begin
ords_admin.enable_schema(
p_schema => 'WKSP_APEXDEV'
);
end;
/SQL> alter user wksp_apexdev identified by <パスワード>;
User WKSP_APEXDEVが変更されました。
SQL> begin
2 ords_admin.enable_schema(
3 p_schema => 'WKSP_APEXDEV'
4 );
5 end;
6* /
PL/SQLプロシージャが正常に完了しました。
SQL>
以上の設定で、Oracle Developer Tools for VS Codeの拡張機能を使って、Autonomous Databaseに接続できるようになります。
ブランチed_hanakoとエディションED_HANAKOの準備
ブランチとしてed_hanakoを作成し、ブランチmainのアプリケーションを修正します。
ブランチの名前を
ed_hanakoとします。
これ以降のGitHubへのコミットが、ブランチ
ed_hanakoに対して行われるようになりました。
APEXアプリケーションをホストしているAutonomous Databaseに、管理者ユーザーADMINで接続します。APEXのワークスペースのスキーマがWKSP_APEXDEVと仮定します。
以下のコマンドを実行し、スキーマをエディションを有効にします。
alter user wksp_apexdev enable editions;
SQL> alter user wksp_apexdev enable editions;
User WKSP_APEXDEVが変更されました。
SQL>
エディションED_HANAKOを作成し、スキーマWKSP_APEXDEVで利用可能にします。
create edition ed_hanako as child of ora$base;
grant use on edition ed_hanako to wksp_apexdev;
SQL> create edition ed_hanako as child of ora$base;
Edition ED_HANAKOは作成されました。
SQL> grant use on edition ed_hanako to wksp_apexdev;
Grantが正常に実行されました。
SQL>
Oracle Developer Tools for VS CodeでのAutonomous Databaseの接続情報に、ログイン・スクリプト(Login Script)を設定します。
ログイン・スクリプトとして指定するファイルには、以下の行を含めます。データベース接続時にエディションをED_HAHAKOに設定します。
alter session set edition = ED_HANAKO;
作成されているAPEXアプリケーションのコピーを作成し、エディションED_HANAKOで動作するように設定します。
アプリケーションのコピーは
タスクから実行します。
花子さん用のアプリケーションということで、新規アプリケーション名をEdition App (hanako)とします。
次へ進みます。
アプリケーションのコピーを実行します。
アプリケーションが作成されたら、
アプリケーション定義の
セキュリティの
データベース・セッションを開きます。
初期化PL/SQLコードとして以下を記述します。APEXのページ・プロセスは常に、エディションED_HANAKOで動作するようになります。
begin
apex_util.set_edition('ED_HANAKO');
end;
フロントエンドのJavaScriptの修正
APEXアプリケーションには5つの静的アプリケーション・ファイルが含まれており、ボタンSet Textで実行されるアクションはactions.jsに記述されています。
GitHubリポジトリのローカル・コピーでは、ディレクトリfrontend以下にactions.jsを作成しています。
EditionApp % ls -lR frontend
total 16
-rw-rw-rw-@ 1 ********** staff 415 5 17 00:33 actions.js
-rw-rw-rw-@ 1 ********** staff 179 5 17 10:29 actions.min.js
drwxr-xr-x@ 5 ********** staff 160 5 17 09:34 icons
frontend/icons:
total 40
-rw-rw-rw-@ 1 ********** staff 2076 5 17 00:33 app-icon-192.png
-rw-rw-rw-@ 1 ********** staff 347 5 17 00:33 app-icon-32.png
-rw-rw-rw-@ 1 ********** staff 10219 5 17 00:33 app-icon-512.png
EditionApp %
Live Serverを起動するために、ディレクトリfrontend以下にファイルindex.htmlを作成します。以下の内容を記述します。
<html><body>Hanako</body></html>
作成したindex.htmlを対象にして、Live Serverを起動します。
HTTPサーバーがローカルホストの5500番ポートに起動し、index.htmlおよびactions.js、actions.min.jsといったファイルへアクセスできるようになります。
http://127.0.0.1:5500/frontend/index.html
コピーしたアプリケーションにアクセスします。
Current Edition NameがED_HANAKOであることを確認します。続いて開発者ツール・バーのセッションより、セッション・オーバーライドを実行します。
セッション・オーバーライドの有効化をオンに変更します。
ファイル・パスのアプリケーション・ファイル#APP_FILES#をオンに変更し、Live ServerのURLであるhttp://127.0.0.1/frontend/を設定します。
以上の変更を行い、保存します。
VS Codeでactions.jsを変更します。This is MAINをThis is HANAKOに書き換え、保存します。
Command+Shift+P(macOSの場合)を入力しコマンド・パレットを表示し、Minifyを実行します。actions.jsからactions.min.jsを生成します。APEXアプリケーションではデバッグ・モードでない場合はミニファイされたファイルにアクセスします。
ミニファイされたファイル
actions.min.jsも変更されたことを確認します。
フラグが
Mに変わります。
アプリケーションをリロードし、ボタンSet Textをクリックします。
テキスト・フィールドTextにThis is HANAKOと表示されることを確認します。
この変更は、オリジナルのアプリケーション(ブランチmain、エディションORA$BASE)には影響を与えていません。
拡張機能の
ソース管理を開き、
actions.jsと
actions.min.jsをステージします。
+サインをクリックします。
メッセージを入力して、変更をコミットします。
JavaScriptのコードの修正についての説明は、ここまでにします。
バックエンドのPL/SQLパッケージの修正
接続済みのOracle Databaseを選択し、Open New SQL Fileを呼び出します。
以下のSELECT文を実行し、設定されているエディションを確認します。
SELECT SYS_CONTEXT('USERENV', 'CURRENT_EDITION_NAME') FROM DUAL;
結果がED_HANAKOであることを確認します。
ディレクトリbackend以下のtest_package.sql(本当は拡張子は.plbにしたかったのだけど、どうしてもVSCodeで上手く扱えなかったので.sqlにしています)を開きます。
ファンクションSHOW_EDITIONの戻り値をMAINからHANAKOに変更し、ファイルを保存します。
接続済みデータベースを選択し、Open Existing SQL Fileを呼び出します。
パッケージ定義test_package.plsを開き、スクリプトを実行します。
実行結果にエラーがないことを確認します。
同様にパッケージ本体test_package.sqlを開き、スクリプトの実行を行います。
なぜかNot connectedというエラーが発生することがあります。再度実行すると成功するので、原因はよくわかりません。
以下のコードを実行し、コードの変更を確認します。
begin
dbms_output.put_line(test_package.show_edition);
end;
結果が
HANAKOであれば、エディションED_HANAKOでのパッケージの変更は完了です。
エディションHANAKOで動作するアプリケーションを使って、パッケージの変更を確認します。
ボタンSet Editionをクリックすると、EditionにHANAKOと表示されます。
オリジナルのアプリケーションは影響を受けません。
パッケージの修正は以上になります。
バックエンドのビューEMP_Vの修正
パッケージの修正と同じ作業になります。
Open Existing SQL Fileを呼び出しemp_v.sqlを開きます。
ビュー定義に条件句を追加し保存します。その後、スクリプトを実行します。
create or replace editionable view emp_v
as
select * from emp
where deptno = 10;
エディションHANAKOで動作するアプリケーションのページをリロードします。レポートの表示が変わっていることを確認します。
ビューの修正は以上になります。
複数のブランチ/エディションを使う
例えば太郎さんのためにブランチed_taroを作成します。これはブランチmainから派生させます。GitHub側は特別な操作ではありませんが、データベースのエディションの扱いは難しい部分があります。
Oracle Databaseのエディションは、親となるエディションに作成できる子のエディションは1つという制約があります。また、エディションの削除はリーフとなるエディション、つまり最後のエディションしか削除できない、という制限があります。
今までの作業を継続してエディションED_TAROを作成するために、GitHubのブランチmainからed_taroを作成したように、ベース・エディションであるORA$BASEから作成しようとすると、以下のようなエラーが発生します。
SQL> create edition ed_taro as child of ora$base;
次のコマンドの開始中にエラーが発生しました : 行 1 -
create edition ed_taro as child of ora$base
エラー・レポート -
ORA-38807: 実装制限: エディションが持てる子は1つのみです
38807. 00000 - "Implementation restriction: an edition can have only one child"
*Cause: This error occurred because an attempt was made to create more
than one child edition for a parent edition.
*Action: Create the edition as a child of the leaf edition.
SQL>
そのため、エディションED_TAROはED_HANAKOの子エディションとして作成する必要があります。
create edition ed_taro as child of ed_hanako;
SQL> create edition ed_taro as child of ed_hanako;
Edition ED_TAROは作成されました。
SQL>
ワークスペース・スキーマにエディション
ED_TAROを利用する権限を与えます。
grant use on edition ed_taro to wksp_apexdev;そのため、親のエディション(ベース・エディションであるORA$BASE以外)でオブジェクト定義が変更されているものは、対応するSQLファイルを実行してベース・エディションで定義されているオブジェクトを作成する必要があります。
リーフ・エディションのED_TAROを削除し、続いてED_HANAKOを削除します。そしてED_TAROを再度作成します。エディションED_TAROを作成したのち、ブランチed_taroにあるSQLファイルをすべて実行し直し、そのブランチのパッケージやビューを作り直します。
ビューやパッケージの新規作成、ビューの列数の変更、パッケージのサブプログラムの追加、引数の変更などは、ベースであるエディションORA$BASEで実施する必要があります。
スキーマに対してエディションを有効にすると、元に戻すことができません。また、11gから実装されている機能とはいえ、事例が豊富ということは無いようです。環境として、テスト、ステージ、本番があるときに、エディションを活用するのはテスト環境に限定した方が良いように思います。