今回はファイルに紐づくコメントをデータベースのテーブルに保存する方法を試してみたいと思います。
テーブルを作成する場合主キーを設定する必要がありますが、今回はファイルのパスを主キーとします。
また、管理する項目はとりあえずコメントだけにします。
基本このブログの記事は個人用のメモのつもりで書いていますが、時間が経過書いた当人が理解不能な文章になっていました。
これから文章能力が向上するとも考えにくいのですが、万人といわずとも自分ぐらいは理解できるような文章になるよう心掛けたいと思います。
閑話休題
モジュールのインポート
Import-Module SQLite
スクリプトを実行するたびにモジュールのインストールが実行されるのはパフォーマンスが低下しそうですし、インターネットに接続できない環境だとエラーになりそうなので、データベースの作成などと合わせてセットアップ用のスクリプトを別途用意した方が良さそうです。追記、コンソールをログアウトするとモジュールが消えるようなので?毎回読み込む必要あり要調査
データベースファイルのパスを定数で定義
定数の書き方を知らなかったので調べてみたところ以下の書式で定義することが出来るようです。
Set-Variable -name 定数名 -value 値 -option Constant
optionのConstantで定数として定義されるようです。構文が長いのでちょっと使いずらい感じがします。
PowerSehllでinclude
そうなると、データベースファイルのパスの定義を複数のスクリプトから参照できるようにすると、データベースファイルを移動しても修正が一か所で済むので良いです。
PowerShellでincludeをする方法を調べてみたところ、以下の方法が見つかりました。
. "スクリプトのパス.ps1"
“.”を使うこの書き方は、外部スクリプトを実行する方法だと認識していたのですが、よくよく考えるとincludeと同じような振る舞いになることに気が付きました。
データベースとの接続
過去の記事を見たところ以下のようなコードを書いていました。
# コネクションオブジェクトの生成
$con = [SQLiteConnection]::new() | % {
$_.ConnectionString = ("Data Source = {0}"-f "データベースファイルのパス.db")
$_.Open()
$_
}
%{}はForEach-Object{}の省略系で繰り返しとはかけ離れた、逆に複数実行すると具合が悪いオブジェクトの生成に使っています。
{}でコネクションを生成に関する処理は、ここから、ここまでですよと表現しているだと思われます。
テーブルの作成
# テーブル作成
# コマンドオブジェクト
$cmd = [SQLiteCommand]::new()
# コマンドオブジェクトとDBを接続
$cmd.Connection = $con
# 実行するSQLをセット
$cmd.CommandText = @"
CREATE TABLE IF NOT EXISTS file_comment (
full_name text,
comment text,
primary key(full_name)
)
"@
$cmd.ExecuteNonQuery() | Out-Null
$cmd.ExecuteNonQuery()の結果をOut-Nullに流し込んで握りつぶしています。これで良いのでしょうか?
SQLのCREATE TABLEでIF NOT EXISTSという構文があり、これはテーブルが存在しない場合実行(テーブルを作成する)するという振る舞いになります。このような書き方をすれば何回実行してもエラーにはなりませんが、テーブルの作成は基本的に1回すればよいので、こちらもセットアップ用のスクリプトに組み込むと良さそうです。
ファイルにコメントをセット
テーブル内にファイルが存在しない場合INSERT文でレコードを追加、ファイルが存在している場合UPDATE分でレコードを更新する処理になります。よくある処理なので便利な方法が無いかと調べたところUPSERTという造語ヒットし、INSRTでキーが重複したらUPDATEをする仕組みのようです。素敵な機能なのですが、自分の実行環境ではバージョンが古いため残念ながら実行できませんでした。
仕方が無いので、レコードの件数を取得し1件以上の場合UPDATE、そうで無い場合INSERTするようにします。
# レコードの件数を取得
$cmd.CommandText = @"
SELECT comment FROM file_comment WHERE full_name = '$FileName'
"@
$rec = $cmd.ExecuteReader()
$count = $rec.Count
$rec.Close()
if ($count -ge 1) {
# 更新
$cmd.CommandText = @"
UPDATE file_comment SET comment = '$Comment' WHERE full_name = '$FileName'
"@
} else {
# 追加
$cmd.CommandText = @"
INSERT INTO
file_comment (
full_name
, comment
)
VALUES (
'$FileName'
, '$Comment'
)
"@
}
$cmd.ExecuteNonQuery() | Out-Null
登録されたコメントの確認
# レコードの参照
$cmd.CommandText = @"
SELECT full_name, comment FROM file_comment
"@
$rec = $cmd.ExecuteReader()
while ($rec.Read()) {
Write-Host ("full_name:{0} comment:{1}" -f $rec['full_name'], $rec['comment'])
}
$rec.Close()
スクリプト
ファイル名:Init-FileComment.ps1
# モジュールのインポート
Import-Module SQLite
# データベースファイルのパス
Set-Variable -Name "DBPath" -Value "H:\ps1\FileComment.db" -Option Constant
# コネクションオブジェクトの生成
$con = [SQLiteConnection]::new() | % {
$_.ConnectionString = ("Data Source = {0}"-f $DBPath)
$_.Open()
$_
}
ファイル名:Setup-FileComment.ps1
using namespace System.Data.SQLite
# モジュールのインポート
Import-Module SQLite
# データベース接続の初期処理
. "h:\ps1\Init-FileComment.ps1"
# テーブル作成
# コマンドオブジェクト
$cmd = [SQLiteCommand]::new()
# コマンドオブジェクトとDBを接続
$cmd.Connection = $con
# 実行するSQLをセット
$cmd.CommandText = @"
CREATE TABLE IF NOT EXISTS file_comment (
full_name text,
comment text,
primary key(full_name)
)
"@
$cmd.ExecuteNonQuery() | Out-Null
ファイル名:Add-FileComment.ps1
using namespace System.Data.SQLite
# コメントを追加
param(
[string]
$FileName,
[string]
$Comment
)
# データベース接続の初期処理
. ".\Init-FileComment.ps1"
function Usage
{
Write-Host ".\Add-FileComment.ps1 -FileName ""ファイル名"" -Comment ""コメント"" "
}
if ( $FileName -eq "" ) {
Usage
Exit
}
# 相対パス ⇒ 絶対パス
$FileName = (Resolve-Path $FileName).Path
# コマンドオブジェクト
$cmd = [SQLiteCommand]::new()
# コマンドオブジェクトとDBを接続
$cmd.Connection = $con
# レコードの件数を取得
$cmd.CommandText = @"
SELECT comment FROM file_comment WHERE full_name = '$FileName'
"@
$rec = $cmd.ExecuteReader()
$count = $rec.Count
$rec.Close()
if ($count -ge 1) {
# 更新
$cmd.CommandText = @"
UPDATE file_comment SET comment = '$Comment' WHERE full_name = '$FileName'
"@
} else {
# 追加
$cmd.CommandText = @"
INSERT INTO
file_comment (
full_name
, comment
)
VALUES (
'$FileName'
, '$Comment'
)
"@
}
$cmd.ExecuteNonQuery() | Out-Null
ファイル名:ListAll-FileComment.ps1
using namespace System.Data.SQLite
# テーブルの一覧を表示(テスト用)
# データベース接続の初期処理
. ".\Init-FileComment.ps1"
# コマンドオブジェクト
$cmd = [SQLiteCommand]::new()
# コマンドオブジェクトとDBを接続
$cmd.Connection = $con
# レコードの件数を取得
$cmd.CommandText = @"
SELECT full_name, comment FROM file_comment
"@
$rec = $cmd.ExecuteReader()
while ($rec.Read()) {
Write-Host ("full_name:{0} comment:{1}" -f $rec['full_name'], $rec['comment'])
}
$rec.Close()
コメント