6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Pleasanterは、SQL Server、PostgreSQL、MySQLという3つの異なるRDBMSに対応しています。それぞれのRDBMSは独自の方言を持っていますが、CodeDefinerはこれらの違いを吸収しながら自動的にテーブルを作成する仕組みを持っています。
本記事では、CodeDefinerがどのようにテーブル作成SQLを生成し、RDBMS間の差異を吸収しているのか、その仕組みを見ていきます。

テーブル作成テンプレートの基本構造

テンプレートファイルの役割

CodeDefinerは、Implem.Pleasanter/App_Data/Definitions/Definition_Sql/CreateTable_Body.txtというテンプレートファイルを使用してテーブル作成SQLを生成します。

このテンプレートには、# で囲まれたプレースホルダーが含まれています。CodeDefinerは、実行時にこれらを実際の値に置き換えてSQLを生成します。

プレースホルダーの詳細

テンプレート内のプレースホルダーは、以下のように置き換えられます。

プレースホルダー 置き換えられる内容 具体例
#TableName# 作成するテーブル名 Users, Issues, Results
#DropConstraint# 既存制約の削除SQL 主に更新時に、既存の制約を削除するSQL
#Columns# カラム定義のリスト "UserId" bigint identity(1,1) not null,
"LoginId" nvarchar(100) not null
#Pks# 主キー制約の定義 CONSTRAINT "PK_Users" PRIMARY KEY ("UserId")
#Defaults# デフォルト値制約 ALTER TABLE ... ADD DEFAULT ...

プレースホルダー置換の実装

プレースホルダーの置換は、Tables.CreateTable() メソッドで行われます。

それぞれ次のように実装されています。

// テンプレートを読み込み 
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, ...); sqlStatement.CreateDefault(factory, tableNameTemp, columnDefinitionCollection); sqlStatement.DropConstraint(factory: factory, sourceTableName: sourceTableName, ...);
// テーブル名を置換 
qlStatement.CommandText = sqlStatement.CommandText.Replace("#TableName#", tableNameTemp);
// SQLを実行
Def.SqlIoByAdmin(factory: factory, transactional: true).ExecuteNonQuery(...);

RDBMS間の差異を吸収する仕組み

ファクトリパターンによる実装の切り替え

CodeDefinerは、ファクトリパターンとISqlsインターフェースを使用して、RDBMS固有の処理を切り替えています。

ここで呼び出されたファクトリ内部にはそれぞれのRDBMS固有のコマンドを保持している部分があります。代表的なものは下記のものです。

// RDBMS別の現在日時関数
factory.Sqls.CurrentDateTime  
// RDBMS別の自動連番定義
factory.Sqls.GenerateIdentity

主要な差異の例

以下では、RDBMS間で大きく異なる部分について、具体的にどのように対応しているかを見ていきます。

1. IDENTITY(自動連番)の違い

自動連番カラムの定義方法は、RDBMSごとに大きく異なります。

// SQL Server
public string GenerateIdentity { get; } = " generated by default as identity (start with {0} increment by 1)";
// PostgreSQL
public string GenerateIdentity { get; } = " generated by default as identity (start with {0} increment by 1)";
// MySQL
public string GenerateIdentity { get; } = string.Empty;

MySQLの場合、主キー制約追加後に別途auto_incrementを設定して対応しています。(ここでは割愛します)

2. 現在日時関数の違い

デフォルト値として現在日時を設定する場合の関数名が異なります。

RDBMS 関数
SQL Server getdate()
PostgreSQL CURRENT_TIMESTAMP
MySQL CURRENT_TIMESTAMP(3)
// SQL Server
public string CurrentDateTime { get; } = " getdate() ";
// PostgreSQL
public string CurrentDateTime { get; } = " CURRENT_TIMESTAMP ";
// MySQL
public string CurrentDateTime { get; } = " CURRENT_TIMESTAMP(3) ";

3. NULL値処理関数の違い

NULL値を別の値に置き換える関数が異なります。

RDBMS 関数
SQL Server isnull(column, value)
PostgreSQL coalesce(column, value)
MySQL ifnull(column, value)
// SQL Server
public string IsNull { get; } = "isnull";
// PostgreSQL
public string IsNull { get; } = "coalesce";
// MySQL
public string IsNull { get; } = "ifnull";

4. データ型の自動変換

RDBMSごとに対応しているデータ型が異なるため、CodeDefinerは自動的にデータ型を変換します。Pleasanterではベースの実装はSQL Serverとなっているため、PosrgreSQL、MySQLに対してのみ変換を行っています。

PostgreSQL の場合
元のデータ型 変換後
nvarchar varchar
datetime timestamp
bit boolean
varbinary(max) bytea

PostgreSqlDataTypes.Convert() メソッドが、これらの変換を自動的に実施します。

MySQL の場合
元のデータ型 変換後
nvarchar varchar または text
datetime datetime(3)
bit tinyint(1)
varbinary(max) blob

varchar(1024) のような大きなサイズを多用するとテーブル作成時にエラーが発生するという制約があります。そのため、下記の条件に従って自動的にデータ型を調整します。

条件 変換後のデータ型 理由
デフォルト値あり varchar(200) text型にはデフォルト値を設定できない
インデックス設定あり varchar(200) text型のインデックスにはサイズ制限がある
上記以外 text より大きなデータを格納できる

テーブル作成の全体的な流れ

実際にテーブルが作成されるまでの処理の流れをまとめるとこのようになります。

  1. CodeDefiner 起動
  2. RDBMS判定(SQL Server/PostgreSQL/MySQL)
  3. ファクトリ生成var factory = RdsFactory.Create(Parameters.Rds.Dbms)
  4. テーブル定義読み込みColumnDefinitionCollectionから対象テーブルの定義を取得
  5. テンプレート読み込みDef.Sql.CreateTable(CreateTable_Body.txtの内容)
  6. プレースホルダー置換
    6. CreateColumn()#Columns#
    6. CreatePk()#Pks#
    6. CreateIx()→インデックス定義
    6. CreateDefault()#Defaults#
    6. DropConstraint()#DropConstraint#
    6. Replace("#TableName#", tableName)
  7. データ型変換factory.SqlDataType.Convert()で RDBMS固有のデータ型に変換
  8. SQL実行Def.SqlIoByAdmin().ExecuteNonQuery()
  9. テーブル作成完了

まとめ

PleasanterのCodeDefinerは、以下の仕組みでマルチRDBMS対応を実現しています。

  1. テンプレートベースのSQL生成
    • プレースホルダー方式により、柔軟なSQL生成が可能
    • RDBMS共通の構造を維持しながら、固有の部分を差し替え
  2. ファクトリパターンによる実装の切り替え
    • ISqls インターフェースでRDBMS固有の処理を定義
    • 実行時にRDBMSに応じた実装クラスを選択
  3. 自動データ型変換
    • RDBMS固有のデータ型に自動変換
    • SqlDataType.Convert()メソッドで一括変換
  4. 制約の自動調整
    • MySQLのvarcharサイズ制限などに自動対応
    • デフォルト値やインデックスの有無に応じた最適な型を選択

これらの仕組みにより、開発者は単一のテーブル定義を記述するだけで、3つのRDBMSすべてに対応したテーブルを自動生成できるようになっています。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?