[訳注:postgres95 ユーザ・マニュアル日本語版について (以下,訳者による注意書きを [ と ] で囲んで,訳注:として記します.) このドキュメントは,postgres95 ユーザ・マニュアル(原題 "The POSTGRES95 User Manual Version 1.0")を日本語化したものです.なお,オ リジナルドキュメントは,postgres95-1.0 と postgres95-1.01 に附属したも のの両方が存在しますが,この日本語版では 1.01 に附属のものをベースにし ています.なお,原時点での最新バージョンの 1.08 では,doc/userguide.ps という名前でpostgres95 ユーザ・マニュアルは存在しますが,内容は変わっ ていないように思われます. 最終更新日: 1997年8月27日 バージョン: 1.3 訳者: postgres95 ドキュメント日本語化プロジェクト 変更履歴: 1997/8/27 バージョン1.3リリース 1. オリジナルマニュアル 12章 PQputline() の使用例が誤っていたのを 修正。この情報は PostgreSQL jp ML[2372]にて ematsu@pfu.co.jp さんからお寄せ頂いたものです。 1997/6/23 バージョン1.2リリース 1. 本人からの希望により、金升 忠男さんの email アドレスを変更。 1997/1/21 バージョン1.1リリース 1. 第5章の誤訳を修正(by hkuro@kokugakuin.ac.jp) 34c34 < 州都には,余分な属性であるそれらの状態を示す state があります. --- > 州都には、追加の属性としてその州を示す state があります。 2. 変更履歴、及び謝辞を about.txt に追加 1996/10/14 バージョン1.0リリース 謝辞: 以下の方々の手により,本ドキュメントは作成されました(敬称略) 中村 昌義(masayang@ns-kansai.co.jp) 金升 忠男(kanemasu@tora.email.ne.jp) 妻鹿 崇(mega@rodan.misc.hit-u.ac.jp) 星 隆一(hoshi@ap.kagu.sut.ac.jp) 桑村 潤(juk@rccm.co.jp) 花井 浩之(hanai@astec.co.jp) 田仲 稔(green@keiken.co.jp) 齊藤 知人(tomos@elelab.nsc.co.jp) 進藤 昭広(asin@air.rim.or.jp) 石井 達夫(t-ishii@sra.co.jp) また、以下の方々から有用な御指摘を頂きました(敬称略) 黒崎浩行(hkuro@kokugakuin.ac.jp) まつもと えいじ(ematsu@pfu.co.jp) ] POSTGRES95 ユーザマニュアル バージョン 1.0 1995年9月5日 Andrew Yu and Jolly Chen (with the POSTGRES Group) Computer Science Div., Dept. of EECS University of California at Berkeley Copyright (C) 1994-6 by the Regents of the University of California POSTGRES95 の著作権(copyright (C) 1994-5)はカリフォルニア大学の本校が 有します.このソフトウェアとその文書を,無料で使用許諾書無しに,如何な る目的に対しても,使用・複写・修正・そして再配布することへの許可を,上 記の著作権表示・この段落・およびこれに続く二つの段落がすべての複写に現 れる限りにおいて,ここにそれを認めます. 例えカリフォルニア大学が以下のような損害の可能性について言及していたと しても,このソフトウェア及び文書の使用上,直接的・間接的・特別・偶然も しくは必然的に,生じた失われた利益を含む損害に於いて,いずれの当事者に 対してもカリフォルニア大学は一切の責任を負いません. カリフォルニア大学は,特定目的のための商用性及び適合性の暗黙の保証を含 む,しかしそれに限定されることのない,いかなる保証も明確に放棄します. ここにおいて用意されたソフトウェアは「あるがまま」ということを基本とし, カリフォルニア大学は維持・補助・更新・改良・修正を用意する義務を負いま せん. 1. 紹介 この文書は,カリフォルニア大学バークレー校で開発された「POSTGRES95」 というデータベース管理システムのユーザーマニュアルです.POSTGRES95は POSTGRES リリース4.2に基づいております.「POSTGRES プロジェクト」は Michael Stonebraker教授に指導されて,Defense Advanced Research Projects Agency (DARPA),Army Research Office (ARO), National Science Foundation (NSF), そしてESL社に後援されました. 1.1. POSTGRESとは何か? 伝統的な関係データベース管理システムは,特定の型の属性を含む,名前付 けられた関係の集合から成るデータモデルをサポートします.現在の商用のシ ステムにおいては,使用可能な型は浮動小数点数,整数,文字列,金額,日付 を含みます.そして,このモデルは将来のデータ処理のアプリケーションのた めには十分ではないと,一般的には認識されています. 関係モデルはその「極度の簡潔さ」により,首尾良く以前のモデルを部分的 に置き換えました.しかしながら,良く言われるように,この簡潔さが,しば しばある種のアプリケーションの実装を極めて難しいものにしました. POSTGRESは以下の4つの基本的な構造を取り込むことにより,利用者が容易に システムを拡張できるような重要な付加的なパワーを提供します. クラス 継承 型 関数 加えて,POSTGRESはパワフルなプロダクション・ルールシステムをサポート します. 1.2. POSTGRES プロジェクトのショートストーリー POSTGRES DBMSの開発は1986年に始まりました.初期のシステムのコンセプ トは[STON86]において出されました.そして,初期のデータモデルの定義は [ROWE87]で著されました.当時のルールシステムの雛型は[STON87a]で説明さ れました.ストレージ・マネージャの基本原理とアーキテクチャは[STON87b] で詳しく記されました. POSTGRESはその後数種類のメジャーリリースを経験しました.最初のデモシ ステムは1987年に動かせるようになり,1988年のACM-SIGM0D会議で公開されま した.私達は[STON90a]で記述されたVersion 1を1989年の6月に数人の外部の 利用者にリリースしました.最初のルールシステムである[STON89]への論評に 応えて,ルールシステムは[STON90b]として再びデザインされ,新しいルール システムとともに1990年の6月にVersion 2がリリースされました.Version 3 は1991年に現れ,複数のストレージマネージャ,改善された問い合わせの実行, 新しく書き直された書き換えルールシステムのサポートを加えました.その後 のリリースは,ほとんどの部分が移植性と信頼性に焦点が置かれました. POSTGRESは多くの異なる調査や生産アプリケーションの実装に使われてきま した.これらは財務分析,ジェットエンジン性能監視のためのパッケージ,小 惑星の軌道データベース,医療情報データベース,いくつかの地理情報システ ムなどを含みます.POSTGRESはまた,教育のための道具としていくつかの大学 で使われています.最終的に,Illustra Information Technologiesがコード をピックアップし,商用化しました. POSTGRESは1992年の後半に,Sequoia 2000 科学技術計算プロジェクトのた めの主要なデータマネージャとなりました.なおそのうえ,外部利用者共同体 の数は1993年の間におよそ倍になりました.これは段々明らかになったことな のですが,プロトタイプコードの維持やサポートは,本来データベース調査に ささげられるべき時間の大部分を必要としていました.このサポートの重荷を 減らす努力の結果,そのプロジェクトは公式にVersion 4.2において終りまし た. 1.3. POSTGRES95とは何か? POSTGRES95は,POSTGRESの最後の公式リリースであるVersion 4.2 からの派 生です.そのコードは今完全にANSI Cで書かれ,コードのサイズは25 %にまで 小さくなりました.多くのパフォーマンスとコードの維持性を改善した実質的 な変更が多く存在します.POSTGRES95はVersion 4.2と比べて,Wisconsin Benchmarkでは30 %ないし50 %程速く走ります.バグフィックスは別として, 多くの改良が存在します: ・問い合わせ言語 POSTQUELは(サーバに実装された)SQLに取ってかわられまし た.(ユーザ定義のSQL関数で同様なことができますから)副問い合わせをサ ポートしません.集合は再び実装されるようになりました.また,GROUP BY のサポートを加えました.libpqインターフェースは今でもCプログラムのた めに利用できます. ・monitor プログラムに加えて,私達はGNU readline をサポートする新しい プログラム(psql)を提供します. ・私達は新しいフロントエンドライブラリであるlibpgtclを加えました.それ はTclベースのクライアントをサポートします.サンプルシェルである pgtclshは,POSTGRES95バックエンドとのtclプログラムのインターフェース を提供する新しいTclコマンドです. ・巨大オブジェクトのインターフェースはオーバーホールされました.私達は インバージョン巨大オブジェクトを,巨大オブジェクトを保存するためのメ カニズムでしかないと位置付けました(これを,除かれたインバージョンファ イルシステムと混乱させるべきではありません) ・インスタンスレベルのルールシステムは除かれてしまいました.ルールはま だ書き換えルールとして利用できます. ・普通のSQlの機能を紹介する短いチュートリアルが,我々のSQLと同様にソー スコードと共に配布されます.[訳注: doc/man/sql.l のことです] ・(BSD makeの代わりに)GNU makeが実行モジュール構築のため利用できます. また,POSTGRES95はパッチのあたっていない(doubleのデータアライメント が改善された)gccでもコンパイルできます. 1.4. このリリースに関して POSTGRES95は無料で利用できます.このマニュアルはPOSTGRES95のVersion 1.0について説明します.著者はPOSTGRES95を以下のプラットフォームでコン パイル及びテストを行いました. アーキテクチャ プロセッサ オペレーティングシステム DECstation 3000 Alpha AXP OSF/1 2.1, 3.0, 3.2 DECstation 5000 MIPS ULTRIX 4.4 Sun4 SPARC SunOS 4.1.3, 4.1.3_U1; Solaris 2.4 H-P 9000/700 and 800 PA-RISC HP-UX 9.00, 9.01, 9.03 Intel X86 Linux 1.2.8, ELF 1.5. このマニュアルの概要 今から,私達はPOSTGRESという単語をPOSTGRES95の意味で使います.このマ ニュアルの最初の部分はPOSTGRESシステムの使用にあたっての幾つかの基本的 なシステムの概念と手続きについて概観を示します.その後,2,3の進化し た機能を紹介しながら,POSTGRESのデータモデルとSQL問い合わせ言語につい てのチュートリアルに転じます.次に,拡張性に対するPOSTGRESのアプローチ を説明し,ユーザ定義の型,演算子,集約,そして問い合わせ言語及びプログ ラミング言語による関数の追加,いかにしてユーザがPOSTGRESを拡張すること ができるかを説明します.POSTGRESルールシステムのとても簡潔な概要の後に, システム拡張における複雑なことがらと,オペレーティングシステム特有の手 続きとについて議論してある詳細に渡った付録でマニュアルは完結します. 私達は読者の方がUNIX及びCプログラミングに堪能であることを想定してい ます. UNIXはX/Open, Ltd.の商標です.Sun4,SPARC,SunOSそしてSolarisはSun Microsystems Inc.の商標です.DEC,DEC station,Alpha AXPそしてULTRIXは Digital Equipment Corp.の商標です.PA-RISCとHP-UXはHewlett Packard Co. の商標です.OSF/1はOpen Software Foundationの商標です. 2. POSTGRES アーキテクチャ の考え方 先に進む前に,まず POSTGRES の基本的なシステム・アーキテクチャを理解し ておいた方が良いでしょう.POSTGRES の構成要素がどの様に関連しているか を理解すれば,次の章がある程度分かりやすくなります. POSTGRES は,データベースの専門用語で言うところの,単純な「1 ユーザに つき 1 プロセス」クライアント/サーバモデルを採用しています.1 個の POSTGRES セッションは,協調して動く以下の UNIX プロセス(プログラム) から構成されます: + 監視デーモン・プロセス(postmaster) + ユーザ側のフロントエンド・アプリケーション(たとえば psql) + 一つまたはそれ以上のバックエンド・データベース・サーバ(postgres のプ ロセス自身) 一つの postmaster は単一ホスト上の複数のデータベースの集合を管理します. そのような集合はインストレーションまたはサイトと呼ばれます.あるインス トレーション内のデータベースにアクセスするアプリケーションは LIBPQ ラ イブラリを呼び出します.LIBPQ ライブラリはネットワークを経由して postmaster にユーザの要求を送信します(図1(a)).そして次に新しいバック エンド・サーバ・プロセスを起動し(図1(b)),フロントエンド・プロセスを新 しいサーバに接続します(図1(c)).この時点から,フロントエンド・プロセス とバックエンド・サーバは,postmaster の介入なしに通信するようになりま す.そういうわけで,フロントエンドとバックエンドのプロセスが生成消滅す るのに対し,postmaster は常に走り続け,(ユーザの)要求を待っているわ けです.LIBPQ ライブラリでは,一つのフロントエンドが複数のバックエンド・ プロセスと接続することができます.しかし,フロントエンド・アプリケーショ ンは依然としてシングルスレッドのプロセスです.マルチスレッド対応のフロ ントエンド/バックエンド接続は現在 LIBPQ ではサポートされていません. このアーキテクチャが示唆することは,フロントエンド・アプリケーションが どこで走っても良いのに対し,postmaster とバックエンドは常に同じマシン (データベース・サーバ)で走るということです.クライアントがアクセスで きるファイルが常にデータベース・サーバ・マシン上でアクセスできるとは限 らない(あるいは違うファイル名でなければアクセスできない)ので,このこ とを心に止めておく必要があります. また,postmaster と postgres サーバは POSTGRES の「スーパユーザ」のユー ザID で走ることも覚えておきましょう.ただし,POSTGRES のスーパーユーザ は特別なユーザ(たとえば,"postgres" という名前のユーザ)である必要は ありません.さらに,POSTGRES のスーパユーザは絶対に UNIX のスーパユー ザ "root" であってはいけません!どんな場合でも,データベースに関連する ファイルは POSTGRES のスーパユーザがそのオーナーであるべきです. 3. POSTGRES を使い始めるにあたって この章では、フロントエンド・アプリケーションを使うための POSTGRES の起 動方法と環境設定の方法を説明します.POSTGRES は既にきちんとインストー ルされているものとします(POSTGRESのインストール方法についてはインスト レーション・ノートを参照して下さい). この章で述べられていることの一部はすべての POSTGRES 利用者に適用されま すが,ある部分については主にサイト・データベース管理者に適用されます. サイト・データベース管理者とは,ソフトウェアをインストールし,データベー ス用のディレクトリを作り,postmaster プロセスを起動する人のことです. この人は UNIX のスーパーユーザすなわち 「root」 や,コンピュータ・シス テムの管理者である必要はありません. この章では,エンド・ユーザ用の項目には「利用者」という印を付け,管理者 向けの項目には「管理者」と記すことにします. このマニュアル全体を通じ,「%」で始まる例は,Unix のシェル・プロンプト が表示されている状態で入力します.「*」で始まる例は,POSTGRES の問い合 わせ言語である POSTGRES SQL のコマンドです. 3.1. 管理者/利用者: 環境設定 (図2. POSTGRES のファイルレイアウト) 図2は,デフォルトでインストールされた場合の POSTGRES のレイアウトを示 しています.簡単のため,POSTGRES は /usr/local/postgres95 にインストー ルされているものとします.したがって,/usr/local/postgres95 というディ レクトリが現れた場合,そのディレクトリ名を実際に POSTGRES95 がインストー ルされている場所に置き換えてください. POSTGRES のコマンドは,すべて /usr/local/postgres/bin に配置されます. ですから,このディレクトリをあなたの シェル・コマンド・パスに加えて下 さい.もしバークレー版 C シェルの類,csh や tcsh を使っているなら,ホー ム・ディレクトリにある .login ファイルに以下を付け加えます. % set path = ( /usr/local/postgres95/bin $path ) もしボーン・ シェルの類,sh や bash を使っているなら,ホーム・ディレク トリにある .profile ファイルに以下を付け加えます. % PATH=/usr/local/postgres95/bin:$PATH % export PATH 以後,すでに POSTGRES の bin ディレクトリがパスに追加されれていること を前提とします.また,このドキュメント中「シェル変数を設定する」とか 「環境変数を設定する」というくだりがしばしば登場します.もしあなたがサー チ・パスの変更についてのべた最後の段落がよく理解できない場合は,まずあ なたが使っているシェルについて説明している UNIX マニュアルのページを参 照した方がよいでしょう. 3.2. 管理者: postmaster の起動 前に説明したことで明らかなように,postmaster プロセスが走っていない限 り,データベースに対して何も操作することができません.サイト管理者が postmaster を起動する前に,覚えておかなければいけないことがたくさんあ ります.これらの点に関してはこのマニュアルの「POSTGRES の管理」という 章で説明します.しかしながら,前に書いた方法の通りに POSTGRES がインス トールされているのなら,次の簡単なコマンドが postmaster を起動する手続 きのすべてとなります. % postmaster & 時には,postmaster は,障害を解決するのに役立つメッセージを出力します. postmaster のデバッグ用のメッセージを見たい場合には,-d オプションを付 けて起動し,出力をログファイルにリダイレクトして下さい. % postmaster -d >& pm.log & もしメッセージが不要の場合には,以下のように入力すれば postmaster は 「寡黙」になります.[訳注: 'S' は "Silent" の S です] % postmaster -S この例では,最後にアンパーサンド ("&") が付いていないことに注意してく ださい. 3.3. 管理者: ユーザの追加と削除 creatuser コマンドにより,特定のユーザが POSTGRES にアクセスできるよう になります.destroyuser コマンドはユーザを削除しPOSTGRES をアクセスで きなくします.これらのコマンドは,ユーザに対して POSTGRES を利用する という面でのみ効果を持つことに注意してください.オペレー ティングシステムのユーザ管理には何の影響もありません. 3.4 利用者: アプリケーションの起動 サイト管理者が正しく postmaster プロセスを起動し,あなたにデータベース を使用する権限を与えれば,(ユーザとして)アプリケーションを起動できるよ うになります.以前述べたように,シェルのサーチパスに /usr/local/postgres95/bin を追加しておかなければなりません.これが準備 することのすべてです.(注1) (注1) もしサイト管理者がデフォルト設定をしていない場合,少し余分な作業 が発生します.たとえば,データベース・サーバー・マシンがリモート・マシ ンなら,PGHOST 環境変数にデータベース・サーバー・マシンの名前を設定す る必要があります.また,PGPORT 環境変数を設定する必要があるかもしれま せん.つまり要点はこうです: もしアプリケーションを起動しようとして, postmaster に接続できないエラーが発生した場合は,すぐにサイト管理者に 相談し,環境変数が正しく設定されているかどうか確認すること. もしも以下のようなエラーメッセージが POSTGRES のコマンド(psql や createdb のような)から出力された場合,(1)postmaster が走っていない,あ るいは (2) 間違ったサーバー・ホストに接続しようとしているという原因が 通常考えられます. connectDB() failed: Is the postmaster running at 'localhost' on port '4322'? もしもエラーメッセージが以下のようなものなら,サイト管理者が postmaster を間違ったユーザで起動していることを示します.管理者に言っ て,POSTGRES のスーパーユーザで再起動してもらいましょう. FATAL 1:Feb 17 23:19:55:process userid (2360) != database owner (268) 3.5. 利用者: データベースの管理 さて,POSTGRES が起動され,走っているので,試しにデータベースを作るこ とができます.ここでは,データベースを管理するための基本的なコマンドを 説明します. 3.5.1. データベースの生成 mydb というデータベースを作るとします.そのためには,以下のコマンドを 入力します. % createdb mydb POSTGRES では好きなだけサイトにデータベースを作ることができ,作成者は 自動的に作成されたデータベースの管理者になります.データベース名は 16 文字以内で,最初の文字はアルファベットでなくてはなりません. すべてのユーザがデータベースの管理者になる権限を持っているわけではあり ません.もし POSTGRES があなたのためにデータベースを作ることを拒否した 場合は,サイト管理者によって,あなたにデータベースを作成する権限を付与 する必要があります.もしこういう事態が起きたら,サイト管理者に相談して ください. 3.5.2. データベースのアクセス データベースを作ることができれば,以下のようにしてデータベースをアクセ スすることができます: + POSTGRES の端末プログラム(monitor または psql)を使い,対話的に SQL コマンドを入力,編集,実行する + LIBPQ サブルーチン・ライブラリを使い,C プログラムを書く.この方法 で,C から SQL を発行し,応答やステータス・メッセージをプログラムが 受け取ることができます.このインターフェイスはのちに??章で議論されま す. [訳注: 12 章です.なお,C 以外にも Tcl/Tk や perl インターフェイスを 利用することができます.] あなたは psql を起動し,マニュアルにある例を試してみたくなるかもしれま せん.以下のコマンドを入力することにより,mydb データベースをアクセス する psql を起動することができます.[訳注: psql を起動する前に,環境変 数 PAGER を unset して下さい.PAGER がセットされていると,いろいろなト ラブルの種になる恐れがあります.] % psql mydb すると以下のような挨拶メッセージが表示されます. Welcome to the POSTGRES95 interactive sql monitor: type \? for help on slash commands type \q to quit type \g or terminate with semicolon to execute query You are currently connected to the database: mydb mydb=> このプロンプトは,ターミナル・モニターが入力待の状態であり,SQL の問い 合わせを,ターミナル・モニター内に保持されている作業領域に入力できるこ とを示しています. psql プログラムはバックスラッシュ文字 "\" で始まるエスケープ・コードを 認識します.たとえば,POSTGRES の さまざまな SQL コマンドの構文に関す るヘルプを以下のようにして得ることができます: mydb=> \h ワークスペースに問い合わせを入力し終わったら,ワークスペースの内容を POSTGRES サーバーに以下のようにして送信することができます: mydb=> \g これにより,サーバーは問い合わせを処理します.もし問い合わせがセミコロ ンで終わっているなら,"\g" は必要ありません.psql は自動的にセミコロン で終わっている問い合わせを処理します. 問い合わせを対話的に入力するのではなく,ファイル - 仮に myFile という 名前だとします - から読むには,以下のように入力します: mydb=> \i fileName psql を終了し,UNIX に戻るには,以下のように入力します.すると,psql は終了し,コマンド・シェルへと戻ります.(他のエスケープ・コードについ ては,モニターのプロンプトで \h と打ってください. mydb=> \q 空白(すなわちスペース,タブ,そして改行)は SQL 問い合わせの中で自由に 使うことができます.コメントは -- で表します.ダッシュから後は,その行 の最後まですべて無視されます. 3.5.3. データベースを消去する あなたが mydb データベースの管理者なら,以下の UNIX コマンドでデータベー スを消去できます: % destroydb mydb この行為により,データベースに関したすべての UNIX ファイルが物理的に消 去され,取り消しはできません.したがって,このコマンドは非常に慎重に使 うべきです. 4. 問い合わせ言語 POSTGRESの問い合わせ言語はSQL-3の変形です.拡張可能な型,継承,関数や 処理ルール(production rules)といった,数々の拡張がほどこされています. これらの機能は,本来のPOSTGRESの問い合わせ言語であったPOSTQUELから引き ついだものです.本章では,POSTGRES SQLを使って簡単な処理を実行する方法 を紹介します. 本マニュアルはPOSTGRES SQLの香り程度をお届けするだけで,SQLの完全な手 引にはなっていません.SQLについては,たくさんの書物があります.例えば, [MELT93]や[DATE93]を参考にしてください.POSTGRES SQLの機能の一部はANSI 標準に準拠してないことにも注意してください. 以下説明する例では,前節で説明したmydbデータベースを作成し,psqlを起動 したという前提ですすめていきます. このマニュアルで使用する例は,/usr/local/postgres95/src/tutorialにも格 納されているはずです.このディレクトリにあるREADMEファイルに利用方法が 記載されています.手引を開始するには,以下の方法にしたがってください. % cd /usr/local/postgres95/src/tutorial % psql -s mydb Welcome to the POSTGRES95 interactive sql monitor: type \? for help on slash commands type \q to quit type \g or terminate with semicolon to execute query You are currently connected to the database: jolly mydb=> \i basics.sql \iコマンドは指定したファイルから問い合わせを読み込みます.-sオプション により,問い合わせが実行される前に処理が中断される逐次実行モードになり ます.本節で使用する問い合わせはbasics.sqlというファイルに格納されてい ます. 4.1. 基本的考え方 POSTGRESにおける基本的な概念はクラスです.クラスはオブジェクトのインス タンスの名前付き集合です.それぞれのインスタンスは名前のついた属性を保 持しています.属性とは指定された型のことです.また,それぞれのインスタ ンスは永続的なオブジェクト識別子(OID)を保有しています.これはあるシス テム内でユニークな識別子です.SQLの構文は表を参照するので,このマニュ アルでは表とクラスという言葉を同様の意味で使用します.同様に行(row)は あるインスタンスであり,列(column)は属性ということになります. すでに説明しましたように,クラスはデータベースの中にグループ化されます. そしてデータベースの集合はpostmasterのプロセス単位で管理され,これがひ とつの実装系(installation)またはサイトを構成します. 4.2. 新しいクラスを生成する 新しいクラスを作成するには,クラスの名前とその全ての属性名と型を定義し ます. CREATE TABLE weather ( city varchar(80), temp_lo int, -- low temperature temp_hi int, -- high temperature prcp real, -- precipitation date date ); キーワードは大文字小文字を区別しませんが,名前は大文字小文字を区別する ことに注意してください.POSTGRES SQLは通常のSQLの型,すなわち整数(int), 浮動小数型(float),実数(real),短い整数(smallint),文字列(char(N)),可 変長文字列(varchar(N)),日付(date),時間(time)をサポートしています.後 半で述べますが,POSTGRESはユーザーが好きなだけデータ型を定義することが できます.従って,名前はキーワードにはなりません. ここまででは,POSTGRESのcreateコマンドは,従来からあるリレーショナルシ ステムの表作成コマンドと同じように見えます.が,クラスにはリレーショナ ルモデルの拡張という面があることをこれから見ていきます. 4.3. クラスにインスタンスを格納する insertコマンドを使って,クラスにインスタンスを格納します. INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '11/27/1994') また,copyコマンドを利用することで,フラット形式(ASCII)ファイルから大 量データを一括してロードすることができます. 4.4. クラスの問い合わせ weatherクラスに対して,通常の関係選択および射影(projection queries)を 使って問い合わせをすることができます.これにはSQLのselect命令を利用し ます.select文は対象のリスト(戻ってくる属性の一覧)と制限部(限定条件を 指定する部分)にわけることができます.例えば,weatherの全ての行を取得す るには以下のように打ち込みます. SELECT * FROM WEATHER; 出力は以下のようになるはずです. city temp_lo temp_hi prcp date San Francisco 46 50 0.25 11-27-1994 San Francisco 43 57 0 11-29-1994 Hayward 37 54 11-29-1994 対象リストの中に任意の式を指定することもできます.たとえば, * SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather; 任意の論理演算子(and, or, not)を制限部に記述することもできます.例えば SELECT * FROM weather WHERE city = 'San Francisco' and prcp > 0.0; city temp_lo temp_hi prcp date San Francisco 46 50 0.25 11-27-1994 もう一つ.選択結果を並べかえて出力したり,重複インスタンスを削除して出 力したりすることもできます. SELECT DISTINCT city FROM weather ORDER BY city; 4.5. SELECT問い合わせのリダイレクト 選択結果は,新たなクラスにリダイレクトすることができます. SELECT * INTO temp from weather; This creates an implicit create command, creating a new class temp with the attribute names and types specified in the target list of the SELECT INTO command. We can then, of course, perform any operations on the resulting class that we can perform on other classes. これにより,暗黙のcreateコマンドが実行され,新しいtempクラスが生成され ます.tempクラスの属性名と型は,SELECT INTOコマンドの対象リストで指定 されます.もちろん,結果として生成されるクラスに対しては他のクラス同様 の操作を実行できます. 4.6. クラス間の結合 ここまでの例では,同時に参照したクラスは一度に一つだけでした.問い合わ せは同時に複数のクラスに対して実行できます.あるいは,同一クラスの中の 複数のインスタンスが同時に処理されるようなことも可能です.同一クラスの 複数のインスタンスや,異なるクラス間の複数のインスタンスを同時に参照す るような問い合わせを「結合」(join query)と呼びます. 例えば,他の記録の温度幅の中にある全ての記録を探すとしましょう.実際に は,各々のEMPインスタンスのtemp_loとtemp_hi属性を,他の全てのEMPインス タンスのtemp_lo,temp_hiと比較する必要があります.(注2) これは以下の問い合わせで実現できます. (注2) これはあくまでも理論上のモデルです.実際の結合はより効果的に,かつ, 利用者には見えない形で実現されることでしょう. SELECT W1.city, W1.temp_lo, W1.temp_hi, W2.city, W2.temp_lo, W2.temp_hi FROM weather W1, weather W2 WHERE W1.temp_lo < W2.temp_lo and W1.temp_hi > W2.temp_hi; city temp_lo temp_hi city temp_lo temp_hi San Francisco 43 57 San Francisco 46 50 San Francisco 37 54 San Francisco 46 50 この例では,W1,W2はweatherクラスのインスタンス,およびこのクラスの全イ ンスタンスに渡っての範囲の代用(surrogates)です.(多くのデータベースシ ステムの用語では,W1とW2は「範囲変数」(range variables)と呼ばれていま す.)問い合わせでは,任意の数のクラス名称と代用を含むことができます. (注3) 4.7. 更新 Updateコマンドを使うことで,すでに存在しているインスタンスを更新するこ とができます.11月28日以降の温度が全て2度高く記録されていたことが わかったとしましょう.以下のようにしてデータを変更できます. * UPDATE weather SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2 WHERE date > '11/28/1994; 4.8. 削除 削除は以下のようにdeleteコマンドを使って実行します. * DELETE FROM weather WHERE city = 'Hayward'; Hayward市に関連する気象記録は全て削除されます. 次のような形式の問い合わせには細心の注意をはらってください. DELETE FROM classname; 条件指定がない場合,deleteコマンドはそのクラスのインスタンス全てを単純 に消し去り,からっぽにしてしまいます.実行に際して,システムはいかなる 確認もしません. 4.9. 集約関数の利用 他の問い合わせ言語と同様,POSTGRESは集約関数をサポートします.しかし, 現状での実装は非常に限られたものです.あるインスタンスの集合に対する件 数勘定(count),合計,平均,最大値,最小値をもとめる集約関数があります が,これらの集約関数は問い合わせの対象リスト部分にだけ使用することがで きます.条件指定部(where句)には利用できません.(例) SELECT max(temp_lo) FROM weather; 集約にはGROUP BY句も利用できます. SELECT city, max(temp_lo) FROM weather GROUP BY city; (注3) 問い合わせ中に出現するクラスの直積の真偽式を制約とするのが,その ような結合の意味です.直積を構成するインスタンスのうち,制約を満足する ものについて POSTGRES はターゲット・リストで指定された計算を行ない,結 果の値を返します.POSTGRES の SQL は,ターゲット・リスト中の重複した値 を特別扱いしません.このことは,POSTGRES がしばしば同じターゲット・リ ストの計算を複数回行なうことを意味します.これは論理式を or でつないだ ような時に良く起こります.そのような重複を避けたい時は,select distinct 文を使う必要があります. 5. 先進的な POSTGRES SQL の特徴 あなたのデータにアクセスするために,POSTGRS SQL の使い方の 基本を扱ってきたわけですが,従来型のデータマネージャと区別 する POSTGRS の特徴を,述べたいと思います. それらの特徴は継承,タイムトラベル,そして非原子的データ値 (配列属性と組属性)を含んでいます. この節の例もまた,tutorial ディレクトリにある advance.sql の 中で見ることができます.(使い方は,前章の概要を参照してくだ さい.) 5.1. 継承 2つのクラスを作りましょう.capitals クラスには,それもまた 都市である州都が入っています.当然 capitals クラスは,cities から引き継ぐべきです. CREATE TABLE cities ( name text, population float, altitude int -- (in ft) ); CREATE TABLE capitals ( state char2 ) INHERITS (cities); この場合,capitals クラスのインスタンスは,親クラスである cities から全ての属性(name[名前], population,[人口] そして altitude[標高]) を継承しています.属性 name の型は, POSTGRESの組み込み型である可変長アスキー文字列型です. 属性 population の型は, POSTGRESの組み込み型である 倍精度浮動小数点型です. 州都には、追加の属性としてその州を示す state があります。 POSTGRES では,クラスは,皆無,または多くの他のクラス(4) から継承することができます. そして問い合わせはクラスの全てのインスタンスかまたは クラスの全てのインスタンスに加えて全てのその子孫を 参照することができます. 例えば,次の問い合わせは,標高500フィートかそれより高い 場所に位置する都市全てを見つけます. [訳注:しかし例題では 標高500フィートはマッチしません.] (注4) I.e. 継承階層構造は閉路を持たない有向グラフです. これに対して,標高500フィートを越える場所に位置し,州都を含む 全ての都市の名前を見つける問い合わせは: SELECT c.name, c.altitude FROM cities* c WHERE c.altitude > 500; 戻り値は: +----------+----------+ |name | altitude | +----------+----------+ |Las Vegas | 2174 | +----------+----------+ |Mariposa | 1953 | +----------+----------+ |Madison | 845 | +----------+----------+ ここで cities の後の * は, cities そして 継承階層中 cities より下の全てのクラスに行き渡るよう,問い合わせに指示します. 既に述べたコマンドの多く( select, update そして delete ,他 にも例えば alter コマンド)がこの * 表記をサポートします. 5.2. タイムトラベル POSTGRES はタイムトラベルについての概念をサポートします. この特徴はユーザが経過的問い合わせするのを許します. 例えば, Mariposa の現在の人口を見つけるための問い合わせは: SELECT * FROM cities WHERE name = 'Mariposa'; +---------+------------+----------+ |name | population | altitude | +---------+------------+----------+ |Mariposa | 1320 | 1953 | +---------+------------+----------+ POSTGRES は自動的に現時点において妥当な Mariposa のレコードの バージョンを見つけます.また時間範囲をも与えることができます. たとえば Mariposa の過去と現在の人口を見るための問い合わせは: SELECT name, population FROM cities['epoch', 'now'] WHERE name = 'Mariposa'; "epoch" がシステムクロックの始め(5)を示します. それでもしあなたがこれまでのところすべての例を実行したなら, 上記の問い合わせの戻り値は: +---------+------------+ |name | population | +---------+------------+ |Mariposa | 1200 | +---------+------------+ |Mariposa | 1320 | +---------+------------+ 時間範囲の開始のデフォルト値はシステムで表現できる最も早い時間 そして,終了のデフォルト値は現在の時間です. 従って上記の時間範囲は, ``[,]''と短縮することができます. 5.3. 非原子的データ値 関係モデルの主義の一つは,関係の属性が最小単位であること です.POSTGRES にはこの制限がありません. 属性は自ら問い合わせ言語から呼び出されることができた 部分値を含むことができます. たとえば,基本型の配列で属性を作ることができます. 5.3.1. 配列 POSTGRESでは,インスタンスの属性に固定長または,可変長 の多次元配列の定義することを許します. どんな基本型の配列でもユーザ定義型の配列でも作ることができます. 最初に基本型の配列を持ったクラスを作り,説明のために使います. * CREATE TABLE SAL_EMP ( name text, (注5) UNIX システムでは,これはいつも 1970年1月1日の深夜 0:00 (グリニッジ 標準時)になります. pay_by_quarter int4[], schedule char16[][] ); 上記の問い合わせは,テキスト文字列 (name) ,従業員の4半期の サラリーに相当するint4の1次元配列 (pay_by_quarter), そして従業員の週間予定に相当する char16 の2次元配列 (schedule), を持ったSAL_EMP という名前のクラスを作ります. 今,いくつか挿入を行います.配列に追加する時は値をブレースで囲み そしてそれらをコンマで区切ることに注意して下さい.もし C 言語を 御存知なら,これは構造体を初期化するための構文に似ていないことも ありません. INSERT INTO SAL_EMP VALUES ('Bill', '{10000, 10000, 10000, 10000}', '{{"meeting", "lunch"}, {}}'); INSERT INTO SAL_EMP VALUES ('Carol', '{20000, 25000, 25000, 25000}', '{{"talk", "consult"}, {"meeting"}}'); デフォルトでは,POSTGRES は配列の番号付けの取り決めに 1始まりを使います. -- n個の要素の配列はarray[1]で 始まりarray[n]で終わります. 今,SAL_EMPにいくつかの問い合わせを行います. 最初に,一度に配列の一つの要素にアクセスする方法を示します. この問い合わせは,第2四半期で給料が変わった従業員の 名前を検索します. * SELECT name FROM SAL_EMP WHERE SAL_EMP.pay_by_quarter[1] <> SAL_EMP.pay_by_quarter[2]; +------+ |name | +------+ |Carol | +------+ この問い合わせは,全従業員の第3四半期の給料を検索します. * SELECT SAL_EMP.pay_by_quarter[3] FROM SAL_EMP; +---------------+ |pay_by_quarter | +---------------+ |10000 | +---------------+ |25000 | +---------------+ また,配列スライス(または部分配列)にもアクセスすることが できます.この問い合わせは週の最初の2日間の Bill の予定の 最初の項目を検索します. * SELECT SAL_EMP.schedule[1:2][1:1] FROM SAL_EMP WHERE SAL_EMP.name = 'Bill'; +-------------------+ |schedule | +-------------------+ |{{"meeting"},{""}} | +-------------------+ 第6章 SQLの拡張:概観 本節では POSTGRES SQL を以下を付け加えることにより拡張する方法を解説します. ・関数 ・型 ・演算子 ・集約 6.1. 拡張の方法 POSTGRESはその操作がカタログ駆動型であるので,拡張可能です.一般的な関 係データベースシステムに馴染みのある読者なら,データベースや表や属性に 関する情報がシステムカタログとして蓄えられていることを知っていると思い ます(いくつかのシステムではこれをデータ・ディクショナリと呼んでいます). 他のシステムと同様,カタログはユーザに対してクラスとして表れますが, DBMSに対しては内部的な情報として蓄えられています.POSTGRESと他の一般的 な関係データベースシステムとの主な違いはPOSTGRESはそのカタログの中によ り多くの情報を蓄えておくことが出来るということです 表や属性の情報だけ ではなく,型や,関数や,アクセス法なども蓄えておくことが出来ます.これ らのクラスはユーザが変更でき,POSTGRESは内部的な操作をこのクラスを元に しているので,POSTGRESをユーザが拡張できる,ということを意味します.そ れと比較すると,従来の関係データベースシステムは,データベース管理シス テム内にあるコード化された手続きを変更するか,ベンダによって提供される 特別なモジュールをロードすることによってしか,拡張できません. POSTGRESは,サーバが,ユーザが書いたコードを動的ローディングを通して組み 込むことができるという点で他の多くのデータマネジャと異なっています.つま り,ユーザは新しい型や関数を書いたオブジェクトファイル(例えば .oファイ ルや共有ライブラリなど)を指定することが出来,POSTGRESは要求があればそれ をロードすることが出来ます.SQLで書かれたコードはより簡単にサーバに付け 加えることが出来ます. このように「軽快に」操作を変更することが出来ることが,POSTGRESが新しいア プリケーションや記憶構造の迅速なプロトタイプ化を可能にしています. 6.2. POSTGRESの型システム POSTGRESの型システムはいくつかの方法で分類することが出来ます.型は基本 型と複合型に分けられます.基本型は int4 などのようなものでC言語などの プログラミング言語にも実装されています.それ等は一般的に「抽象データ型」 として知られているものに相当します:POSTGRESはユーザによって作られたメ ソッドを通してしかそれらの型を操作することが出来ませんし,ユーザが記述 した以上にはそれらの型の振舞いを理解することが出来ません.複合型はユー ザがクラスを作成した時に作られます.EMPは複合型の例の一つです. POSTGRESはこれらの型を一つの方法でしか(クラスのすべてのインスタンスを 保存するファイルに)蓄えておくことが出来ませんが,ユーザは問い合わせ言 語を用いてこれらの型の属性を見ることが出来ますし,例えば属性にインデッ クスを定義するなどの方法でデータの取り出しを最適化することが出来ます. POSTGRESの基本型はさらに 組み込み 型と ユーザ定義 型に分けることが出来ま す.組み込み型(int4など)はシステムにコンパイルされて組み込まれています. ユーザ定義型は以下に述べるような方法でユーザによって作られるものです. 6.3. POSTGRESのシステムカタログについて 前項までで,基本的な拡張性の概念が導入されましたので,実際にカタログが どのように構成されているかを見ていくことが出来ます.この節は今は読み飛 ばしても構いませんが,いくつか後の節ではここで得られる情報なしには理解 できなくなるでしょうから,このページをマークして後で参照できるようにし ておいて下さい.すべてのシステムカタログの名前はは pg_ で始まります. 以下にあげるクラスはエンドユーザにとって有用な情報を含んでいます.(他 にもたくさんのシステムカタログがありますが,それらを直接問い合わせるよ うなことは稀でしょう.) 〜〜〜図〜〜〜 Reference Manual はこれらのカタログや属性に対してより詳しい解説をしてい ます.しかし,図3はシステムカタログの主だった実体や関係を示しています. (他の実体を参照していない属性はプライマリキーでない限り省いてあります.) カタログの中身を実際に見て,それ等がどのように関係づけられているかを理解 するまでは,この図は幾分理解しにくいでしょう.現時点でこの図から主な見究 めるべきことは以下の4つです. (1) 以下の数節で,システムを拡張するのに必要な情報を示すのに必要な,シ ステムカタログ上の結合(join query)を紹介します.この図を見るとそれらの いくつかの結合(join query)(それらはしばしば3回または4回の結合になりま す)が理解しやすいものになるでしょう,何故なら,その問い合わせに使われ た属性が他のクラスの外部キーになっていることがわかるからです. (2) 沢山の異なる機能(クラス,属性,関数,型,アクセス法など)がこのスキー マで強く結びつけられています.簡単なcreateコマンドでこれらの多くのカタ ログを変更することが出来ます. (3) 型や手続きはこのスキーマの中心をなします.ほとんどすべてのカタログが これらのクラスのインスタンスに対する参照を含みます.例えば,POSTGRESは他 のカタログの一つのインスタンスを特定するのに型シグネチャ(例えば関数や演 算子)をたびたび使います. (4) 意味の明白な属性や関係が多くありますが,そうでないものも多くあります (特にアクセス法に関係するものです). pg_am,pg_amop,pg_amproc,pg_operator,pg_opclassの間の関係は特に理解しにく いものですが,基本的な拡張についての議論が終った後で(型へのインターフェ イスやインデックスに対する演算子の節で)より詳しく解説することにします. 注6:手続きと関数という言葉をあまり区別せずに用いています. 7. 拡張 SQL: 関数 すでにおわかりのように,新しい型を定義することの一部として,そのふるま いを記述する関数の定義があります.したがって,新しい型を定義しなくても 新しい関数を定義することはできますが,その逆はできません.それゆえ,こ こでは POSTGRESの新しい型の追加の仕方を述べる前に,新しい関数の追加の 仕方を述べます. POSTGRES SQL は2つの関数の型を用意しています:問い合わせ言語関数(SQL で書かれた関数) とプログラミング言語関数(C のようなコンパイルされるプ ログラミング言語で書かれた関数).いずれの種類の関数も基本型,複合型, あるいは,あるいはそれらを混在させた引数(パラメータ)を取ることができま す.それに加えて,どちらの種類の関数も基本型あるいは複合型を返すことが できます.SQL 関数を定義する方が簡単なので,まず,これらの定義の仕方か ら始めます. この章での例は funcs.sql と C-code/funcs.c でも見られます. 7.1. 問い合わせ言語(SQL)関数 7.1.1. 基本型での SQL 関数 もっとも簡単な SQL 関数は引数を持たず,ただ単に int4 のような基本型を 返すものです: CREATE FUNCTION one() RETURNS int4 AS 'SELECT 1 as RESULT' LANGUAGE 'sql'; SELECT one() AS answer; +-------+ |answer | +-------+ |1 | +-------+ 注目すべきことは,(RESULT という名前で)ターゲットリストを関数に定義し ましたが,関数を起動した問い合わせのターゲットリストが関数のターゲット リストに取って代ることです.したがって,結果は one の代わりに answer とラベル付けされます. 基本型を引数とする SQL 関数を定義することもほとんど同じ位簡単です.下 の例では,$1 と $2 を使って関数の中で引数を定義することに注目して下さ い. CREATE FUNCTION add_em(int4, int4) RETURNS int4 AS 'SELECT $1 + $2;' LANGUAGE 'sql'; SELECT add_em(1, 2) AS answer; +-------+ |answer | +-------+ |3 | +-------+ 7.1.2. 複合型の SQL 関数 (EMP のような)複合型の引数で関数を指定する時,(上記の $1 と $2 のよう に)指定したい引数を指定するだけでなく,引数の属性も指定しなくてはなり ません.例えば,関数 double_salary を例に取ると,もしそれが2倍になると したらあなたの給料が幾らになるかを計算します. CREATE FUNCTION double_salary(EMP) RETURNS int4 AS 'SELECT $1.salary * 2 AS salary;' LANGUAGE 'sql'; SELECT name, double_salary(EMP) AS dream FROM EMP WHERE EMP.dept = 'toy'; +-----+-------+ |name | dream | +-----+-------+ |Sam | 2400 | +-----+-------+ $1.salary の構文の使用に注目してください. 複合型を返す関数の主題に入り込む前に,まず最初に,属性を射影するための 関数表記について紹介する必要があります.これを説明する簡単な方法は,通 常我々が表記の上で attribute(class) と class.attribute とを等価に使え ることです. -- -- これと同じ:: -- SELECT EMP.name AS youngster FROM EMP WHERE EMP.age < 30 -- SELECT name(EMP) AS youngster FROM EMP WHERE age(EMP) < 30; +----------+ |youngster | +----------+ |Sam | +----------+ しかしながら御覧のように,これはいつもそうなるとはかぎりません.この関 数表記は一個のインスタンスを返す関数を使いたい時には重要です.我々はこ れを関数の中で,属性毎にすべてのインスタンスを組み立てることによって行 ないます.これは,一個の EMP インスタンスを返す関数の例です. CREATE FUNCTION new_emp() RETURNS EMP AS 'SELECT \'None\'::text AS name, 1000 AS salary, 25 AS age, \'none\'::char16 AS dept;' LANGUAGE 'sql'; この場合,それぞれの属性を定数値で指定してしまいましたが,しかし,どの 計算も評価もこれらの定数に置き換えられてしまうでしょう.このような関数 を定義することは技術的に難しいかもしれません.幾つかのもっと重要な留意 点は以下の通りです. + ターゲットリストの順番は CREATE TABLE 文で(あるいは .* 問い 合わせを実行した時)現れた属性の中の順番と全く同じです. + (:: を使って)表現を型変換するときは注意深くしなければなりま せん.十分に注意しておこなわないと次のエラーが起こります: WARN::function declared to return type EMP does not retrieve (EMP.*) (型 EMP を返すように宣言された関数は (EMP.*) を取り出しません) + 一つのインスタンスを返す関数の呼び出しでは,全てのインスタン スを取り出すことはできません.一つの属性をそのインスタンスの 外へ射影するか全てのインスタンスを別の関数に引き渡すかをしなけ ればなりません. SELECT name(new_emp()) AS nobody; +-------+ |nobody | +-------+ |None | +-------+ + 一般的に,関数構文を関数の戻り値の属性を射影するために使わな くてはならない理由は,関数呼び出しが組み合わされた時,ただ単に そのパーサが射影のために他方の(点を使った)構文を理解できないか らです. SELECT new emp().name AS nobody; - WARN:parser: syntax error at or near "." ("." か,その近くで構文エラー) SQL 問い合わせ言語の任意のコマンドをひとくくりにして、関数として定義で きます.そのコマンドには選択(select)問い合わせと同様に更新(すなわち, insert, updateおよび delete)を含むことが出来ます.しかしながら,最後 のコマンドは,関数の戻り型として指定されたものを返す select でなければ なりません. CREATE FUNCTION clean_EMP () RETURNS int4 AS 'DELETE FROM EMP WHERE EMP.salary <= 0; SELECT 1 AS ignore_this' LANGUAGE 'sql'; SELECT clean_EMP(); +--+ |x | +--+ |1 | +--+ 7.2. プログラミング言語関数 ------------------------------------------------------------ 7.2.1. 基本型でのプログラミング言語関数 内部的に POSTGRES は基本型を "メモリーの一粒(blob)" として参照します. 自分で型として定義するユーザ定義関数は,POSTGRES が操作できるような方 法で順次定義します.すなわち,POSTGRES はデータの保存と取り出しをディ スクから行なうだけで,ユーザ定義関数をデータの入力,処理,そして,出力 に使います.基本型は次の3つの内の1つのフォーマットに成り得ます: + 値渡し,固定長 + 参照渡し,固定長 + 参照渡し,可変長 値渡しの型は 1 か 2 か 4 バイトの長さのみです(もしお使いのコンピュータ が値渡し型の長さをそれ以外の長さでサポートしていても).POSTGRES 自身は 整数型のみを値として渡します.型を定義する上では,異なる全てのアーキテ クチャにおいて,その型が同じ大きさ(バイト数)になるように注意するべきで す.たとえば,int 型はほとんどの UNIX マシン上で(ほとんどのパーソナル・ マシンではそうでありませんが) 4 バイトであるのに対し,long型は,あるマ シンでは4 バイトですが別のマシンでは 8 バイトですので危険です.UNIX マ シンでの int4 型の合理的な実装はこのようになると思われます: /* 値渡しされる 4 バイトの整数 */ typedef int int4; その一方,任意の大きさの固定長型は参照渡しで構いません.たとえば,ここ にPOSTGRES char16 型の実装例があります: /* 参照渡しされる 16 バイトの構造体 */ typedef struct { char data[16]; } char16; このような型の POSTGRES 関数への出し入れに際し,ポインターのみが使わ れます. 最後に,全ての可変長型も参照渡しでなくてはなりません.全ての可変長型は 正確に 4 バイトのデータ・フィールドと,データ長フィールドの直後に続く メモリー位置に,その型に収まるすべてのデータが保存されてなくてはなりま せん.データ長フィールドはその構造体の合計長さです(すなわち,データ長 フィールドそのものも含みます).text 型を次のように定義できます: typedef struct { int4 length; char data[1]; } text; 明らかに,データ・フィールドは可能な文字列を全て含むには十分な長さでは ありません.-- C 言語ではこのような構造体を宣言する事は不可能です.可 変長型を操作する時には,正しいメモリの大きさを確保して,データ長フィー ルドを初期化することに注意する必要があります.たとえば,40 バイトをテ キスト構造体に保存したいとすると,次のようなコード・フラグメントを使う でしょう. #include "postgres.h" #include "utils/palloc.h" ... char buffer[40]; /* our source data */ ... text *destination = (text *) palloc(VARHDRSZ + 40); destination->length = VARHDRSZ + 40; memmove(destination->data, buffer, 40); ... 今,基本型として可能な構造体の全てについて説明しましたので,実際の関数 の例を幾つか示すことができます.さしあたり,funcs.c をこのようにします: #include #include "postgres.h" /* for char16, etc. */ #include "utils/palloc.h" /* for palloc */ int add_one(int arg) { return(arg + 1); } char16 * concat16(char16 *arg1, char16 *arg2) { char16 *new_c16 = (char16 *) palloc(sizeof(char16)); memset((void *) new_c16, 0, sizeof(char16)); (void) strncpy(new_c16, arg1, 16); return (char16 *)(strncat(new_c16, arg2, 16)); } text * copytext(text *t) { /* # * VARSIZE is the total size of the struct in bytes. * VARSIZE は構造体の合計サイズのバイト数. */ text *new_t = (text *) palloc(VARSIZE(t)); memset(new_t, 0, VARSIZE(t)); VARSIZE(new_t) = VARSIZE(t); /* * VARDATA は構造体のデータ領域へのポインタ. */ memcpy((void *) VARDATA(new_t), /* destination */ (void *) VARDATA(t), /* source */ VARSIZE(t)-VARHDRSZ); /* how many bytes */ return(new_t); } OSF/1 では次のように入力するでしょう: CREATE FUNCTION add_one(int4) RETURNS int4 AS '/usr/local/postgres95/tutorial/obj/funcs.so' LANGUAGE 'c'; CREATE FUNCTION concat16(char16, char16) RETURNS char16 AS '/usr/local/postgres95/tutorial/obj/funcs.so' LANGUAGE 'c'; CREATE FUNCTION copytext(text) RETURNS text AS '/usr/local/postgres95/tutorial/obj/funcs.so' LANGUAGE 'c'; 他のシステムでは,(共有ライブラリであることを示すために) .sl で終るファ イル名で作らなくてはならないかもしれません. 7.2.2. 複合型でのプログラミング言語関数 複合型は C の構造体のように固定された割付を持ちません.複合型のインス タンスはナル(null)のフィールドを持っても構いません.それに加えて継承階 層構造の部分での複合型は,同じ継承階層構造の他のメンバーとは別のフィー ルドを持っても構いません.それゆえ,POSTGRES は C 言語から複合型のフィー ルドにアクセスするための手続き形式のインターフェースを用意しています. POSTGRES はインスタンスの組を処理しますので,それぞれのインスタンスは 不明瞭な TUPLE 型の構造体として関数の中へ渡されるでしょう.さしあたり, 問い合わせに答えるための関数を書くとすると * SELECT name, c_overpaid(EMP, 1500) AS overpaid FROM EMP WHERE name = 'Bill' or name = 'Sam'; 上記の問い合わせでは,c_overpaid を次のように定義できます.: #include "postgres.h" /* for char16, etc. */ #include "libpq-fe.h" /* for TUPLE */ bool c_overpaid(TUPLE t,/* EMP の現行のインスタンス */ int4 limit) { bool isnull = false; int4 salary; salary = (int4) GetAttributeByName(t, "salary", &isnull); if (isnull) return (false); return(salary > limit); } GetAttributeByName は現在のインスタンスのからその属性を返す POSTGRESシ ステム関数です.そして3つの引数を持ちます:関数の中に渡される TUPLE 型の引数,目論まれた属性の名前,それと,属性がナル(null)かどうかを表す 戻り値です.GetAttributeByName はデータをきちんと並べますので,戻り値 を目的の型に型変換できます.例えば,もし char16 型の属性名を持っている とすると,その GetAttributeByName の呼び出しは,次のように見えるでしょ う: char *str; ... str = (char *) GetAttributeByName(t, "name", &isnull) 次の問い合わせは POSTGRES に c_overpaid 関数を知らせます: * CREATE FUNCTION c_overpaid(EMP, int4) RETURNS bool AS '/usr/local/postgres95/tutorial/obj/funcs.so' LANGUAGE 'c'; C 関数の中から,新しいインスタンスを構築したり,既存のインスタンスを修 正したりする方法もありますが,これらは,このマニュアルの中で議論するに はあまりにも複雑すぎます. 7.2.3. 留意点 さて,ここからプログラミング言語関数を書くと言う更に難しい作業に入りま す.警告:このマニュアルのこの章はプログラマを養成するためのものではあ りません.POSTGRES で使う C 言語関数を書こうとする前に,すくなくとも (ポインターと malloc メモリー管理の使用を含む) C言語の理解が必要です. C 言語以外で書かれた関数を POSTGRES の中に読み込むことは可能かもしれま せんが,(それが出来たとしても)これは大抵難しいことです.なぜなら,他の 言語は大抵 FORTRAN や Pascal のように C と同様の "呼び出し慣例" には従 いません.言い替えると,他の言語は同じ方法で引数を渡して値を返すことを しません.こういった理由で,プログラミング言語関数は C で書くことを想 定します. C 関数を構築するための基本的な規則はつぎの通りです: (1) POSTGRES の ほとんどのヘッダー(include)ファイルは既に /usr/local/postgres95/include に(図2参照)インストールされてい るはずです. -I/usr/local/postgres95/include をいつも cc コマンド行に入れるべきです.ときどき,必要なヘッダー ファイルがサーバーのソースそのものとして在る(すなわち,我々が include にインストールするのを怠ったファイルを必要とする時)か もしれません.このような場合,次のうちの一つ以上を追加する必要 があるでしょう. -I/usr/local/postgres95/src/backend -I/usr/local/postgres95/src/backend/include -I/usr/local/postgres95/src/backend/port/ -I/usr/local/postgres95/src/backend/obj (ここで, は移植先の名前で,例えば alpha とか sparc とかです.) (2) メモリーを確保する時は,POSTGRES ルーチンの palloc と pfreeそれに対応する malloc と free の代わりに使いましょう. palloc で確保されたメモリーはそれぞれのトランザクションの終り に自動的に解放され,メモリー・リークを防ぎます. (3) いつも,構造体の中を memset か bzero で零にしましょう.幾 つかのルーチン(ハッシュ・アクセス手法,ハッシュ結合と並び変え アルゴリズムのような)は構造体の中に含まれる生のビットを計算す る関数です.たとえ,もし構造体の全てのフィールドを初期化したと して も,数バイトの並び調整の詰め込み(alignment padding:構造体の穴) がゴミの値を含んでいるかも知れません. (4) ほとんどの POSTGRES 内部の型は postgres.h で宣言されていま すので,通常このファイルをインクルードするのが良いでしょう. (5) コンパイルしてオブジェクト・コードを読み込むときには, POSTGRES に動的に読みませることができますが,いつも特殊なフラグ が要求されます.特定のオペレーティング・システムでそれをどのよ うにするかについては付録 A を参照下さい. 8. SQLの拡張: 型 ____________________________________________________________ さきに述べたように,POSTGRESには2つの種類の型,すなわち基本型 (プログラミング言語において定義されたもの)と複合型(インスタンス), があります. 本章における索引へのインターフェイスまでを含めた例はcomplex.sql とcomplex.cの中に見つけることが出来ます.また複合型の例は funcs.sqlの中にあります。 8.1. ユーザ定義型 ____________________________________________________________ 8.1.1. ユーザ定義型に必要な関数 あるユーザ定義型は常に入出力関数を持っていなければいけません. これらの関数はその型が(ユーザによる入力やユーザへの出力の) 文字列中にどのように現れるか決定し,その型がどのようにメモリ中 に配置されるか決定します.入力関数はヌル文字で区切られた文字列 を入力として取り,その型の内部的な(メモリ中での)表現を返します. 出力関数はその型の内部的な表現を受け取り,ヌル文字で区切られた 文字列を返します. 複素数を表現する複素型を定義したい,としてみましょう. 通常,一つの複素数をメモリ内部で表現するために次のような C の構造体, typedef struct Complex { double x; double y; } Complex; を選び,(x,y)という形の文字列を外部表現として選びます. これらの関数,特に出力関数を書くのは通常そんなに難しくはありません. しかしながら,考慮すべき点がたくさんあります. (1) 外部(文字列)表現を定義するときには,結局はその入力関数としてその 表現に対する完全でしっかりしたパーザを書かなければならないという ことを覚えておいてください! Complex * complex_in(char *str) { double x, y; Complex *result; if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) { elog(WARN, "complex_in: error in parsing return NULL; } result = (Complex *)palloc(sizeof(Complex)); result->x = x; result->y = y; return (result); } 出力関数は簡単に, char * complex_out(Complex *complex) { char *result; if (complex == NULL) return(NULL); result = (char *) palloc(60); sprintf(result, "(%g,%g)", complex->x, complex->y); return(result); } と出来ます. (2) 入力と出力の関数を互いに逆転してみるべきです.もしそうしなければ データをファイルにダンプし、それを読み出す(つまり,別のコンピュータ にある誰か他の人のデータベースに読み込む)時に深刻な問題が起きる でしょう.これは浮動小数点数が含まれている時に特に普通に見られる問題 です. 複素数型を定義するために,その型を作る前に二つのユーザ定義関数 complelx_in と complex_out を作る必要があります. CREATE FUNCTION complex_in(opaque) RETURNS complex AS '/usr/local/postgres95/tutorial/obj/complex.so' LANGUAGE 'c'; CREATE FUNCTION complex_out(opaque) RETURNS opaque AS '/usr/local/postgres95/tutorial/obj/complex.so' LANGUAGE 'c'; CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out ); さきに議論したように,POSTGRESは基本型の配列を完全にサポートします. さらにPOSTGRESはユーザ定義型の配列をもサポートします.ある型を定義 した時,POSTGRESは自動的にその型の配列のサポートを提供します.歴史的 な理由により,その配列型はユーザ定義型に _ が先頭に付いた名前を持ち ます. 複合型は,それに関して定義されたどんな関数も必要としません.なぜなら システムは既にそれが内部でどのように見えるかを理解しているからです. 8.1.2. 巨大オブジェクト これまで議論してきた型は全て「小さい」オブジェクト、すなわちサイズが 8KB(注7)よりも小さいもの、でした.もしドキュメント検索システムやビッ トマップデータの蓄積のようなもののためにもっと大きな型が必要な場合には POSTGRES large object インタフェイスを使う必要があるでしょう. ____________________ 注7 8 * 1024 = 8192バイト.実際,型は8192バイトよりもかなり小さくな ければいけません.なぜならPOSTGRESのタップルやページのオーバヘッド もまたこの8KB制限に納まらなければならないからです.納まるための 実際の値はマシンのアーキテクチュアによります. 9. SQLの拡張: 演算子(OPERATORS) POSTGRES は左単項,右単項,二項演算子をサポートしています.演算子 (operator)はオーバーロード,もしくは異なる数と型の引数で再使用することが できます.曖昧な状況において,使用すべき演算子をシステムが決定できない時 は,エラーが返されるので,どの演算子を使おうとしているのかわかるように, 左のそして/または右の被演算数を型変換してやらなくてはなりません. 二つの複素数(complex number)の加算をする演算子を作るには次のようにします. 最初に新しい型の加算をする関数を作る必要があります.そうすれば,関数から 演算子を作ることができます. CREATE FUNCTION complex_add(complex, complex) RETURNS complex AS '$PWD/obj/complex.so' LANGUAGE 'c'; CREATE OPERATOR + ( leftarg = complex, rightarg = complex, procedure = complex_add, commutator = + ); これで二項演算子の作り方がわかります.単項演算子を作には単に leftarg (左単項演算子用) か rightarg (右単項演算子用) のどちらかを省略するだけで す.十分な型情報を与えれば,使用すべき演算子をシステムが自動的に決定する ことができます. SELECT (a + b) AS c FROM test_complex; +----------------+ |c | +----------------+ |(5.2,6.05) | +----------------+ |(133.42,144.95) | +----------------+ 10. SQLの拡張: 集約関数(AGGREGATES) POSTGRES における集約関数(aggregate)は状態遷移関数(state transition functions)として表現されています.つまり,集約関数は,インスタンスが処理 される度に更新される状態として定義することができます.いくつかの状態関数 は新しい状態を計算する度にインスタンス中から特定の値を検索します(create aggregate 文の中の sfunc1 ).それに対して,他のものはその内部状態の記録 を保持しているだけです(sfunc2). sfunc1だけを使う集約関数を定義したとすると,各インスタンスからの項目値を 計算していく集約関数を定義したことになります."sum"(合計)はこの種の集約 関数の例です. "sum" は0から始めて,常にカレントインスタンスの値を現在の 合計に足していきます.このような加算を行なうには POSTGRES 組み込みの int4pl を使うことになるでしょう.(訳註:下の例は complex_add を使って複 素数の合計を求めています。) CREATE AGGREGATE complex_sum ( sfunc1 = complex_add, basetype = complex, stype1 = complex, initcond1 = '(0,0)' ); SELECT complex_sum(a) FROM test_complex; +------------+ |complex_sum | +------------+ |(34,53.9) | +------------+ sfunc2 だけを定義すると,各インスタンスの項目値に依らない計算をする集約 関数を指定することになります."count"(個数を求める)はこの種の集約関数 の典型的な例です."count" は0から始めて,インスタンス毎に1を現在の合計に 加えていきます.インスタンスの値は無視します.ここではこのような働きをさ せるために組み込みの int4inc ルーチンを使ってみましょう.このルーチンは その引数を増加(1を加える)させます. CREATE AGGREGATE my_count (sfunc2 = int4inc, -- 1を加える basetype = int4, stype2 = int4, initcond2 = '0') SELECT my_count(*) as emp_count from EMP; +----------+ |emp_count | +----------+ |5 | +----------+ "average"(平均)は合計を求める関数と個数を求める関数の両方が必要な集約関 数の例です.すべてのインスタンスの処理が終った時,集約関数の答として合計 を個数で割ります.上で用いた int4pl と int4inc と共に POSTGRES の整数除 算ルーチン int4dev を使って,合計を個数で割ることにしましょう. CREATE AGGREGATE my_average (sfunc1 = int4pl, -- 合計 basetype = int4, stype1 = int4, sfunc2 = int4inc, -- カウント stype2 = int4, finalfunc = int4div, -- 割算 initcond1 = '0', initcond2 = '0') SELECT my_average(salary) as emp_average FROM EMP; +------------+ |emp_average | +------------+ |1640 | +------------+ 11.索引への拡張のインターフェース ここまで説明された手続きによって,新しい型,新しい関数,新しい演算子を 定義できるようになりました.しかし,2次索引(Bツリー,Rツリーやハッ シュアクセスメソッド等)上に新しい型や演算子を定義することはまだできま せん. 図3を再び見てください.図の右半分(つまり pg_am, pg_amop, pg_amproc, pg_opclass)は,索引付のユーザ定義型やユーザ定義演算子の使用方法を POSTGRES に知らせるために,書き換えなければならないカタログを示してい ます.残念ながら,書き換えを行なう簡単なコマンドはありません.ここでは, 実例:整数を絶対値で昇順にソートするBツリー・アクセスメソッドのための 新しい演算子クラスを通して,どのようにしてこれらのカタログを修正するか を説明していきます. pg_am クラスは各ユーザ定義のアクセスメソッドにつき,1つのインスタンス を含みます.ヒープアクセスメソッドのためのサポートが POSTGRES に組み込 まれていますが,他のすべてのアクセスメソッドはここで説明されます.スキー マは以下の通りです: +-------------+---------------------------------------------------------------+ |amname | アクセスメソッドの名前 | +-------------+---------------------------------------------------------------+ |amowner | pg_user における所有者のインスタンスのオブジェクトID | +-------------+---------------------------------------------------------------+ |amkind | 現在は使われていないが,プレースホルダーとして'o'をセットする | +-------------+---------------------------------------------------------------+ |amstrategies | このアクセスメソッドの戦略の数(以下を参照) | +-------------+---------------------------------------------------------------+ |amsupport | このアクセスメソッドのサポートルーチン数(以下を参照) | +-------------+---------------------------------------------------------------+ |amgettuple | アクセスメソッドへのインターフェースルーチンの手続き識別子. | |aminsert | 例えば,ここで現れるアクセスメソッドからのオープン,クローズ,| |.... | ゲットインスタンスの regproc 識別子. | +-------------+---------------------------------------------------------------+ pg_am におけるインスタンスのオブジェクト ID は,他の多数のクラスの外部 キーとして使われます.このクラスへ新しいインスタンスを加える必要はあり ません.ここで関心があるのは,自分が拡張したいアクセスメソッドインスタ ンスのオブジェクト ID だけです. SELECT oid FROM pg_am WHERE amname = 'btree' +----+ |oid | +----+ |403 | +----+ amstrategies 属性は複数のデータ型にわたる,比較の標準として存在します. 例えば,Bツリーはキーに厳密な順序,「より小さい」,「より大きい」を組 み込みます.POSTGRES はユーザによる演算子定義を許しているので, POSTGRES は一つの演算子の名前(例えば,'>' や '<')に注目するわけには いかず,またそれがどんな種類の比較があるのかもわかりません.実際,いく つかのアクセスメソッドはいかなる順序を組み込むこともできません. 例えば,Rツリーは 矩形包含 (rectangle-containment) 関係を表現します. そこではハッシュされたデータ構造は,ハッシュ関数の値に基づくビットパター ンの類似性のみを表現するのです.POSTGRES は問い合わせの制限事項を取り 込み,演算子を見て,使える索引が存在するかどうかを決めるいくつかの首尾 一貫した方法を必要とします.このことは,例えば,<= 演算子や > 演算子が Bツリーを分割することを POSTGRES が知っている必要があることを示してい ます.POSTGRES は索引走査に使われる方法と演算子との間の関係を表現する 戦略を使います.新たな戦略の定義はここでの説明の範囲を越えていますが, 新しい演算子クラスを追加するために知る必要があると思われるので.ここで はBツリー戦略がどのように働くかを説明します.pg_am クラスにおいて, amstrategies 属性は,このアクセスメソッドのために定義された戦略の個数 です.Bツリーについては,この数は5です.これらの戦略は以下に相当しま す: +----------------------+---+ |より小さい | 1 | +----------------------+---+ |以下 | 2 | +----------------------+---+ |等しい | 3 | +----------------------+---+ |以上 | 4 | +----------------------+---+ |より大きい | 5 | +----------------------+---+ 方法としては,上記の比較に相当する手続きを pg_amop リレーションへ加え る必要がありそうです(以下を見てください).アクセスメソッドコードは, どのようにしてBツリーを分割するかを理解し,選択度を計算したり,その他 諸々のために,データ型を気にせずにこれらの戦略番号を使うことができます. 手続きの追加の詳細についてはまだ心配ありません.ただし,int2, int4, oid およびBツリーが操作できる他の全てのデータ型のために,これらの手続 きのセットが存在しなければならないことを理解してください. 時には戦略は,システムが索引の使い方を理解するための情報として十分では ないことがあります.あるアクセスメソッドでは,うまく動作するためには他 のサポートルーチンを必要とします.例えば,Bツリーアクセスメソッドは2 つのキーを比較し,一方が他方よりもより大きいか,等しいか,より小さいか を決定できなければなりません.同様に,Rツリー・アクセスメソッドは積や 和や長方形のサイズを計算できなければなりません.これらの操作はSQL 問い 合わせにおけるユーザ操作には相当しません.これらはアクセスメソッドによっ て内部的に使われる管理的ルーチンです. すべての POSTGRES アクセスメソッドに対する種々のサポートルーチンを首尾 一貫して管理するために,pg_am は amsupport と呼ばれる属性を持ちます. この属性はアクセスメソッドによって使われるサポートルーチンの個数を記録 しています.Bツリーでは,この数は1です -- このルーチンは2つのキーを 持ち,第1キーが第2キーと比べてより小さいか,等しいか,より大きいかに したがって -1, 0, +1 を返します.(注8) pg_am における amstrategies エントリーは,質問の中でアクセスメソッドの ために定義された戦略の個数です.より小さいか,以下か,等々の手続きは pg_am には現れません.同様に,amsupport はアクセスメソッドによって要求 されるサポートルーチンの個数です.実際のルーチンはどこか他のところでリ ストされます. 次に興味のあるクラスは pg_opclass です.このクラスは名前と oid とを関 連付けるためにのみ存在します.pg_amop において,全てのBツリー演算子ク ラスは,上の1から5までの手続きのセットを持ちます.実在するいくつかの opclass は,int2_ops, int4_ops, oid_ops です.pg_opclass には,自分の opclass 名(例えば,complex_abs_ops)を付けたインスタンスを加える必要 があります.このインスタンスの oid は他のクラスでは外部キーとなります. INSERT INTO pg_opclass (opcname) VALUES ('complex_abs_ops'); SELECT oid, opcname FROM pg_opclass WHERE opcname = 'complex_abs_ops'; +------+--------------+ |oid | opcname | +------+--------------+ |17314 | int4_abs_ops | +------+--------------+ ____________________ 注8 厳密に言えば,このルーチンは負数(<0), 0, 正数(>0)を返すことができます. 自分の pg_opclass インスタンスの oid は異なることに注意して下さい!. ここでの説明において現れる 17314 はすべて,自分の oid の値に置き換えて 考えてください.現時点で,アクセスメソッドと演算子クラスを持つに至りま した.しかしまだ,演算子のセットが必要です;演算子定義の手続きはこのマ ニュアルの前の方で述べられています.Bツリー上の complex_abs_ops 演算 子クラスのために必要となる演算子は以下の通りです: absolute value less-than absolute value less-than-or-equal absolute value equal absolute value greater-than-or-equal absolute value greater-than 定義された関数をインプリメントするコードは以下のファイルに格納されてい るとします. /usr/local/postgres95/src/tutorial/complex.c コードの一部は以下のようになります.(ここでは,種々の例の一つとして等 価演算子のみを紹介していることに注意してください.他の4つの演算子も非 常に似通っています.詳細については complex.c または complex.sql を参照 してください.) #define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y) bool complex_abs_eq(Complex *a, Complex *b) { double amag = Mag(a), bmag = Mag(b); return (amag==bmag); } 以下に2つのことがらは重要です. 第1に,int4 に対する,より小さい,以下,等しい,以上,より大きいの演 算子は既に定義されていることに注意してください.これらの演算子の全ては int4 に対して <, <=, =, >=, > という名前で定義されています.新しい演算 子は当然異なる動作をします.POSTGRES がこれら新しい演算子を古い演算子 として使わないことを保証するために,新しい演算子は古い演算子とは異なる 名前を付けられることが必要です.キーポイントはこうです:POSTGRES にお いて演算子をオーバーロードすることはできます.しかしそれは,その引数の 型に対してまだ演算子が定義されていない場合に限られるということです.つ まり,自分が(int4, int4) に対して < を定義していた場合,自分でそれを再 び定義することはできません.自分の演算子を定義したとき,POSTGRES はそ れをチェックしません.この問題を回避するために,演算子には風変わりな名 前を使うと良いでしょう.これを間違えれば,走査を実施しようとするときに, アクセスメソッドはクラッシュすることもあります. もうひとつの重要なポイントは,すべての演算子関数は論理値を返すというこ とです.アクセスメソッドはこの事実を当てにしています.(その一方で,サ ポート関数は個々のアクセスメソッドが期待するものならなんでも -- この場 合では符合付整数 -- を返します.)ファイルにおける最後のルーチンは, pg_am クラスの amsupport 属性を説明したときに,簡単に述べられた”サポー トルーチン”です.後ほどこれを使うことになりますが,ここでは割愛します. CREATE FUNCTION complex_abs_eq(complex, complex) RETURNS bool AS '/usr/local/postgres95/tutorial/obj/complex.so' LANGUAGE 'c'; さて,ここで演算子を定義します.既に注意したように,演算子名は2つの int4 オペランドを持つ全ての演算子に対して一意でなければなりません.下 にリストされている演算子名が使われているかどうかを見るために, pg_operater へ問い合わせを実行して,自分で付けた名前が望みの型であるか どうかを確認するとよいでしょう. /* * この問い合わせは,文字 & で終る3文字演算子名を見つけるために * 正規表現演算子(~)を使う */ SELECT * FROM pg_operator WHERE oprname ~ '^..&$'::text; ここで重要なものは,手続き(上で定義された C 関数)と制約,結合選択度 関数です.つまり,下で使われたものだけを使うべきです -- より小さい,等 しい,より大きいを示す文字に対して,種々の同様な関数があることに注意し てください.これらは与えられていなければなりません.さもなければアクセ スメソッドはその演算子を使おうとしたときにクラッシュするでしょう.制約 や結合の名前はコピーすべきですが,最後のステップで自分で定義した手続き 名は使うべきです. CREATE OPERATOR = ( leftarg = complex, rightarg = complex, procedure = complex_abs_eq, restrict = eqsel, join = eqjoinsel ) より小さい,以下,等しい,より大きい,以上に相当する5つの演算子が定義 されていることに注意してください.これでほとんど終わりです.最後に行な う必要のあることは pg_amop 関係を更新することです.これを行なうために は,以下の属性が必要です: +------------+------------------------+ |amopid | Bツリーのための pg_am | | | インスタンスの oid | | | (== 403, 上を参照) | +------------+------------------------+ |amopclaid | int4_abs_ops のための | | | pg_opclassインスタンス | | | の oid (== 17314 の代わ| | | りに得た値,上を参照) | +------------+------------------------+ |amopopr | opclass のための演算子 | | | の oid (すぐに出てくる)| +------------+------------------------+ |amopselect, | コスト関数 | |amopnpages | | +------------+------------------------+ 問い合わせ最適化部は,走査時に与えられた索引を使うかどうかを決定するた めにコスト関数を使います.幸い,これらは既に存在します.ここで使われる 2つの関数は,Bツリーの選択度を見積もる btreesel,および検索時にアク セスされるツリー中のページ数を見積もる btreenpageです.ここで,自分で 定義した演算子の oid が必要となります.2つの int4 を持つすべての演算 子の名前を調べ,自分のを取り出します: SELECT o.oid AS opoid, o.oprname INTO TABLE complex_ops_tmp FROM pg_operator o, pg_type t WHERE o.oprleft = t.oidand o.oprright = t.oid and t.typname = 'complex'; これは次を返します: +------+---------+ |oid | oprname | +------+---------+ |17321 | < | +------+---------+ |17322 | <= | +------+---------+ |17323 | = | +------+---------+ |17324 | >= | +------+---------+ |17325 | > | +------+---------+ (再び注意です.自分の oid 番号はきっとほとどんどが上図とは異なるでしょ う.)ここで関心がある演算子は 17321 から 17325 までの oid を持つ演算 子です.実際に自分が得る値は十中八九この値とは異なるでしょう.以下に出 てくる値では自分の値と置き換えて考えてください.これで演算子名を見るこ とができ,たった今加えた演算子名を取り出すことができるようになりました. これで,自分の新しい演算子クラスを持つ pg_amop に更新する準備ができま した.ここでの説明全体において最も重要なことは,pg_amop では演算子が以 下から以上まで順番づけられているということです.必要なインスタンスを加 えてみましょう: INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy, amopselect, amopnpages) SELECT am.oid, opcl.oid, c.opoid, 3, 'btreesel'::regproc, 'btreenpage'::regproc FROM pg_am am, pg_opclass opcl, complex_ops_tmp c WHERE amname = 'btree' and opcname = 'complex_abs_ops' and c.oprname = '='; Note the order: "less than" is 1, "less than or equal" is 2, "equal" is 3, "greater than or equal" is 4, and "greater than" is 5. 順番は,”より小さい”が1,”以下”が2,”等しい”が3,”以上”が4,” より大きい”が5であることに注目して下さい. 最後のステップ(ついに!)は pg_am についての説明において以前に触れた” サポートルーチン”の登録です.このサポートルーチンの oid は pg_amproc に格納され,アクセスメソッド oid および演算子クラス oid によってキーが 付けられています.まず最初に,POSTGRES の中の関数の登録が必要です. (演算子ルーチンをインプリメントしたファイルの最後に,このルーチンをイ ンプリメントした C コードを置いたことを思い出してください.) CREATE FUNCTION int4_abs_cmp(int4, int4) RETURNS int4 AS '/usr/local/postgres95/tutorial/obj/complex.so' LANGUAGE 'c'; SELECT oid, proname FROM pg_proc WHERE prname = 'int4_abs_cmp'; +------+--------------+ |oid | proname | +------+--------------+ |17328 | int4_abs_cmp | +------+--------------+ (再び注意です.自分の oid 番号は十中八九この値とは異なるでしょう.以 下に出てくる値では自分の値と置き換えて考えてください.)Bツリーインス タンスの oid は 403 で,int4_abs_ops の oid は 17314 であることを思い 出して,新しいインスタンスを以下のように加えるとよいでしょう: INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) VALUES ('403'::oid, -- btree oid '17314'::oid, -- pg_opclass tuple '17328'::oid, -- new pg_proc oid '1'::int2); 12. LIBPQ LIBPQ は POSTGRES のアプリケーション・プログラミング・インターフェース です.LIBPQ は,クライアント・プログラムが POSTGRES のバックエンド・サー バに問い合わせをし, その問い合わせの結果を受け取るためのライブラリ・ ルーチンのセットです.この章では C インターフェース・ライブラリについ て説明します. LIBPQ を使用してプログラムを記述する際の例として, この セクションの最後に3つの簡単なプログラムを提示してあります.LIBPQ アプ リケーションのいくつかの例が以下のディレクトリの下にあります; ../src/test/regress ../src/test/examples ../src/bin/psql LIBPQ を使用するフロントエンド・プログラムは,ヘッダーファイル libpq-fe.h をインクルードし,libpq ライブラリーとリンクしなければなり ません. 12.1. 制御と初期化 以下の環境変数を使用することで,アプリケーション・プログラムにデータベー ス名をハードコーディングすることなく,デフォルトの環境の値を設定するこ とができます: * PGHOST では,デフォルトのサーバ名を設定します. * PGOPTIONS では,POSTGRES のバックエンドに対する, 追加のランタイム・オプション を設定します. * PGPORT では, POSTGRES のバックエンドと通信するためのデフォルト・ポートを設定 します. * PGTTY では,バックエンド・サーバーが表示するデバッグ・メッセージを表示する ファイルまたは tty を設定します. * PGDATABASE では,デフォルトの POSTGRES データベース名を設定します. * PGREALM では, POSTGRES と一緒に使用する Kerberos realm を設定します (ローカル realm と異なる場合). PGREALM が設定されている場合, POSTGRES アプリケーションは, サーバにその realm の認証を行ない, そして ローカル・チケット・ファイルとの競合を避けるため, 異なるチケット・ ファイルを使用します. この環境変数は Kerberos 認証が有効な場合にのみ 使用できます. 12.2. データベース接続関数 C プログラムからバックエンドへの接続を行なうために以下のルーチンを使用します. PQsetdb バックエンドと新たに接続します. PGconn *PQsetdb(char *pghost, char *pgport, char *pgoptions, char *pgtty, char *dbName); いずれかの引数がヌルの場合, 対応する環境変数をチェックします. もし, 環境変数も設定されていない場合, ハードコーディングされているデフォル トの値が使用されます.PQsetdb は常に有効な PGconn ポインタを返します. PQStatus (以下参照)コマンドは接続による照会を行なう前に, その接続が正 しく行なわれたことを保証するために呼び出されるべきです. LIBPQ プログ ラマは, PGconn 抽象化の保守を注意深く行なうべきです. PGConn の内容を 取得するために,以下の附属関数を使用してください.将来変更される可能性 のある, PGconn 構造体のフィールドを直接参照することは避けてください. PQdb は,接続したデータベース名を返します. char *PQdb(PGconn *conn) PQhost は,接続したホスト名を返します. char *PQhost(PGconn *conn) PQoptions は,接続で使用している pgoptions を返します. char *PQoptions(PGconn *conn) PQport は,接続している pgport を返します. char *PQport(PGconn *conn) PQtty は,接続している pgtty を返します. char *PQtty(PGconn *conn) PQstatus は,接続ステータスを返します.ステータスは CONNECTION_OK または CONNECTION_BAD のいずれかになります. ConnStatusType *PQstatus(PGconn *conn) PQerrorMessage は,接続に関係するエラーメッセージを返します. char *PQerrorMessage(PGconn* conn); PQfinish バックエンドとの接続を終了します.PGconn 構造体によって使用されていた メモリも解放します.PQfinish を呼び出した後は,PGconn ポインタを使用し ないようにするべきです. void PQfinish(PGconn *conn) PQreset バックエンドとの通信ポートをリセットします.この関数は,バックエンドとの IPC ソケット接続を終了し,いずれかのバックエンドとの新しい接続の再確立を 試みます. void PQreset(PGconn *conn) PQtrace フロントエンドとバックエンドのメッセージの受渡しのトレースを有効にします. メッセージは,デバックポートのファイル・ストリームにエコーされます. void PQtrace(PGconn *conn, FILE* debug_port); PQuntrace フロントエンドとバックエンドのメッセージの受渡しのトレースを無効にします. void PQuntrace(PGconn *conn); 12.3. 問い合わせ実行関数 PQexec POSTGRES への問い合わせを行ないます.問い合わせが成功した場合には PGresultポインタが返され,そうでなければヌルが返されます.ヌルが返され, エラーに関する詳細な情報を得たい場合には,PQerrorMessage を使用するこ とができます. PGresult *PQexec(PGconn *conn, char *query); PGresult 構造体では,バックエンドから返される問い合わせ結果をカプセル 化します.LIBPQ プログラマは,PGresult 抽象化の保守を注意深く行なうべき です.問い合わせ結果を取り出すためには,以下の附属関数を使用してくださ い.将来変更される可能性のある,PGresult 構造体のフィールドを直接参照 することは避けてください. PQresultStatus 問い合わせ結果のステータスを返します.PQresultStatus は,以下の値のうちの いずれか1つを返します: PGRES_EMPTY_QUERY, PGRES_COMMAND_OK, /* the query was a command */ PGRES_TUPLES_OK, /* the query successfully returned tuples */ PGRES_COPY_OUT, PGRES_COPY_IN, PGRES_BAD_RESPONSE, /* an unexpected response was received */ PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR 結果のステータスが PGRES_TUPLES_OK の場合,問い合わせによって返された タップルを取り出すために以下のルーチンを使用できます. PQntuples は,問い合わせ結果のタップル(インスタンス) の数を返します. int PQntuples(PGresult *res); PQnfields は,問い合わせ結果のフィールド(属性)の数を返します. int PQnfields(PGresult *res); PQfname は,与えられたフィールド(属性)の索引(field index)と関連するフィー ルド(属性)の名前を返します.フィールド・インディケータは,0 から開始さ れます. char *PQfname(PGresult *res, int field_index); PQfnumber は,与えられたフィールド(属性)の名に関連するフィールド(属性) の索引を返します. int PQfnumber(PGresult *res, char* field_name); PQftype は,与えられたフィールド索引(field_num)に関連するフィールドの 型を返します. 内部コーディングされている型が整数で返されます.フィー ルド・インディケータは,0 から開始されます. Oid PQftype(PGresult *res, int field_num); PQfsize は,与えられたフィールド索引(field_index)と関連するフィールド のサイズをバイト数で返します.返されるサイズが -1 の場合,フィールドは 可変長フィールドです. フィールド・インディケータは,0 から開始されま す. int2 PQfsize(PGresult *res, int field_index); PQgetvalue は,フィールド(属性)の値を返します.ほとんどの問い合わせに 対して,PQgetvalueによって返される値は,属性の値をヌルで終る ASCII 文 字列で表現されます.問い合わせの結果がバイナリ・カーソルであった場合, PQgetvalueによって返される値は,バックエンド・サーバの内部フォーマット の種類のバイナリで表現されます.データを正しい C のタイプにキャストし たり,変換したりするのはプログラマの責任です.PQgetvalue によって返さ れる値は,PGresult 構造体の一部である記憶領域を指します.PGresult 構造 体自身のライフタイムの中で使用されていた場合,その値を明示的に他の記憶 領域へコピーしなければいけません. char* PQgetvalue(PGresult *res, int tup_num, int field_num); PQgetlength は,フィールド(属性)の長さをバイトで返します.フィールドが varlena 構造体である場合,ここへ返される長さには varlena のフィールド・ サイズは含まれません.すなわち,4 バイト少ないということです. int PQgetlength(PGresult *res, int tup_num, int field_num); PQcmdStatus 最後の問い合わせコマンドに関連するコマンド・ステータスを返します. char *PQcmdStatus(PGresult *res); PQoidStatus 最後の問い合わせが INSERT コマンドであった場合,インサートされたタップ ルのオブジェクト id の文字列を返します.そうでない場合には,空の文字列 を返します. char* PQoidStatus(PGresult *res); PQdisplayTuples すべてのタップルをプリントし,また必要に応じて,特定の出力ストリームに 対する属性名もプリントします.psql と monitor の両方のプログラムで,出 力のためにPQdisplayTuples を使用します. void PQdisplayTuples( PGresult* res, FILE* fout, /* output stream */ int fillAlign, /* pad the fields with spaces? */ char *fieldSep, /* string to use as the field separator */ int printHeader, /* display attribute headers */ int quiet, /* print the number of rows returned ? */ ); PQclear PGresult に関連する記憶領域を解放します.それ以上使用しないのであれば, 問い合わせ結果ごとに適切に解放するようにするべきです.そのようにしない とフロント・アプリケーションのメモリがリークしてしまうことになります. void PQclear(PQresult *res); 12.4. ファースト・パス POSTGRES は,バックエンドに関数呼び出しを送るためのファースト・パス・イ ンターフェースを提供します.これはシステム内部に対するトラップドアで潜 在的なセキュリティ・ホールとなる場合があります.ほとんどのユーザは,こ の機能を使用する必要は生じないでしょう. PGresult* PQfn(PGconn* conn, int fnid, int *result_buf, int *result_len, int result_is_int, PQArgBlock *args, int nargs); fnid 変数は,実行される関数のオブジェクト識別子です.result_buf は,返 り値をロードするバッファです.呼び出し側は,返り値を保存するために十分 な領域を割り当てておかなければなりません.結果の長さは,result_len に よってポイントされる記憶領域に返されます.結果が整数値の場合には result_is_int には 1 以上の値が設定されているべきであり,それ以外の場 合には 0 が設定されるているべきです.args と nargs は,この関数に対す る引数です. typedef struct { int len; int isint; union { int *ptr; int integer; } u; } PQArgBlock; PQfn は常に有効な PGresult* を返します.resultStatus は,その結果を使 用する前に検査するようにするべきです.呼び出し側が,それ以上必要のない PGresult をPQclear で解放する責任を負います. 12.5. 非同期通知 POSTGRES は LISTEN コマンドと NOTIFY コマンドによる非同期通知をサポー トします. バックエンドは,LISTEN コマンドにより関連する特定のリレーショ ンを登録します.すべてのバックエンドは特定の関係についてリスニングし, 他のバックエンドによってその関係名のNOTIFY が実行された場合,非同期に 通知されます.通知側からリスナーへ渡される追加情報はありません.従って 通常は,通信を必要とするすべての実際のデータが関係によって転送されます. LIBPQ アプリケーションは,接続されたバックエンドが非同期通知を受け取る ごとに通知を受けます.しかし,バックエンドからフロントエンドへの通信は 非同期ではありません.通知は他の問い合わせ上にピギーバックされて受け取 ります.従って,アプリケーションは,バックエンド通知を受け取るためには, 例え結果が空だとしても問い合わせを行わなければなりません.実際には, LIBPQ アプリケーションは未処理の通知情報を確認するために,バックエンド にポールしなければなりません.問い合わせ実行後,フロントエンドはバック エンドから何らかの通知データが利用できる状態にあるかを確認するために PQNotifies を呼び出しても構いません. PQNotifies バックエンドからの未処理の通知リストから通知を返します.バッ クエンドからの未処理の通知が無い場合にはヌルが返されます.PQNotifies はスタックから(通知を)取り除く(ポップする)ように動作します.一度通知が PQNotifies から返されれば,処理されたとみなされ,通知のリストから取り 除かれます. PGnotify* PQNotifies(PGconn *conn); 2 番目のサンプル・プログラムは,非同期通知の使用の例です. 12.6. COPY コマンドに関係する関数 POSTGRES の copy コマンドには,LIBPQ によって使用されるネットワーク接 続からの読み取り,ネットワーク接続への書き込みのためのオプションがあり ます. そのため,関数はこのネットワーク接続に直接アクセスする必要があ るので,アプリケーションでもこの機能を大いに利用することになります. PQgetline (バックエンド・サーバから)大きさ length のバッファ stringへ(渡された) 文字列の改行コードで終る行を読み取ります.fgets(3) と同様に,このルーチ ンは length-1 文字を string へコピーします.しかしながら gets(3) と同 様に,終端行をヌルへ変換します.PQgetline は,EOF の場合には EOF を, 行全体を読み取った場合には 0 を,バッファがいっぱいではあるのに,まだ 行末改行コードを読み取っていない場合には 1 を返します.参照するために は,アプリケーションはバックエンド・サーバが copy コマンドの結果の送信 を完了したことを示す単一文字列 "." が新規行にあるかどうかを検査しなけ ればならないことに注意してください. そのため,アプリケーション今まで length-1 文字の長さ以上の行を受け取ることを期待していた場合には, PQgetline の返り値を慎重に検査し確かめなければなりません. 以下のコード ../src/bin/psql/psql.c は, copy プロトコルを正しく扱うルーチンを含んでいます. PQputline ヌルで終る文字列をバックエンド・サーバへ送信します. アプリ ケーションは,データの送信が完了したことをバックエンドに示すために,単一 文字 "." を明示的に送信しなければなりません. void PQputline(PGconn *conn, char *string); PQendcopy バックエンドと同期をとります.この関数は,バックエンドが copy を完了す るまで待ちます.PQputline を使用してバックエンドに最終文字列を送信した 場合や PGgetlineを使用してバックエンドから最終文字列を受け取った場合の いずれかに発行するべきものです.それが発行されなければ,バックエンドが フロントエンドから "out of sync"を受け取ってしまいます.バックエンドが 次の問い合わせを受け取る用意ができているかは,この関数からの返り値によ ります. 返り値が 0 の場合はうまく完了したことになり,0 以外はそうでは ありません. int PQendcopy(PGconn *conn); 例: PQexec(conn, "create table foo (a int4, b char16, d float8)"); PQexec(conn, "copy foo from stdin"); PQputline(conn, "3hello world4.5\n"); PQputline(conn,"4goodbye world7.11\n"); ... PQputline(conn,"\\.\n"); /* [注]参照 */ PQendcopy(conn); [訳注: オリジナルでは "PQputline(conn,".\n");" となっていますが、 少なくともこれでは PostgreSQL 6.1 では動作しません。] 12.7. LIBPQ トレース関数 PQtrace ファイル・ストリームのデバッグを行なうために,フロントエンド/バックエンド の通信のトレースを有効にします. void PQtrace(PGconn *conn FILE *debug_port) PQuntrace PQtrace によって開始したトレースを無効にします. void PQuntrace(PGconn *conn) 12.8. ユーザ認証関数 もしユーザが適切な認証証明書(例えば, Kerberos チケットの入手)を生成で きるのであれば,フロントエンド/バックエンドの認証処理は他に何も仲介を 必要とせずに PQexec によって扱われます.認証手続きの動作に適合するよう に LIBPQ プログラムによって,以下のルーチンが呼び出されます. fe_getauthname ユーザの認証をした何らかの名前を含む静的領域を示すポインタを返します. アプリケーションの getenv(3) や getpwuid(3) を呼び出すような場所ではこ のルーチンを使用することを強く推奨します. なぜなら,認証ユーザ名が USER 環境変数の値や /etc/passwd にエントリされているユーザと全く異なる ことも可能だからです. fe_setauthsvc LIBPQ がコンパイル時のデフォルト以外の認証サービス名を使うべきことを指 定します.典型的にこの値は,コマンドラインと切替えによって得られます. void fe_setauthsvc(char *name, char* errorMessage) いかなる認証の試みからのエラー・メッセージも, errorMessage 引数のなか に返されます. 問い合わせバッファの長さは 8192 バイトで,その長さを越えた問い合わせは 自動的に切り捨てられます. 12.10. サンプル・プログラム 12.10.1. サンプル・プログラム 1 /* * testlibpq.c * POSTGRES のフロントエンド・ライブラリ LIBPQ の C バージョンのテスト * * */ include include "libpq-fe.h" void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); } main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int nFields; int i,j; /* FILE *debug; */ PGconn* conn; PGresult* res; /* もしパラメータがヌルの場合は,バックエンド接続のためにパラメータを セットすることからはじめる,そして,システムは理にかなったデフォルトを 環境変数から捜し,失敗するとハードコーディングされた定数を使う. */ pghost = NULL; /* host name of the backend server */ /* バックエンド・サーバのホスト名 */ pgport = NULL; /* port of the backend server */ /* バックエンド・サーバのポート */ pgoptions = NULL; /* special options to start up the backend server */ /* バックエンド・サーバを起動するときの特別なオプション */ pgtty = NULL; /* debugging tty for the backend server */ /* バックエンド・サーバのデバッグをする端末 */ dbName = "template1"; /* データベースに接続をはる */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); /* バックエンド接続が確立したことを確認する */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,"Connection to database '%s' failed.0, dbName); fprintf(stderr,"%s",PQerrorMessage(conn)); exit_nicely(conn); } /* debug = fopen("/tmp/trace.out","w"); */ /* PQtrace(conn, debug); */ /* トランザクション・ブロックの開始 */ res = PQexec(conn,"BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,"BEGIN command failed0); PQclear(res); exit_nicely(conn); } /* メモリー・リークを避ける為に不要となったら PGresult を PQclean すべき */ PQclear(res); /* データベースのシステム・カタログ pg_database からインスタンスを フェッチする */ res = PQexec(conn,"DECLARE myportal CURSOR FOR select * from pg_database"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,"DECLARE CURSOR command failed0); PQclear(res); exit_nicely(conn); } PQclear(res); res = PQexec(conn,"FETCH ALL in myportal"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,"FETCH ALL command didn't return tuples properly0); PQclear(res); exit_nicely(conn); } /* 最初に,属性名を印刷する */ nFields = PQnfields(res); for (i=0; i < nFields; i++) { printf("%-15s",PQfname(res,i)); } printf("0); /* 次に,インスタンスを印刷する */ for (i=0; i < PQntuples(res); i++) { for (j=0 ; j < nFields; j++) { printf("%-15s", PQgetvalue(res,i,j)); } printf("0); } PQclear(res); /* 入口を閉じる */ res = PQexec(conn, "CLOSE myportal"); PQclear(res); /* トランザクションを終る */ res = PQexec(conn, "END"); PQclear(res); /* データベースへの接続を閉じて奇麗にする */ PQfinish(conn); /* fclose(debug); */ } 12.10.2. サンプル・プログラム 2 /* * testlibpq2.c * 非同期通知インターフェースのテスト * 以下のようにデータベースを配置する CREATE TABLE TBL1 (i int4); CREATE TABLE TBL2 (i int4); CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2]; * そして,このプログラムを起動する * プログラムが始まったら,これを行なう INSERT INTO TBL1 values (10); * * */ include include "libpq-fe.h" void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); } main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int nFields; int i,j; PGconn* conn; PGresult* res; PGnotify* notify; /* もしパラメータがヌルの場合は,バックエンド接続のためにパラメータを セットすることからはじめる,そして,システムは理にかなったデフォルトを 環境変数から捜し,失敗するとハードコーディングされた定数を使う. */ pghost = NULL; /* host name of the backend server */ /* バックエンド・サーバのホスト名 */ pgport = NULL; /* port of the backend server */ /* バックエンド・サーバのポート */ pgoptions = NULL; /* special options to start up the backend server */ /* バックエンド・サーバを起動するときの特別なオプション */ pgtty = NULL; /* debugging tty for the backend server */ /* バックエンド・サーバのデバッグをする端末 */ dbName = getenv("USER"); /* change this to the name of your test database*/ /* ここをテストに使うデータベース名に変更する */ /* データベースに接続をはる */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); /* バックエンド接続が確立したことを確認する */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,"Connection to database '%s' failed.0, dbName); fprintf(stderr,"%s",PQerrorMessage(conn)); exit_nicely(conn); } res = PQexec(conn, "LISTEN TBL2"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,"LISTEN command failed0); PQclear(res); exit_nicely(conn); } /* メモリー・リークを避ける為に不要となったら PGresult を PQclean すべき */ PQclear(res); while (1) { /* 非同期通知は問い合わせの結果としてのみ返って来る */ /* 空っぽの問い合わせを送ることができる */ res = PQexec(conn, " "); /* printf("res->status = %s0, pgresStatus[PQresultStatus(res)]); */ /* 非同期の戻りの為の確認 */ notify = PQnotifies(conn); if (notify) { fprintf(stderr, "ASYNC NOTIFY of '%s' from backend pid '%d' received0, notify->relname, notify->be_pid); free(notify); break; } PQclear(res); } /* データベースへの接続を閉じて奇麗にする */ PQfinish(conn); } 12.10.3. サンプル・プログラム 3 /* * testlibpq3.c * POSTGRES のフロントエンド・ライブラリ LIBPQ の C バージョンのテスト * バイナリ・カーソル・インターフェースをテスト * * 以下のようにデータベースを配置する CREATE TABLE test1 (i int4, d float4, p polygon); INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon); INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon); 期待される出力は: tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4 bytes) 2 points boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes) 89.050003, p = (4 bytes) 2 points boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000) * */ include include "libpq-fe.h" include "utils/geo-decls.h" /* for the POLYGON type */ void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); } main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int nFields; int i,j; int i_fnum, d_fnum, p_fnum; PGconn* conn; PGresult* res; /* もしパラメータがヌルの場合は,バックエンド接続のためにパラメータを セットすることからはじめる,そして,システムは理にかなったデフォルトを 環境変数から捜し,失敗するとハードコーディングされた定数を使う. */ pghost = NULL; /* host name of the backend server */ /* バックエンド・サーバのホスト名 */ pgport = NULL; /* port of the backend server */ /* バックエンド・サーバのポート */ pgoptions = NULL; /* special options to start up the backend server * / /* バックエンド・サーバを起動するときの特別なオプション */ pgtty = NULL; /* debugging tty for the backend server */ /* バックエンド・サーバのデバッグをする端末 */ dbName = getenv("USER"); /* change this to the name of your test data base*/ /* ここをテストに使うデータベース名に変更する */ /* データベースに接続をはる */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); /* バックエンド接続が確立したことを確認する */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,"Connection to database '%s' failed.0, dbName); fprintf(stderr,"%s",PQerrorMessage(conn)); exit_nicely(conn); } /* start a transaction block */ /* トランザクション・ブロックの開始 */ res = PQexec(conn,"BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,"BEGIN command failed0); PQclear(res); exit_nicely(conn); } /* メモリー・リークを避ける為に不要となったら PGresult を PQclean すべき */ PQclear(res); /* データベースのシステム・カタログ pg_database からインスタンスを フェッチする */ res = PQexec(conn,"DECLARE mycursor BINARY CURSOR FOR select * from t est1"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,"DECLARE CURSOR command failed0); PQclear(res); exit_nicely(conn); } PQclear(res); res = PQexec(conn,"FETCH ALL in mycursor"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,"FETCH ALL command didn't return tuples properly0); PQclear(res); exit_nicely(conn); } i_fnum = PQfnumber(res,"i"); d_fnum = PQfnumber(res,"d"); p_fnum = PQfnumber(res,"p"); for (i=0;i<3;i++) { printf("type[%d] = %d, size[%d] = %d0, i, PQftype(res,i), i, PQfsize(res,i)); } for (i=0; i < PQntuples(res); i++) { int *ival; float *dval; int plen; POLYGON* pval; /* 知る限りの3つのフィールドを変数に結びつける */ ival = (int*)PQgetvalue(res,i,i_fnum); dval = (float*)PQgetvalue(res,i,d_fnum); plen = PQgetlength(res,i,p_fnum); VARHDSZ*/ /* plen には length フィールドが含まれないので VARHDSZ 増分する */ pval = (POLYGON*) malloc(plen + VARHDRSZ); pval->size = plen; memmove((char*)&pval->npts, PQgetvalue(res,i,p_fnum), plen); printf("tuple %d: got0, i); printf(" i = (%d bytes) %d,0, PQgetlength(res,i,i_fnum), *ival); printf(" d = (%d bytes) %f,0, PQgetlength(res,i,d_fnum), *dval); printf(" p = (%d bytes) %d points boundbox = (hi=%f/%f, lo = %f,%f) 0, PQgetlength(res,i,d_fnum), pval->npts, pval->boundbox.xh, pval->boundbox.yh, pval->boundbox.xl, pval->boundbox.yl); } PQclear(res); /* 入口を閉じる */ res = PQexec(conn, "CLOSE mycursor"); PQclear(res); /* トランザクションを終る */ res = PQexec(conn, "END"); PQclear(res); /* データベースへの接続を閉じて奇麗にする */ PQfinish(conn); } 13. 巨大オブジェクトの取り扱いについて POSTGRESでは,データの値はタップルに蓄えられ,個々のタップルは複数のデータ ページに跨ることは出来ません.一つのデータページのサイズは8192バイトです から,データ値サイズの上限は比較的小さいのです.より大きい単一のデータをサ ポートするために,POSTGRESは巨大データのインターフェイスを提供しています. このインターフェイスは,巨大データとして宣言されたユーザデータに対してファ イル指向のアクセスをとることにより提供しています.この節ではPOSTGRESの巨 大データの実装と問い合わせ言語に対するインターフェイスについて解説してい ます. ------- 13.1 歴史的注意事項 もともとPOSTGRES 4.2 では 巨大データオブジェクトは,以下の3つの標準的な 手法をもってサポートされていました:POSTGRESの外にあるファイルとして, POSTGRESにより管理されたUNIXのファイルとして,そして,POSTGRESのデータベー スの中に蓄えられたデータとして,です.このことはユーザの間で,少なからぬ 混乱を招きました.結果的にPOSTGRES95では,POSTGRESデータベースの中に巨大 オブジェクトを入れる方法のみをサポートすることにしました.これはデータに アクセスするのは遅いのですが,より厳密なデータ一貫性とタイムトラベルをも たらします.歴史的理由により,これらは転置巨大データと呼ばれます.(この 章では転置と巨大データという語を同じものを意味するものとして,交換的に用 いることとします.) ------- 13.2 転置巨大オブジェクト 転置巨大オブジェクトの実装では巨大オブジェクトを「塊」に分け,それらをデー タベースのタップルの中に蓄えます.B-木による索引は,ランダムな書き込みや読 み出しをする際,正しい塊への迅速な検索を保証しています. ------- 13.3 巨大オブジェクトインターフェイス POSTGRESが提供する巨大オブジェクトへのアクセスの仕組み,ユーザ定義の関数の 一部としてのバックエンドと,インターフェイスを用いたアプリケーションの一 部としてのフロントエンド,の両方は,以下に述べられています.POSTGRES4.2 に親しんでいるユーザのために,POSTGRES95はより一貫性のあるインターフェイ スを提供する新しい関数群を持っています.このインターフェイスは動的にロー ドされる C言語で書かれた関数と同じものです. ------- POSTGRESの巨大オブジェクトへのインターフェイスはUNIXのファイルシステム インターフェイスに習ってモデル化されましたので,open(2)やread(2)や write(2)やlseek(2)に類似しています.ユーザの関数は巨大オブジェクトから 興味のある部分だけを取り出すためにこれらの関数を呼び出します.例えば, 顔写真(magshot)と呼ばれる巨大オブジェクトの型があって顔の写真を蓄えて いるとして,そこで,「鬚(beard)」と呼ばれる関数がこの magshot データで 宣言時によばれます.beard は,写真の下三分の一を見ることが出来て,そこ に現れている鬚の色を,もし鬚があればですが,判定することが出来ます.巨 大オブジェクトのすべてのデータがバッファに読み込まれる必要はなく,また, beard 関数に調査されることすらありません.巨大オブジェクトは動的にロー ドされるC言語の関数もしくはライブラリをリンクするデータベースのクライ アントプログラムから,アクセスすることが出来ます.POSTGRESは,巨大オブ ジェクトを,開いたり,読んだり,書き込んだり,閉じたり,探索したりする ルーチン群を提供しています. ------- 13.3.1 巨大オブジェクトの作成 以下のルーチンは新しい巨大オブジェクトを作成します. Oid lo_creat(PGconn *conn, int mode) modeは新しいオブジェクトのいくつかの異なった属性を示すビットマスクです. これらの定数は以下のファイルで定義されています. /usr/local/postgres95/src/backend/libpq/libpq-fs.h アクセスの型(読み込み,書き込み,もしくは両方)は,INV_READと, INV_WRITEとのビットの論理和をとることにより制御されます.もし巨大オブ ジェクトがアーカイブされていたとすると,つまり,もしそのオブジェクトの 過去のバージョンが定期的に特別なアーカイブのリレーションに移動されてい たとすると,INV_ARCHIVE ビットがセットされるはずです.マスクの下位の16 ビットは,そのオブジェクトが存在すべき記憶管理番号です.バークレー以外 のサイトではこれらのビットは常にゼロのはずです. 以下のコマンドは(転置)巨大オブジェクトを作成します. inv_oid = lo_creat(INV_READ|INV_WRITE|INV_ARCHIVE); ------- 13.3.2 巨大オブジェクトのインポート UNIXのファイルを巨大オブジェクトとしてインポートするためには以下のルーチ ンを呼び出します. Oid lo_import(PGconn *conn, text *filename) 引数filenameは,巨大オブジェクトとしてインポートされるファイルのパス名を 指定します. ------- 13.3.3 巨大オブジェクトのエクスポート 巨大オブジェクトをUNIXのファイルにエキスポートするには以下のルーチンを呼 び出します. int lo_export(PGconn *conn, Oid lobjId, text *filename) 引数lobjIdはエキスポートする巨大オブジェクトのOidを指定し,引数filename はファイルのパス名を指定します. ------- 13.3.4 存在する巨大オブジェクトのオープン 既に存在する巨大オブジェクトをオープンするためには,以下のルーチンを呼び出し ます. int lo_open(PGconn *conn, Oid lobjId, int mode, ...) 引数lobjIdはオープンする巨大オブジェクトのOidを指定します. modeのビット はそのオブジェクトが読み込みのためにオープンされるのか,書き出しか,もし くは両方か,を制御します. 巨大オブジェクトは作成される前にオープンされることは出来ません.lo_open は以降の lo_read lo_write lo_lseek lo_tell lo_close などのルーチンでの使 用のために巨大オブジェクト記述子を返します. ------- 13.3.5 巨大オブジェクトへのデータの書き出し 以下のルーチンはbufから,lenバイトのデータを巨大オブジェクトfdに書き出します. int lo_write(PGconn *conn, int fd, char *buf, int len) 引数fdは以前のlo_openにより返された値でなくてはなりません.このルーチンは実 際に書き出されたデータのバイト数を返します.エラーの際には負の値が返され ます. ------- 13.3.6 巨大オブジェクト上の探索 巨大オブジェクト上の読み込みもしくは書き出しの現在位置を変更するためには 以下のルーチンを呼び出します. int lo_lseek(PGconn *conn, int fd, int offset, int whence) このルーチンはfdによって示された巨大オブジェクトの現在位置のポインタを offsetにより示された値だけ移動します.whenceに対する有効な値は, SEEK_SET,SEEK_CUR,および SEEK_END です. ------- 13.3.7 巨大オブジェクト記述子を閉じる (巨大オブジェクトを閉じる) 巨大オブジェクトは以下のルーチンを呼ぶことによって閉じられます int lo_close(PGconn *conn, int fd) ここで,fdはlo_openによって返された巨大オブジェクト記述子です.成功なら, lo_closeはゼロを返します.エラー時には負の値を返します. ------- 13.4 組み込み予約関数 lo_importと,lo_exportという組み込みの予約関数があります,これらはSQLの 問い合わせの中で用いるのに便利です. これらの関数の用例を示します. CREATE TABLE image ( name text, raster oid ); INSERT INTO image (name, raster) VALUES ('beautiful image', lo_import('/etc/motd')); SELECT lo_export(image.raster, "/tmp/motd") from image WHERE name = 'beautiful image'; ------- 13.5 LIBPQからの巨大オブジェクトへのアクセス 以下はLIBPQにある巨大オブジェクトへのインターフェイスの使い方を示すサンプ ルプログラムです.プログラムの一部分はコメントアウトされていますが,読者 の参考のために残してあります.このプログラムは ../src/test/examples の下にあります.LIBPQの巨大オブジェクトへのインターフェイスを使うフロ ントエンド・アプリケーションはヘッダファイルlibpq/libpq-fs.hをインクルー ドし,ライブラリlibpqをリンクしている必要があります. ------- 13.6. プログラム例 /*------------------------------------------------------------------------- * * testlo.c-- * libpq で巨大オブジェクトを使うテスト * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * /usr/local/devel/pglite/cvs/src/doc/manual.me,v 1.16 1995/09/01 23:55:00 jolly Exp * *------------------------------------------------------------------------- */ #include #include "libpq-fe.h" #include "libpq/libpq-fs.h" #define BUFSIZE 1024 /* * importFile - * ファイル "in_filename" を巨大オブジェクト "lobjOid" としてデータベースに移入する * */ Oid importFile(PGconn *conn, char *filename) { Oid lobjId; int lobj_fd; char buf[BUFSIZE]; int nbytes, tmp; int fd; /* * 読み込むファイルを開く */ fd = open(filename, O_RDONLY, 0666); if (fd < 0) { /* error */ fprintf(stderr, "can't open unix file } /* * 巨大オブジェクトを創る */ lobjId = lo_creat(conn, INV_READ|INV_WRITE); if (lobjId == 0) { fprintf(stderr, "can't create large object"); } lobj_fd = lo_open(conn, lobjId, INV_WRITE); /* * Unix のファイルから読み込み、転置ファイルに書く */ while ((nbytes = read(fd, buf, BUFSIZE)) > 0) { tmp = lo_write(conn, lobj_fd, buf, nbytes); if (tmp < nbytes) { fprintf(stderr, "error while reading } } (void) close(fd); (void) lo_close(conn, lobj_fd); return lobjId; } void pickout(PGconn *conn, Oid lobjId, int start, int len) { int lobj_fd; char* buf; int nbytes; int nread; lobj_fd = lo_open(conn, lobjId, INV_READ); if (lobj_fd < 0) { fprintf(stderr,"can't open large object %d", lobjId); } lo_lseek(conn, lobj_fd, start, SEEK_SET); buf = malloc(len+1); nread = 0; while (len - nread > 0) { nbytes = lo_read(conn, lobj_fd, buf, len - nread); buf[nbytes] = ' '; fprintf(stderr,">>> %s", buf); nread += nbytes; } fprintf(stderr,"0); lo_close(conn, lobj_fd); } void overwrite(PGconn *conn, Oid lobjId, int start, int len) { int lobj_fd; char* buf; int nbytes; int nwritten; int i; lobj_fd = lo_open(conn, lobjId, INV_READ); if (lobj_fd < 0) { fprintf(stderr,"can't open large object %d", lobjId); } lo_lseek(conn, lobj_fd, start, SEEK_SET); buf = malloc(len+1); for (i=0;i 0) { nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten); nwritten += nbytes; } fprintf(stderr,"0); lo_close(conn, lobj_fd); } /* * exportFile - * 巨大オブジェクト "lobjOid" をファイル "out_filename" に移出する * */ void exportFile(PGconn *conn, Oid lobjId, char *filename) { int lobj_fd; char buf[BUFSIZE]; int nbytes, tmp; int fd; /* * 転置"オブジェクト" を創る */ lobj_fd = lo_open(conn, lobjId, INV_READ); if (lobj_fd < 0) { fprintf(stderr,"can't open large object %d", lobjId); } /* * 書き出すファイルを開く */ fd = open(filename, O_CREAT|O_WRONLY, 0666); if (fd < 0) { /* error */ fprintf(stderr, "can't open unix file filename); } /* * 転置ファイルを読んで Unix ファイルに書き出す (原文は間違っているようです) */ while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) { tmp = write(fd, buf, nbytes); if (tmp < nbytes) { fprintf(stderr,"error while writing filename); } } (void) lo_close(conn, lobj_fd); (void) close(fd); return; } void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); } int main(int argc, char **argv) { char *in_filename, *out_filename; char *database; Oid lobjOid; PGconn *conn; PGresult *res; if (argc != 4) { fprintf(stderr, "Usage: %s database_name in_filename out_filename0, argv[0]); exit(1); } database = argv[1]; in_filename = argv[2]; out_filename = argv[3]; /* * 接続を準備する */ conn = PQsetdb(NULL, NULL, NULL, NULL, database); /* バックエンド接続が確立したことを確認する */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,"Connection to database '%s' failed.0, database); fprintf(stderr,"%s",PQerrorMessage(conn)); exit_nicely(conn); } res = PQexec(conn, "begin"); PQclear(res); printf("importing file /* lobjOid = importFile(conn, in_filename); */ lobjOid = lo_import(conn, in_filename); /* printf("as large object %d.0, lobjOid); printf("picking out bytes 1000-2000 of the large object0); pickout(conn, lobjOid, 1000, 1000); printf("overwriting bytes 1000-2000 of the large object with X's0); overwrite(conn, lobjOid, 1000, 1000); */ printf("exporting large object to file /* exportFile(conn, lobjOid, out_filename); */ lo_export(conn, lobjOid,out_filename); res = PQexec(conn, "end"); PQclear(res); PQfinish(conn); exit(0); } ____________________________________________________________ 14. POSTGRESのルールシステム プロダクションルールシステムは概念的には簡単ではありますが,実際に使う には微妙な点がたくさんあります.結果的に,ここでは実際のPOSTGRESのルー ルシステムの構文や操作については説明しません.その代わり,POSTGRESのルー ルシステムを使う前に,[STON90b]を読んで,これらのいくつかの点や,理論 的基礎を理解しておくべきでしょう.この章における議論はPOSTGRESのルール システムの概略とユーザの助けとなる参照と例題を示すことです. 「問い合わせ書き換え」のルールシステムはルールを考慮に入れて問い合わせ を書き換え,それを問い合わせ最適化部に渡します.それは非常に強力なもの で,問い合わせ言語手続きやビューや版管理などの沢山のことに使うことが出 来ます.このルールシステムの能力については[ONG90]や[STON90b]の中で議論 されています. 15. POSTGRES の管理 この章では,POSTGRES を大規模に利用したり,POSTGRES ユーザのグループに おけるサイト管理者であったりする人にとって興味深い事柄に関する POSTGRES の側面について述べます. 15.1. 日常的な作業 ここでは,どのようなインストール形態の POSTGRES の管理においても,把握し ておくべきいくつかの作業手順についてまとめておきます. 15.1.1. postmaster の起動 POSTGRES をインストールマニュアルどおりにインストールしていない時は, postmaster プロセスを起動する前に,いくつかの追加作業が必要です. ○ あなたが POSTGRES をインストールしたのではないとしても,インストー ルマニュアルの内容を理解しておくべきです.インストールマニュアルには, POSTGRES が重要なファイルをどこに置くかや,環境変数の正しい設定の仕方 など,POSTGRES のバージョンによって異なる重要事項についての説明があり ます. ○ インストールしたデータベースファイルを所有するユーザIDで, postmaster プロセスを起動しなくてはなりません.ほとんどの場合,インス トールマニュアルに従っているならば,これはユーザ"postgres"になります. 正しいユーザIDで postmaster を起動しないと,postmaster から起動される バックエンドサーバがデータを読めません. /usr/local/postgres95/bin がシェルのコマンドパス(path)に入っていること を確認して下さい.なぜなら,postmaster は POSTGRES のコマンドを探すの にあなたの PATH を使うからです. ○ POSTGRES データベースがインストールされたディレクトリを環境変数 PGDATA に設定して下さい.(この変数の詳細は POSTGRES のインストールマ ニュアルの中で説明されています.) ○ postmaster を非標準的なオプション,たとえば異なる TCP ポート番号で 起動したならば,すべてのユーザにそれを連絡する事を忘れないで下さい.そ うすれば彼らは自分の環境変数 PGPORT を正しく設定することができます. 15.1.2. postmaster の停止 postmaster プロセスを停止する必要があるときは,UNIX の kill(1) コマン ドが使えます.人によっては習慣的に -9 もしくは -KILL オプションを使っ てしまいます.その必要は全くありませんし,そうしないことを勧めます. なぜなら,postmaster がさまざまな共有リソースを解放できなくなりますし, その子プロセスもきれいに終了できなくなってしまうから,などなどです. 15.1.3. ユーザの登録と抹消 createuser 及び destroyuser コマンドはホストシステムにおける特定ユーザ の POSTGRES へのアクセスを許可もしくは不許可にします. 15.1.4. 定期的なお手入れ vacuum コマンドをデータベース毎に定期的に走行させるべきです.このコマ ンドは削除済みのインスタンスを処理し(注9),より重要なこととして,クラ ス毎のサイズに関するシステム統計を更新します.これらの統計が現状に合わ ない不正確なものになるのを許していると,POSTGRES の問い合わせ最適化部 は問い合わせの評価戦略に関して極端に貧弱な決断しかできなくなります.こ のため,vacuum コマンドを毎晩でも走らせることを勧めます.(たぶん,UNIX の cron(1) もしくは at(1) コマンドで実行されるスクリプトを使うことにな ります) 頻繁にバックアップをして下さい.これは,POSTGRES の copy コマンドで/あ るいは,UNIX の dump(1) または tar(1) コマンドを併用して,データベースディ レクトリをバックアップすべきだ,ということです.「なんでバックアップしな きゃならないの? クラッシュからの復旧は無いの?」と思うかもしれません. POSTGRES の「追記型」ストレッジマネジャのひとつの副作用は,それがまた 「ログ無し」ストレッジマネジャであるということです.つまり,データベース ログは abort/commit データのみを保存しています.これは,記憶メディア(ディ スク)やデータベースファイルが壊れた時に,データベースを復旧するのには十 分な情報ではありません! 言い替えれば,ディスクブロックがふっ飛んだり, POSUTOGRES がうっかりデータベースファイルを壊したりしたら,そのファイル は復旧できません.そのファイルが pg_database のような共有カタログのひと つだっだら悲惨なことになります. (注9)これはそれぞれのクラスが作成されてから現在までの記録のしかたによっ て異なってくるでしょう.しかし,vacuum コマンドの現在の実装ではデータに 対するいかなる圧縮も集合化も行ないません.このため,それぞれの POSTGRES クラスが入っている UNIX ファイルは決して小さくなることはありませんし, vacuum で「回収された」領域は実際には決して再利用されることはありません. 15.1.5. チューニング いったんユーザが大量のデータを保存し始めると,決まったようにパフォーマ ンスの問題が発生することになります.POSTGRES は世界一速い DBMS ではあ りませんが,ユーザが起こす最悪の問題のほとんどは,どんな DBMS を使った にせよ,経験不足によるものです.いくつかの一般的な助言を以下に挙げます. (1) 検索条件としてよく使う属性に対して索引を定義すること.たとえば,次 のような形式でしばしば問い合わせをするとします. SELECT * from EMP where salary < 5000 この場合,属性 salary に対する B-tree 索引が便利でしょう.以下のよう な等号を含む検索の方が多い場合は SELECT * from EMP where salary = 5000 salary に対してハッシュ索引を定義することを検討すべきです.両方を定 義することもできます.余分なディスク領域が必要になり,更新が若干遅く なりますが.索引を使った検索はクラス全体の順次検索よりずっと速くなり ます. (2) たくさん vacuum コマンドを走らせること.このコマンドは統計を更新する ので,問い合わせ最適化部が知的な決定を下すようになります.統計が不正 確だと,システムはクラスの結合や検索に関してとんちんかんな決定をする ようになります. (3) 問い合わせの条件を指定する時(つまり,問い合わせの where 部),定数を 含む句が「範囲変数 演算子 定数」の形式に帰することができるようにする こと.たとえば, EMP.salary = 5000 POSTGRES 問い合わせ最適化部はこの形式の定数条件でのみ索引を使います. 句を次のように書いても害はありません. 5000 = EMP.salary 演算子(この場合は = )が交換可能な演算子として定義されているなら, POSTGRES は問い合わせを望ましい形式に書き直すことができます.しかし, そのような演算子が無ければ,POSTGRES は索引の使用を考慮しません. (4) ひとつの問い合わせの中でいくつかのクラスを一緒に結合するときには, 「鎖型」の形式で結合句を書くようにすること.たとえば, where A.a = B.b and B.b = C.c and ... 比較的小数の句が与えられたクラスと属性を参照していることに注意して下 さい.句は属性を直線的に連続して繋いだ形式になっており,鎖で繋いだよ うになっています.これは以下のような「スター型」の形式で書かれた問い 合わせよりずっとましです. where A.a = B.b and A.a = C.c and ... ここでは,多くのクラスが同じクラスの同じ属性(この場合は A.a)を参照し ています.この形式の問い合わせがあると,POSTGRES 問い合わせ最適化部 は本来のものよりずっと多くの選択肢を考慮してメモリを無駄使いしがちに なります. (5) もし,問い合わせプランがどんなふうになるか見たくて本当に必死になって いるのなら,postmaster を -d オプション付きで起動し,モニターを -t オプション付で起動して下さい.問い合わせプランの出力形式は読みにくい ものですが,どんな索引で検索が行なわれているかを知ることができます. 15.2. 非日常的な作業 いつかそのうちに,すべての POSTGRES のサイト管理者は以下の事項をすべて やらなくてはならなくなります. 15.2.1. クラッシュの後始末 れらは個別にも同時にもクラッシュすることがあります.一方に対する管理手 順は他方に対するものとは違ってきます. バックエンドサーバがクラッシュした時にはふつう次のようなメッセージが出 ます. FATAL: no response from backend: detected in ... これは概して2つのもののうちのひとつです.つまり,POSTGRES サーバのバグ か,POSTGRS にダイナミックローディングされたユーザのコードのバグです. アプリケーションを再び走らせて,処理を再開することができますが,いくつ か考慮すべき点があります. (1) POSTGRES は通常サーバマシンのデータベースディレクトリにコアファイル (デバッグ用のプロセスメモリの使用状況のスナップショット)をダンプしま す. /usr/local/postgres95/data/base//core プログラムのデバッグをしたり,他へのバグ報告のためにスタックトレース を作ったりしたくないならば,このファイル(約10MB)を消すことができます. (2) ひとつのバックエンドが制御不能状態でクラッシュした場合(すなわち,組 み込みの後始末ルーチンを呼び出せなかったとき)には,postmaster がその 状況を検知して,走行中のすべてのサーバを終了させ,すべてのバックエン ドが共有する状態(たとえば共有バッファ領域やロック)の再初期化を行ない ます.あなたのサーバがクラッシュしたなら,"no response"メッセージを 見ることになるでしょう.他の誰かのサーバがクラッシュしたために,あな たのサーバが終了させられた時には次のようなメッセージが出るでしょう. (postmaster からシグナルくらっちまった. どっかのバックエンドプロセスが死んじまって,共有メモリ がふっとんだに違いねえ.今のトランザクションは中止だし, おいらはオサラバさせてもらうぜ.最後の問い合わせは送り 直してくんな.-- postgres バックエンド) (3) 時によっては共有状態が完全に片付かないことがあります.フロントエンド アプリケーションでは次のような形式のエラーが出るでしょう. (警告: [mydb] myclass のブロック 34 を書き戻せません) この場合には,postmaster を終了させ,再起動しなくてはなりません. (4) システムカタログの更新中にシステムがクラッシュした時(たとえば,クラ スの作成中,インデックスの定義中,クラスへの入力中など)には,時によっ てはそのカタログに対して定義してある B-tree 索引が壊れることがありま す.一般的な(かつ特異的ではない)兆候としては,すべての問い合わせが働 かなくなります.上記の作業を全部試しても,何も動かないようでしたら, reindexdb コマンドを使って見て下さい.もし、reindexdb が成功してもま だ何も動かないようでしたら,また別の問題があることになります.それが 失敗しているなら,システムカタログ自身がほとんど完全に壊れており,バッ クアップしたときの状態に戻るしかないでしょう. 通常,postmaster がクラッシュすることはありません(サーバを起動するとき以 外はあまりそうなりません)が,時にはそうなることがあります.さらに,小数 のケースとして,共有資源の再初期化の過程で障害が起こる場合があります.と りわけ,OS が postmaster に共有資源を放させておきながら,(競争が無い時で さえ)同量の共有資源の再確保を許さないというような競合条件になることがあ ります. システムエラーが原因で postmaster がクラッシュした場合には,通常は ipcclean コマンドを実行する必要があるでしょう.このようになったら,(UNIX の ipcs(1) コマンドを使って)ユーザ"postgres"が,postmaster を実行中でな いのに共有メモリと/またはセマフォを持っているのを見つけるでしょう.この 場合,これらの資源の割り当て解放をするために ipcclean をユーザ"postgres" の権限で走らせるべきです.ユーザ"postgres"が所有するそのような資源をすべ て割り当て解放するよう注意します.同じマシン上で複数の postmaster プロセ スを走行させている場合には,ipcclean を起動する前にそれらすべてを終了さ せなくてはなりません(そうしないと,共有資源が突然消滅するので,それらは 自らクラッシュします). 15.2.2. データベースディレクトリの移動 デフォルトでは,すべての POSTGRES データベースは /usr/local/postgres95/data/base (注10)下の別々のサブディレクトリに保 管されます.そのうちにいくつかのデータベースを他の場所(例えばもっと空 きのあるファイルシステム)に移したくなるでしょう. (注10) 作成時に非標準的記録管理を指定すれば,実際のクラスのデータは どこにでも保存できます.非標準的記録管理の使用は実験的機能であり,バー クレイの外部ではサポートされていません. > If you wish to move all of your databases to the new > location, you can simply: あなたのすべてのデータベースを新しい場所に移動したい時には,単に次のよ うにします. ○ postmaster を終了させます. ○ データディレクトリ全体を新しい場所にコピーします(ユーザ"postgres"が新 しいファイルの所有者になっているように注意します). % cp -rp /usr/local/postgres95/data /new/place/data ○ 環境変数 PGDATA を設定し直します(このマニュアルの最初の方や,インストー ルマニュアルの中で説明したようにします). # csh か tcsh を使っている場合... % setenv PGDATA /new/place/data # sh, ksh , bash を使っている場合... % PGDATA=/new/place/data; export PGDATA ○ postmaster を再起動します. % postmaster & ○ いくつか問い合わせを実行してみて,新しく移動したデータベースが動くこ とを確認したら,古いデータベースディレクトリを消します. % rm -rf /usr/local/postgres95/data 単一のデータベースを代替ディレクトリに移動し,他のものはそのまま残し ておく場合には,以下のようにします. ○ (まだ無ければ)createdb コマンドでデータベースを作成します.以下の作業 ではデータベースの名前が foo であるものとします. ○ postmaster を終了させます. ○ ディレクトリ /usr/local/postgres95/data/base/foo とその内容を遠くの移 動先にコピーします.その所有者はやはりユーザ"postgres"にすべきです. % cp -rp /usr/local/postgres95/data/base/foo /new/place/foo ○ ディレクトリ /usr/local/postgres95/data/base/foo を消します. % rm -rf /usr/local/postgres95/data/base/foo ○ /usr/local/postgres95/data/base から新しいディレクトリへシンボリック リンクを張ります. % ln -s /new/place/foo /usr/local/postgres95/data/base/foo ○ postmaster を再起動します. 15.2.3. データベースの更新 POSTGRES は研究用システムです.たいてい,POSTGRES はひとつのリリースと 次のリリースの間でデータベース格納用のバイナリフォーマットに互換性があ りません.このため,POSTGRES ソフトウェアを更新するときは,データベー スも同様に修正する必要があります.これは商用データベースシステムでも良 く起こることです.不運にも,市販のシステムと違い,POSTGRES には更新作 業を簡単にするユーザフレンドリーなツールは付いてきません. だいたい,新しいソフトウェアリリースに伴うデータベースの更新は次のよう に行なう必要があります. ○ 拡張部分(ユーザ定義の型,関数,集約関数など)は SQL の CREATE コマンド を再実行してロードし直さなくてはなりません.詳細は Appendix A を参照 して下さい. ○ データは古いクラスからASCIIファイルにダンプし(COPY コマンドを使います), 新しいデータベースに新しいクラスを作り(CREATE TABLE コマンドを使いま す),それからデータをASCIIファイルから再ロードします. ○ ルールとビューもさまざまな CREATE コマンドを再実行して再ロードしなく てはなりません. 新しいリリースは「お試し版」と思うべきです.特に,新しいソフトウェアを 使っても互換性の問題はないことに満足するまでは,古いデータベースを消し てはなりません.例えば,"input(ASCIIからの変換)"や"output(ASCIIへの 変換)"のようなルーチンにバグがあって,古いデータベースを消した後でデー タが再ロードできないのに気付くなんてことはいやでしょう! (これはどんな ソフトウェアパッケージについても標準的な手続きのはずです.しかし,人に よっては十分な見通しを得ないままにディスク領域を節約しようとしてしまい ます.) 15.3. データベースのセキュリティ POSTGRES を使うほとんどのサイトは教育もしくは研究機関であり,その POSTGRES のセキュリティについてはそれほど注意を払っていないでしょう. 望むなら,POSTGRES をセキュリティ機能付でインストールすることができま す.当然,そのような機能は処理のための余分な管理上のオーバヘッドを生じ ることになります. 15.3.1. Kerberos POSTGRES は MIT Kerberos ネットワーク認証システムを使うように設定する ことができます.これは,外部のユーザが正しい認証情報が無いままに,ネッ トワーク経由であなたのデータベースに接続するのを防ぎます. 15.4. システムカタログへの問い合わせ 管理者として(また,ときには普通のユーザとして),特定のデータベースに 追加されている拡張を調べたいことがあるでしょう.以下の問い合わせは「缶 詰の」問い合わせであり,どんなデータベース上でも簡単な答を得るために実 行することができます.下の問い合わせのどれかを実行する前に,POSTGRES の vacuum コマンドを実行するようにしましょう(そうすれば,問い合わせ がずっと速く実行されます).また,これらの問い合わせは /usr/local/postgres95/tutorial/syscat.sql の中にリストアップされているので,たくさん入力する替わりにカット&ペー スト(もしくは \i コマンド)を使いましょう. この問い合わせはすべてのデータベース管理者名とデータベース名を出力しま す. SELECT usename, datname FROM pg_user, pg_database WHERE usesysid = int2in(int4out(datdba)) ORDER BY usename, datname; この問い合わせはデータベース中のすべてのユーザ定義クラスを出力します. SELECT relname FROM pg_class WHERE relkind = 'r' -- 索引でない(not indices) and relname !~ '^pg_' -- カタログでない(not catalogs) and relname !~ '^Inv' -- 巨大オブジェクトでない(not large objects) ORDER BY relname; この問い合わせはすべての単純索引(つまり,複数の属 性の関数に対して定義されていないもの)をリストアップします. SELECT bc.relname AS class_name, ic.relname AS index_name, a.attname FROM pg_class bc, -- 基本クラス(base class) pg_class ic, -- 索引クラス(index class) pg_index i, pg_attribute a -- 基本属性(att in base) WHERE i.indrelid = bc.oid and i.indexrelid = ic.oid and i.indkey[0] = a.attnum and a.attrelid = bc.oid and i.indproc = '0'::oid -- 関数索引でない(no functional indices) ORDER BY class_name, index_name, attname; この問い合わせは,データベース中のすべてのユーザ定義クラスに関するユー ザ定義属性とその型のレポートを出力します. SELECT c.relname, a.attname, t.typname FROM pg_class c, pg_attribute a, pg_type t WHERE c.relkind = 'r' -- 索引でない(no indices) and c.relname !~ '^pg_' -- カタログでない(no catalogs) and c.relname !~ '^Inv' -- 巨大オブジェクトでない(no large objects) and a.attnum > 0 -- システム属性でない(no system att's) and a.attrelid = c.oid and a.atttypid = t.oid ORDER BY relname, attname; この問い合わせはすべてのユーザ定義基本型(配列型を含まない)をリストし ます. SELECT u.usename, t.typname FROM pg_type t, pg_user u WHERE u.usesysid = int2in(int4out(t.typowner)) and t.typrelid = '0'::oid -- 複合型でない(no complex types) and t.typelem = '0'::oid -- 配列でない(no arrays) and u.usename <> 'postgres' ORDER BY usename, typname; この問い合わせはすべての左単項(後置)演算子をリストします. SELECT o.oprname AS left_unary, right.typname AS operand, result.typname AS return_type FROM pg_operator o, pg_type right, pg_type result WHERE o.oprkind = 'l' -- 左単項(left unary) and o.oprright = right.oid and o.oprresult = result.oid ORDER BY operand; この問い合わせはすべての右単項(前置)演算子をリストします. SELECT o.oprname AS right_unary, left.typname AS operand, result.typname AS return_type FROM pg_operator o, pg_type left, pg_type result WHERE o.oprkind = 'r' -- 右単項(right unary) and o.oprleft = left.oid and o.oprresult = result.oid ORDER BY operand; この問い合わせはすべてのバイナリ演算子をリストします. SELECT o.oprname AS binary_op, left.typname AS left_opr, right.typname AS right_opr, result.typname AS return_type FROM pg_operator o, pg_type left, pg_type right, pg_type result WHERE o.oprkind = 'b' -- バイナリ and o.oprleft = left.oid and o.oprright = right.oid and o.oprresult = result.oid ORDER BY left_opr, right_opr; この問い合わせはすべてのユーザ定義 C 関数の名前,引数(パラメータ)の 数,戻り値の型を返します.同じ問い合わせが,"C"を"internal"に置 換することですべての組み込み C 関数の出力に,"C"を"postquel"に置換 することですべての SQL 関数に使えます. SELECT p.proname, p.pronargs, t.typname FROM pg_proc p, pg_language l, pg_type t WHERE p.prolang = l.oid and p.prorettype = t.oid and l.lanname = 'c' ORDER BY proname; この問い合わせはインストールされているすべての集約関数と適用される型を リストします.count はどんな型の引数にも適用できるので含まれません. SELECT a.aggname, t.typname FROM pg_aggregate a, pg_type t WHERE a.aggbasetype = t.oid ORDER BY aggname, typname; この問い合わせは,演算子クラス毎に使われ得る演算子と,同様にそれぞれの アクセス手法で使われ得る演算子クラスのすべてをリストします. SELECT am.amname, opc.opcname, opr.oprname FROM pg_am am, pg_amop amop, pg_opclass opc, pg_operator opr WHERE amop.amopid = am.oid and amop.amopclaid = opc.oid and amop.amopopr = opr.oid ORDER BY amname, opcname, oprname; 付録 A: 動的読み込みされる関数の結合 ------------------------------------------------------------ ユーザ定義関数を創って登録してしまえば,基本的には作業は終りです.しか しながら,POSTGRES は作成した関数を実装するオブジェクト・コード(たとえ ば,1つの .o ファイルあるいは共有ライブラリ)を読み込まなければなりま せん.先に述べたように,POSTGRES は実行時に要求に従ってユーザのコード をロードします.ユーザのコードが動的に読み込まれることを許すためには, そのコードを特別な方法でコンパイルし,結合編集しなければならないかもし れません.この節では,ユーザ定義関数を実行中の POSTGRES サーバに読み込 むことが出来るようにする前に必要な、コンパイルと結合編集のやり方につい て簡単に述べます.この過程はバージョン 4.2(注11) のころから変わったこ とに注意して下さい.もし,特別な疑問があれば C コンパイラ, cc(1) とリ ンクエディタ, ld(1)のオンライン・マニュアルを読む(読んで,読んで,読み 返す)事を期待するべきです.加えて,/usr/local/postgres95/src/regress ディレクトリに在るリグレッション・テスト・スイートには,この過程の幾つ かの実際の動作例が含まれています.もし,これらのテストでやっている事を コピーすれば,特に問題は無いはずです.次の専門用語が以下で使われます: 動的読み込み(Dynamic loading) は POSTGRES がオブジェクト・ファイルに対して行なう動作のことで す.オブジェクト・ファイルは実行中の POSTGRES サーバの中にコピー され,そして,ファイルの中の関数と変数が POSTGRES プロセスの中 の関数として利用可能となされます.POSTGRES はこれをオペレーティ ング・システムが提供する動的読み込み機構を利用して行ないます. -------------------- (注11) 古い POSTGRES の動的読み込み機構は実行形式の書式,配置,それと 主記憶内での実行形式の命令の列び等に関して,ダイナミック・ローダを書い ている人が持つような一部深い知識が要求されました.このようなローダは遅 くてバグが多いといった傾向がありました.バージョン4.2 のようなことがあっ たため,POSTGRES の動的読み込み機構はオペレーティングシステムで用意さ れた動的読み込み機構に書き換えられました.一般的に,この方法は以前の読 み込み機構に比べて,より速く,より信頼性が高く,より移植性に優れていま す.その理由は,ほぼ全ての最新のバージョンの UNIX は動的読み込み機構を 使って共有ライブラリを実装していて,それゆえ,速くて信頼性の高い機構を 用意せざるを得ないからです.一方,オブジェクト・ファイルは POSTGRES に 読み込む事が出来るようになる手前で,ちょっとだけ後処理がされなければな りません.われわれは、すこしばかりの手間を惜しむよりも,速度と信頼性に おいて大きな改善があることを望みます. 読み込みと結合編集(Loading and link editing) とは別の種類のオブジェクト・ファイル(たとえば,実行プログラム や共有ライブラリ)を作成するためにオブジェクト・ファイルに対し て行なう事です.これは,結合編集プログラムの ld(1) で行ないます. 次の一般的な制限と注意書きが以下の論議に適用されます. + 関数生成(create function)コマンドで与えられるパスは,その上 で POSTGRES サーバーが走っているマシン上で見えるディレクトリを 参照している絶対パス名(すなわち,"/" で始まる)でなくてはなりま せん.(注12) + POSTGRES ユーザは関数生成(create function)コマンドに与えられ たパスに行くことが出来なくてはならないし,またオブジェクト・ファ イルを読むことが出来なくてはなりません.これは,POSTGRES サーバ がフロントエンドのプロセスを走らせたユーザではなく,POSTGRES の ユーザ として走るからです.(ファイルやその上のディレクトリを ユーザ "postgres" として読んだり実行したり出来なくするのは非常に 一般的な間違いです.) + オブジェクト・ファイルの中で定義されるシンボルの名前はお互い に,また,POSTGRES の中で定義されるシンボルと混乱しないように しなければなりません. + 通常 GNU C コンパイラは,オペレーティング・システムのダイナミッ ク・ローダ・インターフェースを使うために要求されるような特別なオ プションは提供しません. -------------------- (注12) 相対パスは,実際には有効ですが,データベースが存在する場所から の相対的な(一般的にフロントエンドのアプリケーションからは見えない)場所 になっています.ユーザがフロントエンド・アプリケーションを起動したとこ ろのディレクトリへの相対パスは,サーバが全然異なるマシンで走る事ができ るために,全く意味が在りません. ULTRIX 動的読み込みされる ULTRIX のオブジェクト・ファイルを造るのはと ても簡単です.ULTRIX は共有ライブラリ機構は持ち合わせないので, ダイナミック・ローダ・インターフェースには何の制限もありません. 一方,移植の出来ないようなダイナミック・ローダを自分達で書き(直 し)しなくてはならなかったので,本当の共有ライブラリは使用でき ませんでした.ULTRIX のもとでの制限は -G 0 オプションでオブジェ クト・ファイルを生成しなくてはならないということです.(それは 数字の "0" で文字の "O" ではありません.例えば, # simple ULTRIX example % cc -G 0 -c foo.c は, foo.o という動的に POSTGRES に読み込まれるオブジェクト・ ファイルを生成します.この他には読み込み,あるいは結合編集は成 されていません. DEC OSF/1 DEC OSF/1 のもとでは,ld コマンドを正しいオプションでそのファ イルに対し実行する事によって単純なオブジェクト・ファイルから共 有オブジェクトファイルを生成する事ができます.以下のようなコマ ンドを実行します: # simple DEC OSF/1 example % cc -c foo.c % ld -shared -expect_unresolved '*' -o foo.so foo.o 結果的に共有オブジェクト・ファイルは,このようにして POSTGRES に読み込む事が可能になります.関数生成(create function)コマンドにオブジェクト・ファイル名を指定するとき,単 純なオブジェクト・ファイル(注13)ではなく,むしろ,共有オブジェ クト・ファイル(.so で終る)の名前を与えなければなりません. SunOS 4.x, Solaris 2.x および HP-UX SunOS 4.x, Solaris 2.x および HP-UX のもとでは共通して,単純な オブジェクト・ファイルは,特別なコンパイル・フラッグでソース・ ファイルをコンパイルして創られなくてはならず,更に,共有ライブ ラリが生成されなくてはなりません. HP-UX での必要な手順は次のとおりです.HP-UX C コンパイラの +z フラッグは「非位置依存コード(Position Independent Code)」(PIC) と呼ばれ,+u フラッグは PA-RISC アーキテクチャが通常強制する幾 つかの列びの制限を取り除きます.オブジェクト・ファイルは HP-UX のリンクエディタの -b オプションで共有ライブラリに作り変えられ なくてはなりません.これは複雑に思えるかも知れませんが,コマンド 行では次の様になり,実際にはとても簡単です: # simple HP-UX example % cc +z +u -c foo.c % ld -b -o foo.sl foo.o -------------------- (注13) 実際に POSTGRES は,オブジェクト・ファイルである限り,そのファ イルにどのような名前を付けても構いません.もし,自分の共有オブジェクト の名前にに拡張子 .o を付けたければ,POSTGRES にとっては create function コマンドで正しく名前を与える限り大丈夫です.言い替えれば,た だ単に整合性をとらなければならないということです.とはいえ,現実的な見 地から言えば,どのファイルを共有オブジェクトにしてしまっていて,どのファ イルがそうでないか,ということで必ず混乱を来すでしょう.たとえば,双方 のオブジェクト・ファイルと共有オブジェクト・ファイルの拡張子が共に.o だとすると Makefile を書くことはとても困難なことです. .so ファイルについて最後の小節で触れましたように, create function コマンドでは読み込むために正しいファイルがどれ であるかを教えられなければなりません.(すなわち,共有ライブラ リ,または,.sl ファイルの場所を与えなくてはなりません.) SunOS 4.x ではそのコマンドは,以下のようになります: # simple SunOS 4.x example % cc -PIC -c foo.c % ld -dc -dp -Bdynamic -o foo.so foo.o そして,Solaris 2.x での同等のコマンド行は: # simple Solaris 2.x example % cc -K PIC -c foo.c or % gcc -fPIC -c foo.c % ld -G -Bdynamic -o foo.so foo.o 共有ライブラリを結合するときは,幾つかの追加の共有ライブラリ (典型的に C や数学ライブラリのようなシステム・ライブラリ)を ld コマンド行で指定しなくてはならないかも知れません.