2025年1月17日金曜日

Oracle APEX 24.2のサポート期間をリリース・ノートから確認する

2025年1月にOracle APEX 24.2がリリースされました。現時点(2025年1月)での、Oracle APEXのサポート期間を確認してみました。すべて公開されている情報を元にしていて情報源も記載していますが、公式に確認するには、オラクルのサポート窓口に問い合わせる必要があります。

Oracle APEXのサポート期間


2025年1月時点でサポートされているOracle APEXのリリースは、以下のリンク先のページに記載されています。製品リリース後の18ヶ月がサポート期間になっています。

APEX Collateral / Support

以前のリリースではサポート期間は長かったのですが、2024年12月の時点で、23.2以前のリリースはすべてサポート期間が終了しています。

23.2以前のリリースのサポート期間は、以下の資料より確認できます。

Oracle Lifetime Support Policiesのリソース

Oracle Technology Products (PDF)Oracle APEX (formerly Application Express) のセクションに記載されています。

Universal Themeのサポート


Oracle APEX 24.2のリリース・ノートの以下のセクションで、Oracle APEX 24.2がサポートしているUniversal Themeについて、以下のように記載されています。

1.1.6.1 About Upgrading to the Latest Version of Universal Theme

APEX supports the current and prior APEX version of Universal Theme for each release. For example, APEX 24.2 supports Universal Theme 24.2 and 24.1.


データベースにインストールしているOracle APEXを24.2にアップグレードすると、その上で動作する(つまり開発者が作成した)アプリケーションでは、Universal Themeのバージョン24.2もしくはその一つ前の24.1がサポートの対象となり、23.2を含む24.1以前のUniversal Themeのままのアプリケーションはサポート対象から外れるようです。

リリース・ノートでは、定期的にUniversal Themeのリフレッシュを行うように推奨しています。

Therefore, it is important to refresh your theme regularly so your applications are on the latest (or one version prior) release of Universal Theme and remain supported.


Webサーバーの要件


Oracle APEX 24.2のリリース・ノートの以下のセクションで、Oracle APEX 24.2が要件としているOracle REST Data Servicesについて記載されています。

1.1.3 Web Server Requirements

Oracle APEX requires Oracle REST Data Services (ORDS) 23.3 or later.


ただし、Oracle APEXと同様に、Oracle REST Data Servicesのサポート期間は製品リリース後の18ヶ月となっています。2025年1月の時点で、23.3以前のリリースはサポート期間が終了しています。

Oracle REST Data Services Support
https://www.oracle.com/tools/ords/support.html

Oracle APEXの要件に関わらず、Oracle REST Data Servicesのサポート期間を考慮して、Oracle REST Data Servicesのバージョンアップを実施する必要があります。


Oracle Databaseの要件



Oracle APEX 24.2のリリース・ノートの以下のセクションで、Oracle APEX 24.2が要件としているOracle Databaseについて記載されています。

1.1.1 Oracle Database Requirements

Oracle APEX release 24.2 requires an Oracle Database release 19c or later.

Oracle APEXのOracle Databaseの要件は、Premier SupportかExtended Supportの期間中であることなので、19c以降になっています。

Release Schedule of Current Database Releases (ドキュメントID 742060.1)
Oracle APEX、Oracle REST Data Servicesともに、Oracle Databaseのサポート期間とは独立してサポート期間が設定されています。


今回の記事は以上になります。

2025年1月16日木曜日

Theatre.jsのStudioで編集したアニメーションをOracle Databaseに保存する

Theatre.jsではStudio UIを使ってアニメーションを編集できます。編集したアニメーションはファイルに出力しますが、これをOracle Databaseに保存するように機能を追加します。また、データベースに保存したアニメーションをStudio UIにロードする機能も組み込んでみます。データベースに保存したアニメーションのデータを読み出して、プロダクションのアニメーションを表示します。

Theatre.jsのStudio UIはブラウザの全画面を使うアプリケーションです。これは、Oracle APEXの非モーダル・ダイアログのページに実装します。非モーダル・ダイアログのページは、Oracle APEXのナビゲーションが一切生成されないため、Studio UIと競合することがありません。しかし、追加のUIを載せることもできないため、アニメーションをデータベースに保存するUIは、別ウィンドウに実装します。Studio UIの操作は、BroadcastChannelでイベントを送信して実行します。BroadcastChannelを使って非モーダル・ダイアログで処理を行う実装については、以前の記事「非モーダル・ダイアログにあるレポートをダイアログを開いたウィンドウから制御する」で紹介しています。

作成したAPEXアプリケーションは以下のように動作します。Theatre.jsのプロジェクトは、Getting StartedのWith HTML/SVGを流用しています。

ボタンExportをクリックして、その時点でのStudio UIのアニメーションをOracle Databaseに保存しています。ボタンReloadをクリックして、プロダクションのアニメーションを表示するページをリロードして初期化し、その後にボタンImportをクリックして、Oracle Databaseに保存されたアニメーションを表示しています。


このAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-theatre-js.zip

アプリケーションが依存している表EBAJ_THEATREJS_PROJECTS(アニメーションを保存する表)およびパッケージEBAJ_ASP_THEATREJS_PKGを作成するDDLを、サポートするオブジェクトインストール・スクリプトに含めているため、上記のファイルをインポートすると、アプリケーションとしては動作すると思います。

以下より作成したAPEXアプリケーションを紹介します。あくまで全画面を使うJavaScriptアプリをOracle Databaseと連携させる方法のサンプルなので、Theatre.jsとしての実用性はありません。プロジェクトについても、Getting Started With HTML/SVGの記述をファイルに直書きし、APEXのページに読み込んでいます。

最初にデータベースのオブジェクトを作成します。

EBAJ_THEATREJS_PROJECTSを作成します。

パッケージ定義EBAJ_ASP_THEATREJS_PKGを作成します。

パッケージ本体を作成します。

パッケージに含まれているプロシージャは、Oracle APEXのAjaxコールバックとして呼び出します。

APEXのアプリケーションを作成します。名前Sample Theatre.jsとします。

ホーム・ページにプロダクションのアニメーションを表示するリージョンと、Theatre.jsのアニメーションをデータベースに保存するボタンEXPORTと、データベースからアニメーションを取り出すボタンIMPORTを作成します。また、ページをリロードするボタンRELOADも作成します。

JavaScriptのコードは別ファイルに記述します。ページ・プロパティJavaScriptファイルURLに以下を記述します。

[module]#APP_FILES#js/production-animation-tutorial#MIN#.js


ファイルproduction-animation-tutorial.jsの内容です。

ボタンEXPORTRELOADIMPORTの処理はAPEXアクションとして実装しています。データベースからのアニメーションの取り出しは、AjaxコールバックRESTORE_PROJECT_STATEの呼び出しによって行います。

インポートしているファイルproject.jsでは、Theatre.jsのプロジェクトを記述しています。このファイルはStudio UIを実装しているJavaScriptファイルにも読み込みます。

AjaxコールバックRESTORE_PROJECT_STATEでは、以下のコードを実行します。
ebaj_asp_theatrejs_pkg.ajax_restore_project_state(
    p_project_id => :P1_PROJECT_ID
);

プロダクションのアニメーションを表示する領域として、静的コンテンツのリージョンTheatre.jsを作成します。

ソースHTMLコードにアニメーションを適用するH1要素を記述します。

<h1 id="article-heading" style="text-align: center">Welcome</h1>

外観テンプレート・オプションにより、リージョンの高さ320pxにしています。ボタンを配置しているリージョンをリージョンの上に移動し、リージョンの高さをもう少し高くしても良いかもしれません。


ページ・アイテムP1_PROJECT_IDプロジェクトIDを設定します。ソースタイプにアイテムアイテムとしてG_PROJECT_IDを設定しています。つまり、このページ・アイテムの値はアプリケーション・アイテムG_PROJECT_IDに設定した値になります。

このようにしている理由は、ページ・アイテムの値はJavaScriptのコードから簡単に参照できるためです。Stuido UIのページでも同じプロジェクト名を参照しますが、ページが異なるためJavaScriptのコードからはP1_PROJECT_IDの値は参照できません。そのため、Studio UIを実装したページでは、ページ・アイテムとしてP2_PROJECT_IDを作成し、そのソースをアプリケーション・アイテムG_PROJECT_IDにしています。結果として、双方のページで、アプリケーション・アイテムG_PROJECT_IDの値をプロジェクト名として参照します。


共有コンポーネントアプリケーション・アイテムとしてG_PROJECT_IDを作成します。


アプリケーション・アイテムへの値の設定は、アプリケーションの計算により行います。

頻度計算ポイントとして新規インスタンス(新規セッション)開始時を選択し、計算タイプとして静的割り当て計算HTML Animation Tutorialを設定します。

Theatre.jsのプロジェクトがファイルproject.jsに直書きされていることや、アプリケーション・アイテムに設定しているプロジェクト名が決め打ちなので、このアプリケーション自体はTheatre.jsのGetting StartedのWith HTML/SVGで説明されている以上のことはできません。


ボタンをクリックして実行した処理のステータスを表示するページ・アイテムとしてP1_STATUSを作成します。


ボタンを配置する静的コンテンツのリージョンを作成します。外観テンプレートButtons Containerを選択します。APEXアクションのコンテキストを作成する際にJavaScriptからリージョンを選択できるよう、詳細静的IDとしてCONTROLSを設定します。


ボタンについては、それぞれAPEXアクションを呼び出すように、動作アクションとして動的アクションで定義を選択し、詳細カスタム属性としてdata-action="EXPORT"data-action="RELOAD"data-action="IMPORT"を設定します。


プロダクションのアニメーションの表示と、それをコントロールするページの実装は以上で完了です。

Theatre.jsのStudio UIは、外観ページ・モード非モーダル・ダイアログとしたページに実装します。

ページ・プロパティJavaScriptファイルURLとして以下を記述します。

[module]#APP_FILES#js/theatre-studio#MIN#.js


インポートするtheatre-studio.jsの内容です。


データベースからアニメーションを取り出すためにAjaxコールバックRESTORE_PROJECT_STATEを呼び出し、保存するためにSAVE_PROJECT_STATEを呼び出します。RESTORE_PROJECT_STATEは先のページに実装したものと同じ処理ですが、ページが異なるため、同じAjaxコールバックを作成する必要があります。

AjaxコールバックSAVE_PROJECT_STATEでは、以下のコードを実行します。
ebaj_asp_theatrejs_pkg.ajax_save_project_state(
    p_project_id => :P2_PROJECT_ID
    ,p_project_state => :P2_PROJECT_STATE
);

ページ・アイテムP2_PROJECT_IDは、先ほどのP1_PROJECT_IDと同様に、ソースアイテムとしてG_PROJECT_IDを設定します。


ページ・アイテムP2_PROJECT_STATEには、アニメーションの定義であるJSONドキュメントを設定します。セッション・ステートデータ型としてCLOBを選択します。また、この値はJavaScriptから設定するため、設定保護された値オフにします。


静的コンテンツのリージョンを作成し、ソースHTMLコードにアニメーションの対象とするH1要素を記述します。

<h1 id="article-heading" style="text-align: center">Welcome</h1>


以上でStudio UIの実装も完了です。

Studio UIはナビゲーション・メニューから開きます。ページ作成ウィザードナビゲーション・メニューの作成オンにしていると、自動的にダイアログを開くメニュー・エントリが作成されます。


アプリケーションの背景を黒にするため、テーマ・ローラーを開き、テーマとしてVita - Darkを選択しています。


作成したアプリケーションの紹介は以上になります。

ブラウザとディスクでアニメーションに差異がある場合の、Studio UIの画面です。


Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年1月15日水曜日

非モーダル・ダイアログにあるレポートをダイアログを開いたウィンドウから制御する

非モーダル・ダイアログのページに対話モード・レポートを作成し、そのページを開いたページよりレポートのフィルタの適用と解除をします。非モーダル・ダイアログを開いたページと、そのページから開かれたダイアログのページはWebブラウザのウィンドウとしては異なっているため、ウィンドウ間のイベントのやり取りにBroadcastChannelを使用します。

対話モード・レポートのデータ・ソースとしてサンプル・データセットEMP/DEPTに含まれる表EMPを使用します。作成したアプリケーションは以下のように動作します。

ボタンOpenのクリックで対話モード・レポートを、非モーダル・ダイアログのページとして開きます。ボタンSeachのクリックで対話モード・レポートへのフィルタの適用、ボタンClearのクリックでフィルタを解除します。


以下より、アプリケーションの実装を簡単に紹介します。

ボタンOPENSEARCHCLEARはホーム・ページに作成しています。ページ・プロパティJavaScriptファイルURLに、これらのボタンの動作となるAPEXアクションを記述したファイルを設定します。

[module]#APP_FILES#js/main#MIN#.js


main.jsの内容です。

ボタンOPENSEARCHCLEARがクリックされたときに実行されるAPEXアクションを定義しています。それぞれのアクションは最初にAjaxコールバックとして実装されているデータベースの処理を呼び出し、その後にSEARCHおよびCLEARでは、レポートをリフレッシュするイベントをBroadcastChannelに渡しています。ボタンOPENは、データベースが生成したURLをターゲットとして、ウィンドウを開いています。


ボタンSEARCHをクリックしたときに実行されるAjaxコールバックADD_FILTERには、以下のPL/SQLコードを記述して、apex_ir.add_filterを呼び出します。
declare
    C_REPORT_COLUMN constant varchar2(5) := 'ENAME';
    C_PAGE_ID       constant number := 2;
    C_REGION_ID     constant number := :G_REPORT_REGION_ID;
begin
    apex_ir.add_filter(
        p_page_id        => C_PAGE_ID
        ,p_region_id     => C_REGION_ID
        ,p_report_column => C_REPORT_COLUMN
        ,p_filter_value  => :P1_ENAME
        ,p_operator_abbr => 'EQ'
        ,p_report_id     => null
    );
    /*
     * フィルタを適用したレポートの静的IDを属性reportとして返す。
     */
    htp.p('{ "success": true, "report": "EMP" }');
end;

ボタンCLEARをクリックしたときに実行されるAjaxコールバックCLEAR_REPORTには、以下のPL/SQLコードを記述して、apex_ir.clear_reportを呼び出します。
declare
    C_REPORT_COLUMN constant varchar2(5) := 'ENAME';
    C_PAGE_ID       constant number := 2;
    C_REGION_ID     constant number := :G_REPORT_REGION_ID;
begin
    apex_ir.clear_report(
        p_page_id        => C_PAGE_ID
        ,p_region_id     => C_REGION_ID
        ,p_report_id     => null
    );
    /*
     * リセットしたレポートの静的IDを属性reportとして返す。
     */
    htp.p('{ "success": true, "report": "EMP" }');
end;

ボタンOPENをクリックしたときに実行されるAjaxコールバックGET_URLには、以下のPL/SQLコードを記述して、apex_page.get_urlを呼び出します。
declare
    l_url varchar2(400);
    l_response clob;
begin
    l_url := apex_page.get_url(
        p_page => 2
    );
    l_response := apex_string.format('{ "url": "%s" }', l_url );
    htp.p(l_response);
end;

ボタンを含む静的コンテンツのリージョンに、APEXアクションのコンテキストを作成しています。リージョンを特定するために静的IDとしてCONTROLSを設定しています。


APEXアクションを呼び出すために、ボタンのカスタム属性としてdata-action=を設定しています。ボタンOPENにはdata-action="OPEN"SEARCHにはdata-action="SEARCH"CLEARにはdata-action="CLEAR"を設定しています。すべてのボタンの動作アクション動的アクションで定義に設定します。


フィルタを適用する対話モード・レポートのレポートIDを、セッションの開始時にアプリケーション・アイテムG_REPORT_REGION_IDに保存します。レポートIDはアプリケーションの実行時に変わることはないため、あらかじめ取得しておくことで、フィルタを設定するたびにレポートIDを取得しないようにします。


アプリケーション・アイテムG_REPORT_REGION_IDアプリケーションの計算を作成します。計算タイプSQL問合せ(単一の値を返す)頻度計算ポイント新規インスタンス(新規セッション)開始時を選択します。
select region_id from apex_application_page_regions
where application_id = :APP_ID and page_id = 2 and static_id = 'EMP'

対話モード・レポートはページ番号に作成しています。

外観ページ・モードとして非モーダル・ダイアログを選択し、JavaScriptファイルURLとして以下を設定しています。

[module]#APP_FILES#js/report#MIN#.js


対話モード・レポートのソース表名としてEMPを設定します。また、フィルタの設定に使用するリージョンIDを取得するため、およびリフレッシュ対象のリージョンを特定するために、静的IDEMPを設定します。


ページに読み込むreport.jsのコードは以下です。BroadcastChannelよりリフレッシュの実行を指示するイベントを受け付けて、対話モード・レポートのリフレッシュを実行します。


今回作成したAPEXアプリケーションの紹介は以上になります。

Oracle APEXのページで外観ページ・モードがダイアログ、つまりモーダル・ダイアログまたは非モーダル・ダイアログである場合、JavaScriptリファレンスに記載のないファンクションapex.theme42.dialogを呼び出してページを開いています。そのため、ダイアログとして開かれたウィンドウのハンドルは、簡単には取得できません。ページ・モードがタイアログではなく標準ページであれば、ページをオープンする時にウィンドウ・ハンドルを取得できます。

ウィンドウを特定してイベントのやり取りを行う場合は、ページ・モードをダイアログにしない方が良いでしょう。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-exchange-event.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年1月14日火曜日

OpenAIのRealtime APIをWebRTCで呼び出す

OpenAIのRealtime APIを、Oracle APEXのアプリケーションからWebRTCを使って呼び出してみます。参照するOpenAIのドキュメントは以下です。

Realtime API with WebRTC

WebRTCによるOpenAI Realtime APIの呼び出しは、すべてWebブラウザで動作するJavaScriptによって実装します。上記のOpenAIのドキュメントに記載されているコードを、ほぼそのまま流用します。WebRTCでRealtime APIを呼び出すには、OpenAIのサーバーよりephemeral token(一時トークン)を取得し、それをAPIキーとして使用します。この一時トークンの取得については、Oracle APEXが動作するデータベース・サーバーで実行します。

作成したAPEXアプリケーションは以下のように動作します。GIF動画なので音声が含まれていないため動作が分かりませんが、ボタンStartを押してConnection Stateopenになってから「OpenAIの創業者について教えてください。」と聞いています。Realtime APIのレスポンスに音声のトランスクリプトが含まれるため、それを取り出してページ・アイテムに設定しています。同時にWebブラウザでは、トランスクリプトの読み上げが行われています。Realtime APIから返された次の文章が読み上げられています。

OpenAIは、2015年に設立されました。創業者の一人には、イーロン・マスクやサム・アルトマンなどがいます。彼らは、人工知能の安全な発展と普及を目指して活動を始めました。設立当初は、営利目的ではなく、オープンソースでのAI研究を重視していました。


以下より実装について紹介します。

OpenAIのAPIサーバーより一時キーを取得するに当たって、OpenAIのAPIを呼び出します。このAPI呼び出しの認証には、OpenAIの(一時キーではない)APIキーを指定します。このAPIキーをOracle APEXのWeb資格証明として登録します。

Web資格証明静的IDOPENAI_API_KEYとしています。認証タイプHTTPヘッダー資格証明名はHTTPヘッダー名であるAuthenticationを設定します。資格証明シークレットBearerで始めて空白で区切り、skで始まるOpenAIのAPIキーを設定します。URLに対して有効については、https://api.openai.com/を設定します。


空のAPEXアプリケーションを作成し、デフォルトで作成されるホーム・ページにWebRTCによるOpenAI Realtime APIの呼び出しを実装します。

APEXアプリケーションの名前OpenAI WebRTCとします。

WebRTCの呼び出しを含んだJavaScriptのコードは、すべて静的アプリケーション・ファイルjs/app.jsに記述します。ページ・プロパティJavaScriptファイルURLとして、以下を設定します。

[module,defer]#APP_FILES#js/app#MIN#.js


一時キーの取得は、Ajaxコールバックとして実装します。プロセスの名前GET_EPHEMERAL_TOKENとして、ソースPL/SQLコードに以下を記述します。


一時キーはレスポンスであるJSONドキュメントのclient_secret.valueとして返されます。


ホーム・ページにはAjaxコールバックGET_EPHEMERAL_TOKENの呼び出しとレスポンスを確認するため、ボタンGET_EPHEMERAL_TOKENと、そのボタンをクリックしたときに実行される動的アクションを定義しています。このボタンはあくまでデバッグ用で、WebRTCでのRealtime API呼び出しには使用されません。


WebRTCによる会話を開始するボタンSTARTと、終了するボタンSTOPを作成します。最初に静的コンテンツのリージョンを作成し、静的IDとしてCONTROLSを割り当てます。外観テンプレートとして、Buttons Containerを選択します。


作成した静的コンテンツのリージョンにボタンSTARTを作成します。動作アクションとして動的アクションで定義を選択し、カスタム属性としてdata-action="START"を設定します。この設定により、ボタンをクリックするとAPEXアクションとして定義された"START"が呼び出されます。


同様にボタンSTOPを作成します。詳細カスタム属性としてdata-action="STOP"を設定します。ボタンSTARTの右隣に配置するため、レイアウト新規行の開始オフにします。


静的アプリケーション・ファイルjs/app.jsの内容は以下になります。ボタンSTARTをクリックするとファンクションinitが呼び出されます。

OpenAIのドキュメントにあるサンプルに対して、以下のコードを追加しています。
  • 一時キーを取得するために、apex.server.processを使ってAjaxコールバックGET_EPHEMERAL_TOKENを呼び出しています。
  • データ・チャネルの状態をページ・アイテムP1_CONNECTION_STATEに表示しています。
  • OpenAI Realtime APIの音声応答のトランスクリプトを取り出して、ページ・アイテムP1_RESPONSEに表示しています。
  • WebRTCによる会話の開始と終了を、APEXアクションのSTARTとSTOPとして定義しています。


以上でアプリケーションは完成です。

Realtime APIの呼び出しは、デバッグしているとクレジットがどんどん減るため、あまりデバッグできていません。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-openai-webrtc.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年1月10日金曜日

PL/SQLのコードをOracle SQL Developer Extension for VSCodeで開発する

オラクルはVSCodeの拡張機能として、2024年1月にOracle SQL Developer Extension for VSCodeをリリースしています。VSCode向けの、生成AIによるコード補完やコード生成を行う拡張機能が一般に利用可能になったこともあり、それらの機能を活用するためにPL/SQLのコードをVSCodeで開発する方法を確認してみます。

確認作業はOracleの開発ツールのプロダクト・マネージャのJeff Smithさんによる、以下のブログ記事に沿って行います。

PL/SQL debugger now available in SQL Developer for VS Code
https://www.thatjeffsmith.com/archive/2024/10/pl-sql-debugger-now-available-in-sql-developer-for-vs-code/

PLSQLを実行するデータベースとして、ローカルPCのコンテナで動作しているOracle Database 23ai Freeを使います。Autonomous DatabaseはパッケージDBMS_DEBUG_JDWPをサポートしていないため、Oracle SQL Developer Extension for VSCodeのPL/SQLデバッガは使えません。Oracle SQL Developerに含まれるデバッガであれば、Autonomous Databaseで利用できます。

Debugging your PL/SQL in Oracle Autonomous Databases
https://www.thatjeffsmith.com/archive/2021/02/debugging-your-pl-sql-in-oracle-autonomous-databases/

以下より実施した確認作業を紹介します。

Jeff Smithさんの記事に従って、以下のDEBUGGING_DEBUGGER.plsを作業に使用します。

オリジナルのコードはデータベースにサンプル・スキーマHRがインストールされていることを前提としたコードになっています。

本記事は、Oracle APEXのアプリケーションから呼び出すプロシージャ、ファンクションおよびパッケージを、VSCodeで作成することを想定しています。データベースでの作業は、APEXのワークスペース・スキーマに接続して作業します。そのため、スキーマHRが存在することを前提にできません。

代わりにAPEXのサンプル・データセットHRデータを、APEXのワークスペースにインストールします。

作業対象のデータベースのAPEXのワークスペースにサインインし、SQLワークショップユーティリティサンプル・データセットを開きます。


サンプル・データセットに含まれるHRデータインストールをクリックします。


言語英語以外に選択肢はなく、スキーマはワークスペースのデフォルト・パーシング・スキーマが選ばれます。

通常は変更不要なので、そのままへ進みます。


データセットのインストールをクリックします。APEXのサンプル・データセットのHRデータは、オラクルがGitHubで公開しているHRサンプル・スキーマとほぼ同じですが、表名が競合しないように、表名の接頭辞としてOEHR_が付加されています。


サンプル・データセットがインストールされます。

アプリケーションは作成せず、終了をクリックします。


以上で作業に使用するスキーマの準備ができました。前掲のDEBUGGING_DEBUGGER.plsは、表HR.EMPLOYEES、HR.JOBSの代わりにOEHR_EMPLOYEES、OEHR_JOBSを参照するように変更済みです。

VSCodeを使う作業に移ります。

必ずしも必要な作業ではありませんが、PL/SQLコードを保持するリポジトリをGitHubに作成しました。リポジトリ名はdatabase-scriptsとしています。


ローカルの環境にクローンします。

git clone https://github.com/<username>/database-scripts.git

% git clone https://github.com/ujnak/database-scripts.git

Cloning into 'database-scripts'...

remote: Enumerating objects: 4, done.

remote: Counting objects: 100% (4/4), done.

remote: Compressing objects: 100% (4/4), done.

remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)

Receiving objects: 100% (4/4), done.

% 


VSCodeを起動します。Oracle SQL Developer Extension for VSCodeが未インストールであれば、拡張機能からインストールしておきます。


作成したディレクトリ(本記事ではdatabase-scripts)を開き、フォルダdebugger-testを作成します。その下にファイルDEBUGGING_DEBUGGER.plsを作成します。


ローカルのコンテナとして動作しているOracle Database 23ai Freeの環境に接続して作業を行います。こちらの記事の手順で作成した環境です。

おそらく、VSCodeとOracle Databaseの間でファイアウォールなどがなく、VSCodeからOracle Database(SQL*Netによる接続)、およびOracle DatabaseからVSCode(JDWP - Java Debug Wire Protocolによる接続)へTCPで接続可能なネットワーク環境であれば、同様に動作すると思われます。

拡張機能のSQL Developerを開いて、オラクル・データベースへの接続作成します。


PL/SQLの開発作業は、APEXのワークスペース・スキーマで接続して実施します。PL/SQLデバッガを実行するために、ワークスペース・スキーマへの権限の割り当てと、JDWPによる接続を許可するためのネットワークACLの作成SYSで行なう必要があります。

そのため、SYSによる接続を作成します。

接続名local-23ai-freepdb1-sysとしました。ロールSYSDBAユーザー名sysパスワードにはsysのパスワードを設定します。データベースへの接続時にパスワードの入力を省略するため、パスワードの保存チェックしておきます。

接続タイプ基本ホスト名localhostポート1521タイプサービス名サービス名freepdb1になります。

上記の設定でテストを行い、問題がなければ保存します。


パスワードの保存未チェックのときは、データベースへの接続を要求したときに、以下のようにパスワードの入力が求められます。


APEXのワークスペース・スキーマにPL/SQLデバッガを実行する権限を与えます。以下のコマンドを実行します。

grant debug connect session to <ワークスペース・スキーマ名>;

作成済みの接続local-23ai-freepdb1-sysをクリックしてデータベースに接続します。その接続のSQLワークシートを開きます。SQLワークシートに上記のコマンドを記述し、ボタン文の実行をクリックします。

SQLワークシート上でカーソルが割り当たっている文が実行されます。スクリプト出力Grantが正常に実行されました。と表示されたらコマンドの実行は完了です。


APEXのワークスペース・スキーマによる接続を作成します。

接続名local-23ai-freepdb1-wksp_apexdevとします。

接続先のデータベースは先ほどのSYSと同じです。接続ユーザーはAPEXのワークスペース・スキーマを指定します。APEXのワークスペース・スキーマはパスワードが未設定であったり、CREATE SESSIONまたはCONNECTロールが未割り当ての場合があります。あらかじめ、APEXのワークスペース・スキーマを接続ユーザーとして、データベースに接続できるかどうか確認し、不足があればAPEXのワークスペース・スキーマに追加で権限やロールを割り当てておきます。

今回の作業では、APEXのワークスペース・スキーマ(以下の設定ではユーザー名)としてWKSP_APEXDEVを指定しています。このスキーマは、サンプル・データセットHRデータをインストールしたスキーマです。


作成した接続local-23ai-freepdb1-wksp_apexdevをクリックし、データベースに接続します。

作成済みのファイルDEBUGGING_DEBUGGER.plsを開きます。タイプarray_jobs、プロシージャdebugging_step_infoおよびdebugging_debuggerの3つを作成するため、これらの行を全て選択します。


スクリプトを表示している画面の右上にあるギアのアイコン赤いアクセントが付いている方のアイコン)をクリックし、選択したスクリプトをデバッグ用にコンパイルします。スクリプトを実行する接続先の選択を求められたときは、APEXのワークスペース・スキーマへの接続であるlocal-23ai-freepdb1-wksp_apexdevを選択します。


スクリプトDEBUGGING_DEBUGGER.plsを実行すると、タイプARRAY_JOBS、プロシージャDEBUGGING_STEP_INTOおよびDEBUGGING_DEBUGGERが作成されます。

今までの作業で開いているファイルおよびSQLワークシートは、以降の作業では使用しません。一旦、これらのファイルをすべて閉じます。

接続local-23ai-freepdb1-wksp_apexdev(ローカルのコンテナで動作しているOracle Database 23ai FreeのPDB、FREEPDB1のスキーマWKSP_APEXDEV)にプロシージャDEBUGGING_DEBUGGERが作成されています。

このプロシージャを開きます。


プロシージャDEBUGGING_DEBUGGERが開いたら、右上の実行ボタンのデバッグを実行します。


デバッグを開始する画面が開きます。処理が実行される接続名(今回はlocal-23ai-freepdb1-wksp_apexdev)、ターゲット(今回はDEBUGGING_DEBUGGER)、それとプロシージャDEBUGGING_DEBUGGERに渡すパラメータXへの入力値が求められます。

デバッグをクリックするとデータベース・サーバーはVSCodeへ、JDWPによる接続を試みます。そのため、あらかじめネットワークACLによる許可が必要です。

ACLの表示をクリックすると、実行すべきコマンドが表示されます。


ACLの表示をクリックすると、IPアドレスの選択を求められます。データベース・サーバーから作業に使用しているVSCodeへの接続するためのIPアドレスになります。ホストとコンテナはネットワークが異なるため、ローカルホストの指定である127.0.0.1では接続できません。127.0.0.1でない方の接続先(以下の例では192.168.10.146)を選択します。


デバッグを実行するために必要なネットワークACLを追加するコードが表示されます。Copyをクリックし、クリップボードに保存します。


データベースにSYSで接続し、クリップボードにコピーしたネットワークACLを追加するコードを実行します。

接続local-23ai-freepdb1-sysを開き、その接続でSQLワークシートを開きます。SQLワークシートにクリップボードからネットワークACLを追加するコードをペーストし、ペーストした文を実行します。


スクリプト出力PL/SQLプロシージャが正常に完了しました。と表示されれば、ネットワークACLの追加は完了です。


接続local-23ai-freepdb1-sysを閉じます。ネットワークACLが書かれているSQLワークシートも不要なので、タブを閉じておきます。保存は不要です。


DEBUGGING_DEBUGGER.runの画面に戻ります。すでに閉じている場合は、再度プロシージャDEBUGGING_DEBUGGERをデバッグ実行します。

パラメータX入力値として5を設定します。

デバッグを実行する前にPL/SQLの表示をクリックし、プロシージャDEBUGGING_DEBUGGERのデバッグを行なうために実行されるPL/SQLコードを確認します。


プロシージャDEBUGGING_DEBUGGERを呼び出すコードとして、以下が表示されます。
-- runner for WKSP_APEXDEV.DEBUGGING_DEBUGGER
SET SERVEROUTPUT ON
DECLARE
  X NUMBER;
BEGIN
  X := 5;
  WKSP_APEXDEV.DEBUGGING_DEBUGGER(
    X => X);
  -- Rollback;
end;

SQLコードが確認できたら不要なタブなので、タブを閉じておきます。保存は不要です。


デバッグをクリックします。JDWPの接続先の選択を求められたときは、ネットワークACLとして追加した接続先を選択します。


プロシージャDEBUGGING_DEBUGGERに引数X5を与えた実行結果が、スクリプト出力に表示されます。

ブレークポイントが設定されていないため、デバッグ実行でもスクリプトの最後まで処理が実行されます。


プロシージャDEBUGGING_DEBUGGER.plsのタブに移り、ブレークポイントを設定します。

行番号の左隣をクリックすると赤いマークが付き、ブレークポイントとして設定されます。


タブDEBUGGING_DEBUGGER.runに移り、再度デバッグをクリックします。


今度は設定したブレークポイントの位置で、PL/SQLの処理が一時停止します。

左側には、一時停止した時点での変数の値、ウォッチ式コールスタックといったスクリプトの実行状況が表示されます。


画面上部にデバッガの操作パネルが表示されています。

一番左の6個の点のアイコンを掴むと、操作パネルを移動できます。それ以外は左から、続行ステップ・オーバーステップ・インステップ・アウト再起動停止(または中断)になります。

この辺りの操作は、PL/SQLに限らず一般的なデバッガが持つ機能と同じです。


さて、以上でPL/SQLでのコードの記述やデバッグができるようになりました。

DEBUGGING_DEBUGGERのコードを修正します。以下の2行をプロシージャDEBUGGING_DEBUGGERの中間に挿入します。
 -- print how many lops we will do
 DBMS_OUTPUT.PUT_LINE('x is ' || x);
コードを挿入した後に、コンテキスト・メニューを表示してデバッグ用にコンパイルします。


プロシージャDEBUGGING_DEBUGGERをコンパイルした後、先ほどと同様にコードを実行します。プロシージャに挿入したコードが実行されていることが確認できます。


上記の変更はデータベースに保存されているプロシージャDEBUGGING_DEBUGGERを更新しています。記事の最初に作成したDEBUGGING_DEBUGGER.plsは更新されません。


パッケージ、プロシージャ、ファンクションといったPL/SQLで記述されたデータベースのオブジェクトは、コードがデータベースに保存されます。これらのコードをGitなどのリポジトリに保存する場合はファイルとして保存することになります。データベースに保存されるコードとGitに保存されるコードが自動的に同期されることはありません。

どのようなフローで同期させるかは、開発するアプリケーションの規模や開発チームの大きさで変わるように思います。アプリケーションや開発チームが小さければ、データベースに保存されているオブジェクトを直接編集し、編集が完了したコードをデータベースからGitに保存する方が手間も少なく開発速度も速いでしょう。反対にアプリケーションや開発チームが大きい場合は、勝手にデータベースのオブジェクトを変更されると大きな問題になりそうです。Gitなどを使って変更を管理し、テストやレビューを受けたコードよりデータベースのオブジェクトを作成し、直接データベースのオブジェクトを変更するのは禁止する、といった運用が妥当なように思います。

Oracle SQL Developer Extension for VSCodeのデバッガに関する設定は、設定デバッガにあります。


全てJDWPでの接続に関する項目です。


今回の記事は以上になります。

余談

最近話題のAI Code EditorとしてCursorがあります。CursorはVSCodeをフォークして作られていて、VSCode用の拡張機能をサポートしています。

Oracle APEXのアプリケーション開発に使うには、フロントエンドの開発では拡張機能のLive Serverが必要です。バックエンドではOracle SQL Developer Extension for VSCodeが必要です。

この両方の拡張機能がCursorで使えるか確認してみました。

Cursor自体には、Live ServerおよびOracle SQL Developer Extension for VSCodeの両方とも組み込めています。


Live Serverは問題なく動作します。


Oracle SQL Developer Extension for VSCodeで問題なくデバッグも動きました。


CursorもVSCodeと同様に、Oracle APEXのアプリケーション開発に使用できるでしょう。