Tsurugiは大文字小文字を区別するのか

このエントリは Tsurugi Advent Calendar 2025 の19日目のエントリです。前日は id:hishidama さんによる「 Tsurugi JDBCの使い方 - シャットダウンオプション 」でした。

取り上げる題材はTsurugiのSQLでは大文字小文字の区別はどのようになっているかというものです。結論を先に挙げると次の通りです。

  • SQLキーワード (SELECTFROM など) は大文字小文字を区別しない
  • ユーザーが定義するデータベースオブジェクト名 (テーブル名や列名など) はデフォルト設定では大文字小文字を区別し、小文字に正規化する設定が存在する
    • その設定は tsurugi.ini[sql] セクションに存在する lowercase_regular_identifiers で、デフォルトでは false に設定されている
    • ただし上記設定に関わらず、引用符で囲まれた識別子は大文字小文字を区別する

多くのRDBMSでは引用していないデータベースオブジェクト名は大文字小文字を区別しないため、意外とつまずくかもしれないポイントです。以下で具体的な挙動を見ていきます。

デフォルト設定での動き

Tsurugiをインストール直後の tsurugi.ini[sql] セクションの記述は次のようになっています。

[sql]
    #commit_response=STORED
    #default_partitions=5
    #lowercase_regular_identifiers=false
    #max_result_set_writers=64
    #scan_block_size=100
    #scan_default_parallel=4
    #scan_yield_interval=1
    #stealing_enabled=true
    #thread_pool_size=

lowercase_regular_identifiers の設定がコメントアウトされていますが、デフォルトの設定値はここに記述されている false です。

この状態で次のDDLを実行してテーブルを作ります。

create table Case_Sample (
   ID bigint primary key,
   Col_1 varchar(*) not null
);

tgsqlコンソールでテーブル一覧を確認すると、テーブル名に設定した大文字小文字が反映されていますね。

tgsql> \show table
Case_Sample

ここでテーブル名を全て小文字にして \show table コマンドを実行してみるとテーブルが見つかりません。 大文字小文字を正しく記述することでテーブル定義を確認することができます。

tgsql> \show table case_sample
'case_sample' table not found

tgsql> \show table Case_Sample
databaseName=null
schemaName=null
tableName='Case_Sample'
(0) ID: BIGINT NOT NULL
(1) Col_1: VARCHAR(*) NOT NULL
primary key=[ID]

SQLの実行についても同様に、テーブル名や列名について大文字小文字を正しく記述する必要があります。

tgsql> insert into case_sample (id, col_1) values (1, 'test');
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:29:50.856+09:00"
]
SYMBOL_ANALYZE_EXCEPTION (SQL-03004: compile failed with error:table_not_found message:"table "case_sample" is not found" location:<input>:1:13+11)
transaction rollback finished implicitly.

tgsql> insert into Case_Sample (ID, Col_1) values (1, 'test');
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:30:22.674+09:00"
]
(1 row inserted)
transaction commit(DEFAULT) finished implicitly.

tgsql> select id, col_1 from case_sample;
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:31:00.586+09:00"
]
SYMBOL_ANALYZE_EXCEPTION (SQL-03004: compile failed with error:table_not_found message:"table "case_sample" is not found" location:<input>:1:23+11)
transaction rollback finished implicitly.

tgsql> select ID, Col_1 from Case_Sample;
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:31:28.003+09:00"
]
[ID: BIGINT, Col_1: VARCHAR(*)]
[1, test]
(1 row)
transaction commit(DEFAULT) finished implicitly.

lowercase_regular_identifierstrue を設定した場合の動き

先にも述べたように多くのRDBMSでは引用していないデータベースオブジェクトは大文字小文字を区別しないため、このデフォルトの動きは違和感を感じる方も多いかもしれません。

そこで tsurugi.ini[sql] セクションに存在する lowercase_regular_identifiers の設定を true に変更してみます (先程作成したテーブルを削除した前提で説明しています) 。

[sql]
    #commit_response=STORED
    #default_partitions=5
    lowercase_regular_identifiers=true
    #max_result_set_writers=64
    #scan_block_size=100
    #scan_default_parallel=4
    #scan_yield_interval=1
    #stealing_enabled=true
    #thread_pool_size=

先程と同様に次のDDLでテーブルを作成してみます。

create table Case_Sample (
   ID bigint primary key,
   Col_1 varchar(*) not null
);

tgsqlコンソールでテーブル一覧を確認するとテーブル名は小文字になっています。それは先程の設定が「SQLで指定したデータベースオブジェクトの識別子は小文字に正規化する」という設定になっているからです。

tgsql> \show table
case_sample

\show table コマンドを実行すると列名も小文字になっていることが確認できます。

tgsql> \show table case_sample
databaseName=null
schemaName=null
tableName='case_sample'
(0) id: BIGINT NOT NULL
(1) col_1: VARCHAR(*) NOT NULL
primary key=[id]

SQLを実行してみると、大文字小文字どちらでも動きます。ただし、識別子を引用する場合はこの設定であっても区別します。 この挙動は概ね PostgreSQLと同じ挙動 になります。

tgsql> insert into case_sample (id, col_1) values (1, 'test');
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:26:42.940+09:00"
]
(1 row inserted)
transaction commit(DEFAULT) finished implicitly.

tgsql> select id, col_1 from case_sample;
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:27:02.509+09:00"
]
[id: BIGINT, col_1: VARCHAR(*)]
[1, test]
(1 row)
transaction commit(DEFAULT) finished implicitly.

tgsql> SELECT ID, COL_1 FROM CASE_SAMPLE;
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:28:08.814+09:00"
]
[id: BIGINT, col_1: VARCHAR(*)]
[1, test]
(1 row)
transaction commit(DEFAULT) finished implicitly.

tgsql> select Id, Col_1 from Case_Sample;
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:29:01.847+09:00"
]
[id: BIGINT, col_1: VARCHAR(*)]
[1, test]
(1 row)
transaction commit(DEFAULT) finished implicitly.

tgsql> select Id, Col_1 from "Case_Sample";
start transaction implicitly. option=[
  type: OCC
  label: "tgsql-implicit-transaction2025-12-09 00:36:58.053+09:00"
]
SYMBOL_ANALYZE_EXCEPTION (SQL-03004: compile failed with error:table_not_found message:"table "Case_Sample" is not found" location:<input>:1:23+13)
transaction rollback finished implicitly.

ちなみに \show table コマンドの場合は設定に関わらず大文字小文字を区別するので注意してください。これはSQLではなくtgsqlが備えるコマンドです。

tgsql> \show table Case_Sample
'Case_Sample' table not found

まとめ

まとめると、Tsurugiはデフォルトでデータベースオブジェクト名の大文字小文字をSQL上で区別します。他のRDBMSに慣れていると違和感を感じる方が多いかもしれませんが、Java系のORマッパーを使っている場合などCamelCaseの識別子を使いたい場合にはこの方がいいと感じる方もいるかもですね。

個人的な意見としては、混乱しないように次のどちらかのルールで運用した方がいいと思います。

  • lowercase_regular_identifierstrue に設定して運用する
  • デフォルト設定で運用する場合、データベースオブジェクト名は小文字で統一するかCamelCaseにする

(参考) 他のRDBMSではどのようになっているの?

先程も述べたように lowercase_regular_identifierstrue を設定した場合の動きは概ねPostgreSQLと同じです。 SQL標準では大文字に正規化することになっており、Oracleなどはそのような挙動になっています。

参考までに他のRDBMSでのこの動きについて説明しているドキュメントのリンクを挙げておきます。MySQLはデータベースオブジェクトをファイルとして保存するため、OSやファイルシステムによって挙動が変わることで有名ですね。

Anthropicの "Code execution with MCP" にモヤモヤする

Anthropicが発表したModel Context Tool (MCP) はAIアプリケーションのツールコールのインターフェースを標準化するプロトコルとして大ブームになり、今や様々なソフトウェアやWebサービスが競ってMCPサーバーを提供するようになっています。 *1

modelcontextprotocol.io

しかし先日、そのAnthropic自らがMCPを利用したAIエージェントアプリケーションの開発に伴う問題点と、それに対応する新たなアプローチを "Code execution with MCP: Building more efficient agents" というタイトルで発表しました。

www.anthropic.com

上記エントリ自体はやや抽象的なところもあり、分かりにくいところもあるのですが、発表後様々な人が内容を検証、実際にサンプルアプリケーションを作ったりしています。概ね次のような内容です。

  1. MCPを使ったAIエージェントはトークン消費量が莫大になるという問題がある
    • MCPサーバーが提供するツールのdescriptionを読み込ませる時点で相当数のトークンを消費する
    • LLMとMCPサーバーのやり取りの過程でもさらにトークンを消費する
  2. 1の対応策としてこれまでのLLMが直接MCPツールをコールするスタイルからコード実行 (Code execution) というスタイルに変えることを提案する
    • MCPツール定義は段階的に提示する (Progressive disclosure)
      • 上記エントリではファイルツリーの形にして管理することを提案している
    • MCPサーバーとやり取りを行うプログラムコードを作成しておき、これをツール定義として提示する
    • LLMには 上記ツールを利用するプログラムコードを生成 させ、そのコードをエージェントが実行して結果を取得、LLMに返す
      • これによりトークンの消費量が大幅に抑えられる

1の課題はよく分かります。特にサイズの小さいローカルLLMを使ったAIエージェント開発においては結構深刻な問題で、自分が作ったときにはエージェントコード側でLLMに見せるツール定義を絞ったりしたことがありました。

Anthropicはその対応策として新たに「LLMにはMCPを利用するコードを生成させる」というアプローチを提示してきた *2 わけですが、確かに提案としてはいいと思うものの非常にモヤモヤするものがありました。というのも予め準備したコードからMCPサーバーへ接続するのならば、接続先は別にMCPサーバーである必要はなく従来のAPIで何ら問題ないわけです。Anthropicは自分達が送り出したMCPを否定するようなことを言い出したように自分には見えました。

まあAnthropic自身もMCPを考え出した時はこのようになることが見えていなかったんだろうなあと。このコード実行方式についても主流になるかどうかは不明で、今後も色んなアプローチが模索されるでしょう。新しいテクノロジー周辺の技術は非常に入れ替わりが激しいので、何か決定版のようなものが登場したと思っても少し時間が経てばコロッとひっくり返される可能性があると常に心掛けておいた方が良さそうですね。

*1:Tsurugiも 提供 していますよ

*2:ちなみにこのアプローチはもう少し前に Cloudflareが似たアプローチを発表 しており、それを受けてまとめたもののようです

Java2Dで縁取り文字列を描画する方法

最近Java2Dでちょっとした画像加工処理を行う機会があったのですが、意外と日本語の情報が見当たらなかったものがあったので備忘録的なメモです。今更こんな古いAPIの話を?とも思いますが、こういう基礎的なAPIはいつになっても使うものですし。

やることは既存の画像に対して文字を重ね合わせて出力するというものです。それも縁取りのある文字列を描画します。

次のような画像に対して...

画像加工の対象となる画像。猫が座っている写真。

次のように文字列を重ねます。

加工後の写真。画像下部に &quot;Outlined Text&quot; という文字列が黒い外枠に白色塗りつぶしで描画されている。

見やすくするために文字列は縁取りを付けて描画します。これが今回のポイントです。 Java2Dの java.awt.Graphics2D クラスには drawString() メソッドがありますが、このメソッドでは縁取り文字列を描画することができません。

縁取り文字列を描画するには描画しようとする文字列のグリフ情報を保持する java.awt.font.GlyphVector クラスを利用します。 次のように java.awt.Font インスタンスと文字列から GlyphVector インスタンスを作ります。

// 加工元の画像ファイルに対するGraphics2Dオブジェクトの取得
var image = ImageIO.read(Objects.requireNonNull(Java2DSample.class.getResourceAsStream("/java2dtest.png")));
var graphics = (Graphics2D) image.getGraphics();

// GlyphVectorの取得
var font = new Font(Map.of(
        TextAttribute.FAMILY, Font.SANS_SERIF,
        TextAttribute.SIZE, 80,
        TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD));
var glyphVector = font.createGlyphVector(graphics.getFontRenderContext(), "Outlined Text");

GlyphvectorgetOutline() メソッドをコールすることで当該グリフを描画するための java.awt.Shape オブジェクトを取得します。引数には描画座標を指定します (graphics2D#drawString() と同じく座標はベースラインの左端の文字位置) 。

var outlineShape = glyphVector.getOutline(120.0f, 980.0f);

あとはこの Shape を使って輪郭と塗りつぶしをそれぞれ描画することになります。滑らかに描画するためにアンチエイリアスをオンにすることをお勧めします。

// 滑らかに描画するためにアンチエイリアスをオンにし、品質重視のレンダリングヒントを与えるのがお勧め
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

// 輪郭の描画
graphics.setStroke(new BasicStroke(10.0f));
graphics.setColor(Color.BLACK);
graphics.draw(outlineShape);

// 塗りつぶし
graphics.setColor(Color.WHITE);
graphics.fill(outlineShape);

これで先に示したように縁取り文字列を描画することができます。

JavaFXだとCSSで指定できるのでこの辺りは楽ですね。でもJavaFXは今回のようにアプリを作らず純粋に画像を操作するだけの目的には使えないんですよね。

今回のサンプルコードの全体は以下のgistにアップしてあります。

Java2Dで縁取り文字列を描画する例 · GitHub

ThunderbirdでGmail送信ができなくなったらSMTPの認証設定を確認しよう

自分は長いことメーラーとしてはずっとThunderbirdを使っていて、仕事場でも自宅でもGmailThunderbird経由で使っています。さらに大昔になると Becky とか使ってたのですが、一時期macユーザーになった時にApple純正のメーラーが余りにも使いにくかったので、Windowsによくあるようなメーラーを探していたところThunderbirdが良かろうということで使うようになり、以来Windowsに戻ってもThunderbirdを使っているという経緯です。

で、家の方で先程Thunderbirdからメールを送信しようとしたら認証で引っ掛かりました。パスワードが正しいのにどうして?そういや大分前からGmailサードパーティーツールとの連携にはOAuth2になったからパスワード入力する時点でおかしいのでは?と気付きSMTP認証設定を開いてみたところ [認証方式]通常のパスワード認証 になっていました…。元々Gmailへの連携設定はThunderbirdのウィザード経由で行っていたのですが、それが大分昔のことだったので古い設定にままになっていたのですねえ。

正しくは次のように OAuth2 に設定する必要があります。

ThunderbirdのSMTP認証設定ダイアログ
ThunderbirdSMTP認証設定ダイアログ

この設定を行って改めてメール送信するとGoogleの認証ダイアログが開き、認証することで送信可能になります。

しかしGmailサードパーティー連携にOAuth2を要求するようになったのは大分前の話のはずで、次の記事にあるように2022年5月時点ではそうなっていました。

support.mozilla.org

つまり長いことプライベート用途でメールを送信する機会がなく、送られてくるメールを読むだけだったのですね。仕事ですらメールを使うことはぐっと減りましたし、本当にメールを使う機会が減ったんだなあと改めて実感することになりましたよ。

人生初の大腸内視鏡検査を受けてきた

健康診断の便検査で2つ提出したサンプルのうち片方で潜血が出たため、精密検査として人生初の大腸内視鏡検査を受けてきたのでそのメモでも。 ちなみに昨年の健康診断ではこれまた人生初の胃カメラを体験してたりします。

検査は午後で、午前中は腸洗浄を行います。これは2時間くらいを掛けて特別な下剤を 2リットル も飲み、腸の中身を全部吐き出すというものです。終わってみればこれが一番大変だった。病院で行うことも多いようですが自分の場合はこれを自宅でやってから病院へ行くことになりました。

検査は鎮静剤を投入して行いました。昨年の胃カメラでは何を考えたのか鎮静剤なしでやったら非常に辛かったので今回はあり一択。 鎮静剤は寝てしまうことはなく、ぼうっとした感じになるというもので、どちらかというと恐怖心を和らげる効果の方が大きかったように思います。検査の間も「何かやっているなあ」とぼんやり感じている程度で、気付いたら終わっていました。

もっともこの点は個人差が大きいようで、他の患者には検査後にいびきをかいて寝ている人もいましたね。自分は検査後の休憩時間も完全に寝ることはなかったです。

検査の結果は特に問題がなかったようでほっとしました。休憩中に他の患者が「大きいポリープは今回取れなかったので別途入院です」とか「直腸ガンですね」とか言われているのが耳に入って流石に怖かった😨

今回検査を受けた病院は色んな動物や魚を飼っていました。よかったら見てやってください。

病院で飼っている大型淡水魚たち(タイガーシャベルノーズ、ダトニオプラスワン、アジアアロワナ)
病院で飼っている大型淡水魚たち

病院で飼っている2匹のデグー。片方は回し車で遊んでいる。
デグー

病院で飼っている古代魚のポリプテルス。水槽に下に説明書きが貼られている。
ポリプテルス

病院で飼っているイグアナ
イグアナ

病院で飼っているハッカン。「老夫婦のため体が衰えています。どうかそっと静かに見守ってください!」の注意書きが貼られている。
ハッカン(白鷴)