2023年5月18日木曜日

GitHub、VS Code、APEXのセッション・オーバーライド、DBのエディションを使った開発

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をクリックすると、テキスト・フィールドTextThis is MAINという文字列を挿入します。

これは静的アプリケーション・ファイルに作成したアクションset-my-textを呼び出しています。


この静的アプリケーション・ファイルは、ページ・プロパティJavaScriptファイルURLに設定しています。


ボタンSET_TEXT詳細カスタム属性として以下を記述し、アクションset-my-textを呼び出します。

data-action="#action$set-my-text?target=P1_TEXT"

動的アクションとしても実装できますが、コード埋め込みを避けています。


ボタンSet Editionをクリックすると、テキスト・フィールドEditionMAINという文字列を挿入します。

これはパッケージ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.jsactions.min.jsといったファイルへアクセスできるようになります。

http://127.0.0.1:5500/frontend/index.html

コピーしたアプリケーションにアクセスします。

Current Edition NameED_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をクリックします。

テキスト・フィールドTextThis is HANAKOと表示されることを確認します。


この変更は、オリジナルのアプリケーション(ブランチmain、エディションORA$BASE)には影響を与えていません。


拡張機能のソース管理を開き、actions.jsactions.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をクリックすると、EditionHANAKOと表示されます。


オリジナルのアプリケーションは影響を受けません。

パッケージの修正は以上になります。


バックエンドのビュー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_TAROED_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;

SQL> grant use on edition ed_taro to wksp_apexdev;


Grantが正常に実行されました。


SQL> 


エディションED_TAROで接続してビューEMP_Vを検索すると、親のエディションであるED_HANAKOでの定義が使用され、deptno = 10の条件が適用されます。


ブランチed_taroはmainから派生しているため、ファイルemp_v.sqlにはdeptno = 10の条件は付いていません。

そのため、親のエディション(ベース・エディションであるORA$BASE以外)でオブジェクト定義が変更されているものは、対応するSQLファイルを実行してベース・エディションで定義されているオブジェクトを作成する必要があります。

実際にはどのオブジェクトが変更されているか見つけにくいので、DDLであるSQLファイルはすべて実行することになると予想されます。

また、エディションED_HANAKO(ブランチとしてはed_hanako)を削除するには、以下のような手順になります。

リーフ・エディションのED_TAROを削除し、続いてED_HANAKOを削除します。そしてED_TAROを再度作成します。エディションED_TAROを作成したのち、ブランチed_taroにあるSQLファイルをすべて実行し直し、そのブランチのパッケージやビューを作り直します。

SQL> drop edition ed_taro cascade;


Edition ED_TAROが削除されました。


SQL> drop edition ed_hanako cascade;


Edition ED_HANAKOが削除されました。


SQL> create edition ed_taro as child of ora$base;


Edition ED_TAROは作成されました。


SQL> grant use on edition ed_taro to wksp_apexdev;


Grantが正常に実行されました。


SQL> 



ページ・デザイナはエディション非対応



ページ・デザイナを含むOracle APEXの開発ツールはエディションに対応していないため、つねにORA$BASEで動作します。

ビューやパッケージの新規作成、ビューの列数の変更、パッケージのサブプログラムの追加、引数の変更などは、ベースであるエディションORA$BASEで実施する必要があります。


その他



スキーマに対してエディションを有効にすると、元に戻すことができません。また、11gから実装されている機能とはいえ、事例が豊富ということは無いようです。環境として、テスト、ステージ、本番があるときに、エディションを活用するのはテスト環境に限定した方が良いように思います。