CodeDefinerの_rdsモードは何をしているのか

こんにちは。株式会社インプリムのhmineです。
ふだんはOSSのノーコード・ローコード開発ツール「[**プリザンター**](https://www.pleasanter.org/)」に新機能を追加をしています。

今日はプリザンターユーザの皆さまにはおなじみ、**"CodeDefiner" について、たくさん語りたい**と思います。

# 🎁年末恒例Advent Calendar🎁
今年もプリザンターに関する記事のアドベントカレンダーが立ち上がっています！
我こそはプリザンター界を盛り上げていきたい、という方は、[**OSSのノーコード・ローコード開発ツール「プリザンター」 Advent Calendar 2024**](https://qiita.com/advent-calendar/2024/pleasanter) にご参加いただけると嬉しいです！

https://qiita.com/advent-calendar/2024/pleasanter

# 📝5行でこの記事のサマリ📝

1. ver1.4.6以降、初期セットアップ時は、`/l`および`/z`オプションを正しく指定すること。
2. ver1.4.6以降、バージョンアップ時は、`/l`および`/z`オプションを指定しないこと。
3. 上記1、2のオプション指定を誤ってCodeDefinerを実行し、そのままプリザンターの運用を続けていると、SE（システム・エンジニア）の手によるパラメータファイルやデータの修正が必要となる。プリザンターを利用をはじめる前に検知して修正するのがベスト。
4. CodeDefinerは万能運用ツールなのか？（→A. 実は当初のCodeDefinerはC#ソースコード自動生成ツールだった。プリザンターの拡大とともに役割は変化してきた）
5. CodeDefinerの_rdsモードで通る処理のソースリーディングをする。

# 2024年の新機能リリースと、2025年の方針

2024年のプリザンターは華やかなニュースで彩られていた印象です。

- カラーだけでなく操作感も一新した「[**新UI**](https://pleasanter.org/archives/new-thema-cerulean)」のリリース
- 「[**Operations Tools**](https://pleasanter.org/archives/operations-tools)」のリリース
- 「[**MySQL対応**](https://pleasanter.org/blogs/ver1490release)」に伴うOSS DB界を代表する2大DBシステム（PostgreSQLとMySQL）への対応完了

需要をキャッチして便利機能を追加していく細やかなバージョンアップも健在です。

- [**スタイル**](https://pleasanter.org/ja/manual/table-management-style)／[**スクリプト**](https://pleasanter.org/ja/manual/table-management-script)／[**HTML**](https://pleasanter.org/ja/manual/insert-html)／[**サーバスクリプト**](https://pleasanter.org/ja/manual/table-management-server-script)のコーディングを便利にする「**コードエディタ**」機能
- [**CodeDefinerの「mergeモード」追加**](https://pleasanter.org/ja/manual/version-up-windows-1.4.8.0)による利便性のアップ
 
12月に公開されるバージョン1.4.11も、あっと驚く新機能のリリースを予定しています。

2025年もプリザンター開発チームは引き続き、月次のバージョンアップで様々な新機能をご提供できるように、開発に邁進していきます！

# 2024年、CodeDefiner実行コマンドの一部が変更

そんな中、こちらのニュースも気になるところです。

**「ver1.4.6以降で初回インストール時のCodeDefinerの手順について」**

https://pleasanter.org/ja/manual/codedefiner-changed-steps

以下、解説となります。

（※本記事の内容は個人の調査結果です。サポートサービス等のような組織の公式見解ではございませんので、ご承知おきください。）

## CodeDefiner実行のコマンド：ver1.4.6以降で初回インストールの場合

``` bash
## Windows ##
dotnet Implem.CodeDefiner.dll _rds /l "ja" /z "Tokyo Standard Time"
```
``` bash
## Linux（Dockerを含む） ##
sudo -u <ユーザ名> /usr/local/bin/dotnet Implem.CodeDefiner.dll _rds /l "ja" /z "Asia/Tokyo"
```

**-- FAQベースの解説集**

**Q1. 誤って初回インストールで`/l` `/z`オプションを忘れてしまうと、CodeDefinerは異常終了するのですか？**
A1. CodeDefinerは異常終了しません。

`/l` `/z`オプションをつけずに実行するユーザも存在するためです。（英語かつ協定世界時（UTC）のタイムゾーンのユーザが該当します。）

**Q2. `/l` `/z`オプションをつけずに初期セットアップをしたプリザンターは、正常に利用できますか？**
A2. 日本国内（英語かつUTCのタイムゾーンではない環境）では正常に利用できません。

日本国内でプリザンターを利用するユーザの皆さまは、必ず`/l` `/z`オプションをつけてインストールしてください。

影響が懸念されるのは、「プリザンターをインストールするサーバ」と「プリザンターの設定」にタイムゾーンのずれが発生し、業務に影響をおよぼすことです。
誤ってセットアップした場合は、早めにリカバリを実行してください。

・デフォルト使用言語が英語になっている状態。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/e7f41f5e-0183-72e9-1bab-05bfb691e174.png)

・デフォルトタイムゾーンがUTCになっている状態。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/0e0425f2-417c-ef62-f921-441887b4eccc.png)
▲右下のPCの日時（11/25 20:57＝日本時間）に対して、左上のプリザンターの日時が11/25 11:57（＝UTC時間）。9時間ずれている。

**Q3. リカバリはCodeDefinerの再実行をすればいいのですか？**
A3. プリザンターを一度でも起動してしまうと（ログインをしなくても）、CodeDefinerを再実行するだけではリカバリできません。

1. もし、プリザンターをまだ使い始めていなければ、SQL実行でデータベースを削除した後、`/l`および`/z`を指定した正しいコマンドでCodeDefinerを再実行してください。
2. もし、プリザンターを使いはじめてしまった場合、同じデータベースを使い続けるには、以下パラメータファイルおよびデータの状態チェックと、修正が必要です。
   - Service.json
   - Tenantsテーブルのデータ
   - Usersテーブルの定義（デフォルト値）
   - Usersテーブルのデータ

   ※CodeDefinerには、誤りのデータに自動でパッチを当てる機能はありませんので、SE（システム・エンジニア）がサーバ上で直接パラメータファイルとデータを修正してください。また、作業完了後はプリザンターの再起動も必要です。  

   ※上記のように一度プリザンターを使い始めると影響を受けるデータが増えていくため、パラメータファイルおよびデータのパッチ当てによる修正は、複雑な作業になることが予想されます。（なお、サイトを作成しなくても、プリザンターを起動するだけで増えるデータもあります。）なるべく上記1.で示す通り、データベースを削除しCodeDefinerを再実行して0から環境を作り直せることがベストです。

:::note info
**Tips1.**
以下、プリザンターの「デフォルト言語」および「デフォルトタイムゾーン」の設定箇所です。※SQL Serverを使用している例です。

・Service.json
"TimeZoneDefault"と"DefaultLanguage"がグローバルな設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/a476d026-b375-9a84-7dde-c6b07310e708.png)

・Usersテーブルの定義（デフォルト値）
[Language]列と[TimeZone]列がグローバルな設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/c54f50a2-1363-037f-034b-39948944b69a.png)

・Usersテーブルのデータ
各ユーザデータの[Language]列と[TimeZone]列がグローバルな設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/1986a13e-9581-4f09-8b46-37df0d1c51bf.png)

・Tenantsテーブルのデータ
テナントデータの[Language]列と[TimeZone]列がグローバルな設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/7d6f4589-71ed-a619-243f-1a82880bd193.png)

📝**設定の優先順位**
データベースのデータがNullや空欄になっている場合があります。プリザンターの設定はUsersテーブルの設定が最も優先され、Usersテーブルで空欄になっている項目は所属するTenantsテーブルの設定に従います。Usersテーブル、Tenantsテーブルのどちらについても空欄になっている設定項目は、Service.jsonの設定が直接反映されます。

📝**不正な文字列を設定してしまった場合の動作**
なお上記フローに従ってService.jsonの設定値が反映されようとしている、かつ、Service.jsonの設定値が「不正な文字列」（言語やタイムゾーンとして判断できない文字列）だった場合は、強制的にデフォルト言語＝英語、デフォルトタイムゾーン＝UTCの動作となりますので、Service.jsonは正しく記述するようご注意ください。特に間違いやすいのは、タイムゾーンWindows用表記とLinux用表記の誤用です。

📝**正しい言語・タイムゾーンの記述方法**
以下公式マニュアルを参照してください。
・[FAQ：プリザンターでサポートしている言語とタイムゾーンのパラメータの設定値を知りたい](https://pleasanter.org/ja/manual/faq-supported-language)
:::

**Q4. CodeDefinerの_rdsモードのオプション`/l` `/z`が増えたのはなんのため？**
A4. プリザンターの標準設定をグローバルな設定にしたいという、製品戦略が反映されています。この変更により、日本語圏向けの設定は「既定の設定」から、「選択可能な言語・タイムゾーンのオプションのひとつ」という位置づけに変わっています。

## CodeDefiner実行のコマンド：ver1.4.6以降でバージョンアップの場合

``` bash
## Windows ##
dotnet Implem.CodeDefiner.dll _rds
```
``` bash
## Linux（Dockerを含む） ##
sudo -u <ユーザ名> /usr/local/bin/dotnet Implem.CodeDefiner.dll _rds
```

**-- FAQベースの解説集**

**Q1. 今まで1.4.6より前のプリザンターを日本語設定で運用していました。今回バージョンアップで`/l`と`/z`をつけてCodeDefinerを実行してしまったのですが、影響はないでしょうか？**
A1. 以下追加質問をご確認ください。
●CodeDefinerを実行する前までに運用していたService.jsonの内容を把握していますか？

**A1-1. 今までのService.jsonの内容を把握していない**
プリザンターを運用しているサーバの情報がわかれば、今まで運用していたService.jsonの内容を推測可能です。下表の内容を踏まえ、以下A1-2. および A1-3. を参照してください。

| サーバ | "DefaultLanguage" | "TimeZoneDefault" |
| -- | -- | -- |
| Windows | "ja" | "Tokyo Standard Time" |
| Linux（Dockerを含む） | "ja" | "Asia/Tokyo" |

**A1-2. 今までのService.jsonの内容を把握している。
かつ、右記両方に当てはまる。「今までのService.jsonに記載されていた"DefaultLanguage"と、実行したCodeDefinerに設定した`/l`オプションが一致。かつ、今までの"TimeZoneDefault"と`/z`も一致」**

👉問題がないケースです。
次回の作業時には`/l` `/z`を付けずにCodeDefinerを実行してください。

**A1-3. 今までのService.jsonの内容を把握している。
かつ、右記いずれかに当てはまる。「今までのService.jsonに記載されていた"DefaultLanguage"と、実行したCodeDefinerに設定した`/l`オプションが一致しない。または、今までの"TimeZoneDefault"と`/z`の設定内容が一致しない」**

👉リカバリが必要なケースです。
CodeDefinerの実行時に「Usersテーブルのデータが新しいUsersテーブルにそっくり移行される」という処理が行われています。データの量やインデックスのはり方によってはデータ移行に時間がかかったかもしれませんが、CodeDefinerが正常終了していればひとまず問題はありません。
以下リカバリの作業が必要です。

**-- リカバリ実行方法**
正しい値の`/l`と`/z`を付けてCodeDefinerを実行してください。正しい値とは、今までの"DefaultLanguage"と`/l`が一致、かつ、今までの"TimeZoneDefault"と`/z`が一致する状態のことです。このCodeDefiner実行により、再度Usersテーブルのデータ移行が発生します。

**-- リカバリをする理由**
「Servicen.json」と「Usersテーブルのデフォルト制約」が変わってしまったため、それを元に戻すためリカバリを行います。
なお、Usersテーブルについて変更が生じたのは「デフォルト制約」の定義だけであり、テーブルデータは書き換わっていませんのでご安心ください。

**-- バージョンアップのコマンド誤りに気付かず、しばらくプリザンターを使っていた**
上記のリカバリに加えて、データのパッチ当てが必要となる可能性があります。以下データの状態を確認の上、検討してください。
- Tenantsテーブルのデータ
- Usersテーブルのデータ

**-- 次回作業時の注意点**
バージョンアップ時には`/l` `/z`を付けずにCodeDefinerを実行してください。

**Q2. Service.jsonのパラメータ設定と、CodeDefinerの`/l` `/z`オプションの関係について、仕様を教えてください。**

A2. `/l` `/z`オプションの内容で、Servicen.jsonが書き換えられます。また、連動してUsersテーブルのデフォルト制約定義が変更されます。

Servicen.jsonの上書きの処理は、今プリザンターを初期セットアップしようとしているのか／運用中のプリザンターなのかを区別していません。

つまり、`/l` `/z`オプションが指定されると、いかなる状況であっても、Servicen.jsonに記述していた"DefaultLanguage"と"TimeZoneDefault"の内容が書き換えられます。
またこの時に連動して、Usersテーブルのデフォルト制約定義の変更処理が実行されます。

したがって、バージョンアップ作業において、ve.1.4.6以降で_rdsモードのCodeDefinerを実行する際に、`/l` `/z`オプションをつけないように、作業時にはお気をつけください。

## ver1.4.6で行われたService.jsonの既定値変更

ここで、CodeDefinerの内部処理の話題から離れますが、関連するトピックとして、パラメータファイルのマージ作業について注意事項を述べたいと思います。

以下、ver1.4.6がリリースされた際のソースファイルの差分比較です。（※以下のリンク先ページは、ソースファイル数が多いため読み込みに時間がかかります。）

https://github.com/Implem/Implem.Pleasanter/compare/Pleasanter_1.4.5.0...Pleasanter_1.4.6.0

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3658415/e17d4764-e443-c7f3-01eb-205397243c65.png)

上の画像の通り、既定言語＝英語、かつ、既定タイムゾーン＝UTCの設定です。

このため、ver1.4.6以降、日本語環境で運用しているプリザンターのバージョンアップを行う際に、Service.jsonは常に既定値とは異なる設定をご利用いただくことになります。誤って既定値で現行のパラメータファイルの内容を上書きしないよう、ご注意ください。
[**CodeDefinerの「mergeモード」**](https://pleasanter.org/ja/manual/version-up-windows-1.4.8.0)を使用すると、運用中のプリザンターの設定値を優先してマージすることができるので安心です。

---

`/l` `/z`オプションの解説は、ここまでで終了です。

# 考察：CodeDefinerは何をしているのか

## 業務システムでよくあるSE作業の一部が、プリザンターでは、CodeDefiner（_rdsモード）によって自動化されている

_rdsモードで実行するCodeDefinerの役割とは、なんでしょうか？

業務システムでよくあるSE（システム・エンジニア）の作業に見立てて解説します。

フルスクラッチの業務システム開発経験がある方は、テスト環境や、本番環境のソフトウェア・リリース作業を「手順書」に沿って実施する、というイベントをイメージしてみてください。

たとえば以下のような作業の流れをイメージしてください。

1. アプリケーションサービスの停止
2. データベース定義更新用SQLスクリプトの実行
3. モジュール入れ替え
4. アプリケーションサービスの起動

2番の、SEが「SQL文を記述」し、「対象の環境で実行」する部分を、プリザンターの運用ではCodeDefinerの_rdsモードが自動で行っているイメージです。

:::note warn
**Tips2.**

**📝CodeDefinerにデータ修復機能はない**
CodeDefinerは_rdsモードでは、データベースの定義を新規作成または変更しています。実行しているのは、SQL文におけるCREATE TABLEや、ALTER TABLEの部分です。プリザンターの起動以降に追加されたデータの修復（UPDATEの実行）をすることはできませんので、ご注意ください。
:::

## そもそもCodeDefinerとは？

ここから先は、開発者視点の色が強く出てくる解説となります。

**CodeDefinerは万能ツール？**

CodeDefinerは、データベース定義の生成／更新を行う **_rdsモード** や、SQL ServerからPostgreSQLにデータベースを移行する **migrateモード** に加えて、今年（2024年）はパラメータファイルのマージを行う **mergeモード** が追加され、万能運用ツールのような存在になっています。

ですが、CodeDefinerが世に出された約10年前、CodeDefinerは運用ツールではなく、プリザンターのC#コーディング補助ツールとして誕生していました。

**ソースコード自動生成ツールとして誕生したCodeDefiner**

プリザンターは個人開発で誕生したアプリです。

CodeDefinerが初期から備えていた機能は「C#ソースコードの自動生成機能」でした。

プリザンターの個人開発で生じた「C#ソースコードを記述する上で、定型化可能な "ハード寄り" "共通基盤寄り" のソースコードは、ソースコード自動生成システムに記述させる。"業務寄り"の個別の処理から、必要に応じてそれを呼び出したい」という、開発者ニーズ。

それを実現した結果としてCodeDefinerの「データベース定義の生成／更新」自動実行機能も、共に誕生するに至りました。

**アプリのソースコードの中に共通基盤がある**

プリザンターで定型化可能なソースコードには、以下のようなものがあります。

- 「期限付きテーブル」「記録テーブル」：
  - 同じような画面の動作が多く定型化可能
- Wiki：
  - 「期限付きテーブル」「記録テーブル」のエディタと同じように定型化可能
- データベース接続処理、データの追加や参照・更新・削除といった操作用SQL処理：
  - 基盤の処理は定型化可能。さらに、データ型が一致する列の処理は定型化可能であり、多重の共通化ができる
- データベースの定義の生成／更新処理：
  - データベース操作処理が定型化可能であるのと同様、データベースの定義の生成／更新も定型化可能。さらに、両者が参照するデータベースの定義を共通化して、多重の共通化ができる

一般に「オリジナルフレームワーク」と呼ばれる考え方に当てはまります。

**プリザンターが「オリジナルフレームワーク」である理由**

プリザンターは、

- 「期限付きテーブルや記録テーブルでは、項目追加をユーザが自由に行えるようにして、汎用業務アプリにしたい」
- 「画面部品の読み込みは"必要なタイミング"で"パーツ毎"に"非同期"で行って、トラフィック量を可能な限り少なくしたい」
- 「SQLのSELECT文では、特定のテーブルの特定のカラムのデータのみ取得して、さらにトラフィック量を減らしたい」

といった方針が開発の初期から決まっていました。

これを実現するために、バックエンドの処理はオープンなフレームワークを使用せず、全てアプリのソースコードに記述する方式が選ばれました。

**まとめ：プリザンターの成長とともにCodeDefinerの立ち位置も変化している**

ここまで述べてきたように、プリザンターを開発するC#プログラマーの視点では、CodeDefinerは「C#コーディング補助ツール」といった存在だったのです。

このQiitaは開発者の発信の場ということで、ユーザの視点でみたときの「運用補助ツール」とはまたちがった、CodeDefinerの一面をご紹介しました。

意外な内容だったでしょうか？

プリザンターはコントリビュータになってくださる開発者さまを歓迎しています。
こういった裏話にも興味をもっていただけると嬉しいです。

**「ユーザーフレンドリー」というキーワード**

いまプリザンターを開発するプログラマーに求められていることは、プリザンターを「ユーザフレンドリー」なアプリに成長させていくということです。
同じように、CodeDefinerを使った運用オペレーションも、「ユーザフレンドリー」であることが求められているのだと、日々実感しております。

---

CodeDefinerの成り立ちの解説は、ここまでで終了です。

# _rdsモードのDB定義生成／更新処理

ここまでで1万文字ほどの長文になっていますが、最後にもう一度_rdsモードのCodeDefinerについて解説をして終わりたいと思います。

前半で、`/l`および`/z`オプションの解説をしたことを覚えていらっしゃるでしょうか？

これは最近のプリザンターらしいCodeDefinerへの機能追加といえます。

初期のCodeDefinerでは「データベース定義の生成／更新機能」の役割だけをもっていたことと比較すると、ユーザ環境の「言語」「タイムゾーン」オプションの追加は、ユーザの存在を意識した機能追加です。

では、「初期からCodeDefinerがもっていたデータベース定義の生成／更新機能」はどうなっているのか、という解説でこのブログを締めくくろうと思います。

## _rdsモード：プリザンター初回のセットアップ

CodeDefinerの中で、CREATE TABLE文の生成等がまとめられている、ハブ的なC#の処理をご紹介します。

**/Implem.CodeDefiner/Functions/Rds/Parts/Tables.cs**

https://github.com/Implem/Implem.Pleasanter/blob/main/Implem.CodeDefiner/Functions/Rds/Parts/Tables.cs

※コードは2024/12/1時点の内容です。

```cs
internal static void CreateTable(
    ISqlObjectFactory factory,
    string generalTableName,
    string sourceTableName,
    Sqls.TableTypes tableType,
    IEnumerable<ColumnDefinition> columnDefinitionCollection,
    IEnumerable<IndexInfo> tableIndexCollection,
    string tableNameTemp = "")
{
    Consoles.Write(sourceTableName, Consoles.Types.Info);
    if (tableNameTemp.IsNullOrEmpty())
    {
        tableNameTemp = sourceTableName;
    }
    var sqlStatement = new SqlStatement(
        Def.Sql.CreateTable,
        Sqls.SqlParamCollection());
    sqlStatement.CreateColumn(factory, sourceTableName, columnDefinitionCollection);
    sqlStatement.CreatePk(sourceTableName, columnDefinitionCollection, tableIndexCollection);
    sqlStatement.CreateIx(factory: factory, generalTableName: generalTableName, sourceTableName: sourceTableName, tableType: tableType, columnDefinitionCollection: columnDefinitionCollection);
    sqlStatement.CreateDefault(factory, tableNameTemp, columnDefinitionCollection);
    sqlStatement.DropConstraint(factory: factory, sourceTableName: sourceTableName, tableIndexCollection: tableIndexCollection);
    sqlStatement.CommandText = sqlStatement.CommandText.Replace("#TableName#", tableNameTemp);
    Def.SqlIoByAdmin(factory: factory, transactional: true).ExecuteNonQuery(factory: factory, dbTransaction: null, dbConnection: null, sqlStatement: sqlStatement);
}
```

`sqlStatement.CommandText`に、ひとつのテーブルを定義するSQLが書き溜められ、最後の`ExecuteNonQuery`でデータベースサーバ上で実行されます。

- カラムの定義（データ型とサイズ、Null可否、Identity（自動採番））
- PK制約
- Default制約
- Index

以下書き出されるSQLの例です。

```sql
-- SQLの例
create table "dbo"."Users"
(
    "TenantId" int not null,
    "UserId" int identity(1, 1) not null,
    "Ver" int not null,
    "LoginId" nvarchar(256) not null,
    "GlobalId" nvarchar(36) null,
    "Name" nvarchar(128) null,
    "UserCode" nvarchar(32) null,
    "Password" nvarchar(128) null,
    "LastName" nvarchar(32) null,
    "FirstName" nvarchar(32) null,
    "Birthday" datetime null,
    "Gender" nvarchar(2) null,
    "Language" nvarchar(32) not null,
    "TimeZone" nvarchar(32) null,

～中略～

);

alter table "Users" add constraint "Pk_{{PK制約名}}" primary key clustered ("TenantId" asc, "UserId" asc)
    with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on);
alter table "Users" add constraint "df_Users{{Default制約名}}" default 1 for "Ver";
alter table "Users" add constraint "df_Users{{Default制約名}}" default 'ja' for "Language";
alter table "Users" add constraint "df_Users{{Default制約名}}" default 'Tokyo Standard Time' for "TimeZone";

～中略～

create nonclustered index Ix1_{{Index名}} on dbo."Users"
(
    "UserId" asc, "TenantId" asc
) with( statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on);
alter table dbo."Users" set (lock_escalation = table);

～中略～

create unique nonclustered index Unique_{{一意Index名}} on dbo."Users"
(
    "LoginId" asc
) with( statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on);
alter table dbo."Users" set (lock_escalation = table);

```

:::note info
**Tips3.**

**📝`internal static void CreateTable`のコードについての補足**
ソースコードの`sqlStatement.DropConstraint`の箇所に限っては、プリザンターの初期セットアップ処理とは無関係です。

**📝`sqlStatement.DropConstraint`が記述されている理由**
プリザンターのバージョンアップでテーブル定義の変更を伴う場合は、「古い定義のテーブル」と「新しい定義のテーブル」を共存させるために、`sqlStatement.DropConstraint`の処理が必要です。ここで書き出されるSQLは「古い定義のテーブル」からIndexの削除を行います。「古い定義のテーブル」はデータベース上に残るものの、プリザンターでは実際に使われないアーカイブのような存在になるため、Indexはもう不要です。かつ、システム上、古いテーブルのIndexが残ってしまうと不都合なことがあるため、削除は必須です。

👉本項ではテーブルの新規作成処理に注目していますが、`internal static void CreateTable`はテーブルの定義変更でも共通的に実行される処理であるため、テーブル定義の変更時専用の処理もコード上に書かれています。
:::

## _rdsモード：運用中のプリザンターをバージョンアップ

プリザンターのプログラム資源の中に「データベースの定義を記述する.jsonファイル群」があります。

具体的には、以下フォルダの中にある.jsonファイルです。

**/Implem.Pleasanter/App_Data/Definitions/Definition_Columnフォルダ**

https://github.com/Implem/Implem.Pleasanter/tree/main/Implem.Pleasanter/App_Data/Definitions/Definition_Column

プリザンターに新機能が追加される際、データベース定義が変更されることがあります。

さすがに「データ型を文字から数字に変える」といった破壊的な変更になることはありませんが、「新規カラムの追加」はときどき必要となります。
たとえば「新規カラムの追加」をしたい場合は、このフォルダの中に、「追加カラムの情報が書かれたファイル」（.json形式）を追加、といった変更が行われます。

**Definition_Columnフォルダの内容変更時**
CodeDefinerは、実行モードによって次のように動作します。

- defモード…C#ソースコードの自動生成モード
  - 追加されたカラムを使うC#処理コードを自動で記述（数百行）
- _rdsモード…データベース定義の生成／更新モード
  - データベースサーバ上に新しい定義のテーブルを生成　＋　すぐにプリザンターを使えるように事後処理も同時実行

**_rdsモードの場合**
「`Definition_Column`フォルダの最新の内容＝運用中のデータベースの状態」であるか、すべてのテーブルについてひとつひとつチェックする処理が行われます。

運用中のデータベースの状態は、SQLによってシステムデータベース経由で取得されます。

そのハブとなるのは以下のC#のコードです。

**/Implem.CodeDefiner/Functions/Rds/TablesConfigurator.cs**

https://github.com/Implem/Implem.Pleasanter/blob/main/Implem.CodeDefiner/Functions/Rds/TablesConfigurator.cs

※コードは2024/12/1時点の内容です。

```cs
private static void ConfigureTablePart(
    ISqlObjectFactory factory,
    string generalTableName,
    string sourceTableName,
    Sqls.TableTypes tableType,
    IEnumerable<ColumnDefinition> columnDefinitionCollection)
{
    if (!Tables.Exists(factory: factory, sourceTableName: sourceTableName))
    {
        Tables.CreateTable(
            factory: factory,
            generalTableName: generalTableName,
            sourceTableName: sourceTableName,
            tableType: tableType,
            columnDefinitionCollection: columnDefinitionCollection,
            tableIndexCollection: Indexes.IndexInfoCollection(
                factory: factory,
                generalTableName: generalTableName,
                sourceTableName: sourceTableName,
                tableType: tableType));
    }
    else
    {
        if (Tables.HasChanges(
            factory: factory,
            generalTableName: generalTableName,
            sourceTableName: sourceTableName,
            tableType: tableType,
            columnDefinitionCollection: columnDefinitionCollection,
            rdsColumnCollection: Columns.Get(factory, sourceTableName)))
        {
            Tables.MigrateTable(
                factory: factory,
                generalTableName: generalTableName,
                sourceTableName: sourceTableName,
                tableType: tableType,
                columnDefinitionCollection: columnDefinitionCollection,
                tableIndexCollection: Indexes.IndexInfoCollection(
                    factory: factory,
                    generalTableName: generalTableName,
                    sourceTableName: sourceTableName,
                    tableType: tableType));
        }
    }
}
```

このメソッドでは、先ほどの`Definition_Column`フォルダの内容にもとづいて、テーブルひとつひとつについて、以下1.～2.のチェックが行われます。

1. `Tables.Exists( …`はテーブルの存在チェックです。
   →テーブルがデータベースサーバに存在しない場合は、`Tables.CreateTable`の処理で新規作成されます。`Tables.CreateTable`の内容については、先ほどコードを引用して解説しました。
2. `Tables.HasChanges( …`は同じ名前のテーブルがデータベースサーバに存在した場合に、その定義を比較するチェックです。  
   →カラム追加等の変更が見つかった場合は、`Tables.MigrateTable`メソッドの内部で以下のように処理が行われます。
   - 「新定義のテーブル」が（上記と同じ`Tables.CreateTable`で）データベースサーバに新規作成される。
   - 「旧定義のテーブル」からインデックスが削除される。
   - 「旧定義のテーブル」→「新定義のテーブル」にデータがコピーされる。
   - 今後「新定義のテーブル」がプリザンターで利用されるようにテーブル名を変更する。

CodeDefinerの_rdsモードでは、このようにデータベース定義更新が行われます。

# おわりに

プリザンターを長く運用していると、データベースサーバの参照・操作を必要とする場面があるかもしれません。

通常の運用では、CodeDefinerの_rdsモードにSQLの記述・実行を自動で任せられる仕様が便利である一方、中で行われている処理が見えにくくなっている側面もあります。

本記事が、CodeDefinerの_rdsモードに対する理解の一助となれば幸いです！

また、今後もよりユーザフレンドリーな運用手段を検討してまいります。
GitHub Issue等のチャンネルがございますので、ご意見をぜひお聞かせください。