BunのPostgreSQLアクセス機能を使った簡易なSQLクライアント

# 概要

[Bun](https://bun.sh)でデフォルトとなったPostgreSQLアクセス機能を使った簡単なSQLクライアントを作成してみたので、備忘のため作成

## プロジェクトの作成手順

```shellscript
# 適当な作業ディレクトリを作成してプロジェクトを初期化
mkdir bun-sql-client
cd bun-sql-client

# Bun初期化(Blankで作成)
bun init

# types/bunを追加
bun add -D @types/bun
```

## SQLクライアントコード

DBへの接続情報と、SQLファイルを指定して実行します。
コードの９割は、Geminiさんが作ってくれましたｗ

sql-client.ts

```typescript
import { SQL } from "bun";
import fs from "node:fs/promises";

async function main() {
  // 引数の数をチェック (実行ファイルパスを含めて8つ必要)
  if (process.argv.length !== 8) {
    console.error(
      "Usage: bun sql-client.ts      "
    );
    process.exit(1);
  }

  // コマンドライン引数を取得 (Non-null assertion operator (!) を使用)
  const [_, __, host, portStr, database, user, password, sqlFilePath] =
    process.argv;
  const port = parseInt(portStr!, 10); // portStrがundefinedでないことを示す

  // NaNチェックを追加
  if (isNaN(port)) {
    console.error("Invalid port number provided.");
    process.exit(1);
  }

  let db: SQL | undefined;

  try {
    // PostgreSQLに接続 (bun:postgresを使用)
    db = new SQL({
      hostname: host!,
      port: port,
      database: database!,
      user: user!, // 'username'ではなく'user'を使用
      password: password!,
      // 必要に応じて他の接続オプションを追加
    });

    console.log("Connected to PostgreSQL using bun:postgres");

    // SQLファイルを読み込む (sqlFilePathがundefinedでないことを示す)
    const sqlQuery = await fs.readFile(sqlFilePath!, "utf-8");
    console.log(`Executing SQL from: ${sqlFilePath!}`);
    console.log(`\n${sqlQuery}\n`);

    // SQLクエリを実行
    const result = await db.file(sqlFilePath!);

    // 表形式で結果を出力
    if (result.length > 0) {
      // result は オブジェクトの配列 なので、最初の行から列名を取得
      const columns = Object.keys(result[0]!); // Non-null assertion
      console.log(columns.join(" | "));

      // ヘッダーの下線 (より堅牢な計算方法)
      const colWidths = columns.map((col) =>
        Math.max(
          col.length,
          // result の各行 (row) の対応する列 (col) の文字列長を計算
          ...result.map((row: any) => String(row[col] ?? '').length) // null/undefined の場合に備えて空文字に
        )
      );
      const separator = colWidths.map((w) => "-".repeat(w)).join("-+-");
      console.log(separator);

      // 各行の値を出力 (パディングを追加)
      result.forEach((row: any) => {
        const values = columns.map((col, i) =>
          // null/undefined の場合に備えて空文字にしてから padEnd
          String(row[col] ?? '').padEnd(colWidths[i]!) // Non-null assertion
        );
        console.log(values.join(" | "));
      });
    } else {
      console.log("No rows returned.");
    }

    // result.length を使用して行数を表示
    console.log(`\nQuery returned ${result.length} row(s).`);

  } catch (error) {
    console.error("Error:", error);
    process.exit(1);
  } finally {
    // 接続を閉じる
    if (db) {
      db.end();
      console.log("Disconnected from PostgreSQL");
    }
  }
}

main().catch(console.error);
```

sample.sql

```sql
select 1 as column1 , 2 as column2
```

## 実行結果

*   sample.sqlを実行してみる

```shellscript
bun sql-client localhost 5432 sample sample sample sample.sql
Connected to PostgreSQL using bun:postgres
Executing SQL from: sample.sql

select 1 as column1 , 2 as column2

column1 | column2
--------+--------
1       | 2      

Query returned 1 row(s).
Disconnected from PostgreSQL
```

## まとめ

*   BunだけでTypeScript実行もでき、PostgreSQLにもパッケージ不要でアクセスできるので、手軽なスクリプティング環境としても便利でいいですね
*   この手軽さを活かして、社内DBなどにアクセスする専用MCPサーバとかも簡単に作れそうです