postgresql advent calender 2014 using jsonb by ecpg

49
埋め込み SQL から JSONB を扱う ぬこ@横浜 (@nuko_yokohama) PostgreSQL Advent Calener 2o14

Upload: toshi-harada

Post on 22-Jul-2015

865 views

Category:

Technology


2 download

TRANSCRIPT

埋め込み SQL からJSONB を扱う

ぬこ@横浜 (@nuko_yokohama)

PostgreSQL Advent Calener 2o14

みなさん、こんにちは

このスライドはPostgreSQL Advent Calender 20147 日目のエントリ用のスライドです。

先日 (12/5) 開催されたPostgreSQLカンファレンスで

JSONB 型について発表しましたが、(スライドはこちら)

その発表から削った小ネタをAdvent Calender に転用しますw

ということで今日は埋め込み SQL で

JSONB を使うという相変わらずの誰得なネタ

でいきます。

ところで、皆さん埋め込み SQL って

使ってますか?

というか埋め込み SQL って

なんぞ?

埋め込み SQL(Embedded SQL)

手続き型言語に SQL を埋め込んでデータベースアクセスを可能にする手法

(http://ja.wikipedia.org/wiki/%E5%9F%8B%E3%82%81%E8%BE%BC%E3%81%BFSQL)

[nuko@localhost ecpg]$ cat sample.pgc #include <stdio.h>#include <stdlib.h>

intmain (void){

EXEC SQL CONNECT TO test USER nuko;

/* TRUNCATE TABLE sample */EXEC SQL BEGIN TRANSACTION;

EXEC SQL TRUNCATE test;EXEC SQL COMMIT;EXEC SQL DISCONNECT;

return (0);}

[nuko@localhost ecpg]$

C 言語の埋め込み SQL 例( TRUNCATE TABLE の実行)

"EXEC SQL“ で始まる行は埋め込み SQL として

SQL そのものを記述できる

当たり前だけど、このファイルを C コンパイラにかけてもエラーになってしまう。

なので、埋め込み SQL はコンパイラにかける前に

「プリコンパイラ」にかけて埋め込み SQL を

変換する必要がある。

PostgreSQL には、埋め込みSQL を処理するプリコンパイラ「 ecpg 」がコア機能として

組み込まれている。(http://www.postgresql.jp/document/9.3/html/ecpg.html)

埋め込み SQL を組み込んだC 言語を実行可能にするまでの

おおまかな手順

イメージ図

pgcファイル

ecpg

Ecpg用ヘッダファイル

cファイル

cヘッダファイル

Cコンパイラ(gccなど )

実行形式ファイル

ecpgライブラリ

ということで、埋め込み SQL の

簡単な実例を見てみる。

先ほどの sample.pgc をecpg にかけると

sample.c ファイルが生成される

$ ls sample*sample.pgc$ ecpg sample.pgc $ ls sample*sample.c sample.pgc$

生成された” .c” ファイルを覗いてみる

こんな感じの C コードに展開$ cat sample.c /* Processed by ecpg (4.10.0) *//* These include files are added by the preprocessor */#include <ecpglib.h>#include <ecpgerrno.h>#include <sqlca.h>/* End of automatic include section */

#line 1 "sample.pgc"#include <stdio.h>#include <stdlib.h>

intmain (void){

{ ECPGconnect(__LINE__, 0, "test" , "nuko" , NULL , NULL, 0); }#line 7 "sample.pgc"

/* TRUNCATE TABLE sample */{ ECPGtrans(__LINE__, NULL, "begin transaction");}

#line 10 "sample.pgc"

{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);}#line 11 "sample.pgc"(中略)

return (0);}

生成された C ソースを直接変更する必要性はない。

こんな C ソースが裏で生成されてることを知っておくくらいで OK

で、生成された C ソースをC コンパイラにかける。

ecpg ヘッダファイル格納先を -I で指定ecpg ライブラリ (libecpg) をリンク時に指定

$ cc -o sample -I${HOME}/pgsql/include -L${HOME}/pgsql/lib -lecpg sample.c$

これで完成!

で、埋め込み SQL のメリットって何なの?

PostgreSQL 文書を見ると一応、以下のメリットが

あるらしい。

その1

SQL 標準である。

へぇー(棒)

埋め込み SQL の概念はかなり古くから SQL 標準とし

て取り込まれている。

SQL86, SQL89, SQL92 など

PostgreSQL でも埋め込み SQL 対応はかなり古く (6.3) から取り込まれている。

PostgreSQL における埋め込み SQL の扱い ( 適当 )

西暦 PostgreSQLバージョン

PostgreSQLでの対応

世界の動き

1998 年 6.3 ecpg の初リリース アントニオ猪木がドン・フライと引退試合

2000 年 7.0 メモリリーク対応 PlayStation2 発売

2002 年 7.2 EXECUTE 文対応配列対応の改善

Xbox 日本国内で発売

2003 年 7.4 ecpg と ecpglib のスレッドセーフ化

新幹線 100系が運行終了

2005 年 8.0 SET DESCRIPTOR 対応 愛知万博

8.1 \x16 進数エスケープのサポート

PostgreSQL における埋め込み SQL の扱い ( 適当 )

西暦 PostgreSQLバージョン

PostgreSQLでの対応

世界の動き

2006 年 8.2 SHOW コマンド対応リグレッション試験対応

冥王星が惑星から除外

2008 年 8.3 V3 Frontend/backend protocol の採用

0 系新幹線運用終了

2009 年 8.4 ecpg パーサがサーバパーサから自動生成

1 兆ジンバブエ・ドルの発行

2010 年 9.0 SQLDA サポート はやぶさ、地球に帰還

2011 年 9.1 WHERE CURRENT OF 句の改善

おせち事件

2014 年 9.4 C スタイルコメントのネスト対応(?)

WindowsXP おわた

※要は最近も開発継続中ということ。

SQL 標準ってことは他 DBMS でも

使われているってこと?

SQL 標準における埋め込み SQL の扱い

SQL 標準 対応言語

SQL86 COBOLFORTRANPL/I

SQL89 C

結構昔から存在している規格

他 DBMS での対応DBMS COBOL C C++ FORTRAN Pascal

Oracle ○ ○ ○ ○ ○PostgreSQL ○ △DB2 ○SQLServer(以前)

Informix ○

Oracle はマメにサポートしてるなあ。PostgreSQL の C++ 対応は不完全

Oracle も PostgreSQL もC 言語の埋め込み SQL をサポートしている!

つまり、 Oracle の Pro*C で作ったアプリケーションは

PostgreSQL ecpg への移植が容易?             _   _ _             / ´ = : ミ ´ 二 . ヾ\            / ' / '´r ー = 、ヽ . ヽ 、ヽ          i / 〃 , イ |    | |_L|  l l      埋め込み SQL は誰でもウェルカム            |.l.l ル '__ リヽ  ヘ l_N ヽ !.l |       PostgreSQL でも Oracle でもお好きなものを          | |. バ ̄ o`   ´o  ̄ ,"|l |       どうぞお気になさらず.            レ1  ̄ 〈 |:   ̄  !`|       ご自由にお楽しみください            ド」 、ー -----‐ ァ ,l イ !       _,,... -‐| l ト、` ¨ 二 ¨´ ,. イ .l l ー -   ...._    , ィ ''"´:::::::::::::::| l.l :::: ヽ、 __, .::´ :l.l |::::::::::::::::: ` ¨l ヽ               r' つ.    /:::|:::::::::::::::::::::::W \ ::::::::::: / l ル :::::::::::::::::::::::|::: ヽ               /  ∟、 -‐'' つ  /:::::: |::::::::::::::::::::::::l.   \ /   .l::::::::::::::::::::::::|:::::: ヽ         ,.<    )ヽヾニニ⊃. /:::::::::::|:::::::::::::::::::::::::l   /\   .l::::::::::::::::::::::::|:::::::::: ヽ      /\\   i   l ニ二⊇/:::::::::::::::|:::::::::::::::::::::::::l /\ _ /\ .!::::::::::::::::::::::::|:::::::::::::: ヽ    / :::::::::::: \ . ゝ -─' ー -- ':::::::::::::::::::|::::::::::::::::::::::::: l  ハ   /:::::::::::::::::::::::::|:::::::::::::::::: \ / :::::::::::::::::::: /:::::::::::::::::: l:::::::::::::::::::::::::::! ./  ヽ ./::::::::::::::::::::::::::|:::::::::::::::::::: / :::::::::::::::::::: /ヽ ::::::::::::::; イ :::::::::::::::::::::::::::V    V:::::::::::::::::::::::::::: ト、 :::::::::::::/:::::::::::::::::::::: /:::::::::::::::/   |:::::::::::::::::::::::::::: ヽ  ./::::::::::::::::::::::::::::::|  ヽ :::::::::::::::::::::::::::: /::::::::::::/    | :::::::::::::::::::::::::::::∨::::::::::::::::::::::::::::::::|    ヽ :::::::::::::::::: /_ : /    |:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|     \_ : /

そんなに甘くはない。              ,   -‐; z ..__      _丿        / ゙̄ヽ′ ニ‐ - 、\  \   ところがどっこい       Z ´// , ヘ .∧ ヽ \ヽ ゝ   ヽ   ‥‥‥‥        /, / , リ   v ヘ lヽ\ヽヽ .|     ノ  夢じゃありません        / イル _- 、 ij~   ハにヽ ,, \ `|  <      ‥‥‥‥!.         N⌒ ヽヽ  / / ̄リ :| l l |    ` )            ト、 _e. 〉 u ' e _ ノノ |.l l |   ∠ .    現実です          | 、< 、 ij   _,¨ 、イ || ト、 |     ヽ      ‥‥‥!.            | ドエエエ「 -┴''´|.|L 八   ノ - 、   これが現実‥!            l. ヒ _ ー -r- ー ' スソ | l トゝ、 .__    | ,. - 、     _,,.   -‐ ''" トヽエエエエ ! ゝ '´. イ i l;;;;:::::::::::`:: ー /   ハ :::::::::::::::::::::| l \ー一 _v~'´ j   ,1;;;;;;:::::::::::::::::::.    /:::;l::::::::::::::::::::;W1;;; 下、 / l ル ' !;;;;;;;;;::::::::::::::::  /:::::;;; l :::::::::::::::::;;;;;;;;;;;;;;;|: : X : : : : : |;;;;;;;;;;;;;;::::::::::::  /:::::;;;;;;|:::::::::::::::;;;;;;;;;;;;;;;;|/: : > 、 : : :|;;;;;;;;;;;;;;;:::::::::::

Oracle 独自拡張多すぎぃ!(動的 SQL の扱いなど)

【閑話休題 昔話】

そういえば某社の入社直後にプリコンパイラの概念も知らずホスト言語の COBOL も知らず移植元の PL/I ベースの言語も

知らないままに、C 言語でプリコンパイラ作成の一部を

やらされたのは良い思い出

その2

プリコンパイル時にSQL の構文チェックを

してくれる。

$ cat sample.pgc #include <stdio.h>#include <stdlib.h>

intmain (void){

EXEC SQL CONNECT TO test USER nuko;

/* TRUNCATE TABLE sample */EXEC SQL BEGIN TRANSACTION;

EXEC SQL TRANCATE test;EXEC SQL COMMIT;EXEC SQL DISCONNECT;

return (0);}

$ ecpg sample.pgc sample.pgc:11: ERROR: unrecognized data type name "TRANCATE"$

プリコンパイル時の構文チェックの例

TRUNCATE 文のスペルミス

プリコンパイル時にエラーチェックしてくれる

存在しないテーブル等のプリコンパイル時のチェックはできない。

また動的な SQL を使う場合もプリコンパイル時のチェックはできない。

その3

ホスト変数によるプログラム言語への情報の授受が簡単?

ということで、ここからホスト変数を使ってJSONB 型の挿入と

検索を行なう例を説明します。

やっと本題ですw

というかホスト変数って

何よ?

ホスト変数とは、C プログラムとSQL 文との間で

データをやり取りするための変数のこと。

DECLARE SECTION で記述

intmain (void){EXEC SQL BEGIN DECLARE SECTION; // Host variable VARCHAR insert_data[1024]; VARCHAR id[16]; VARCHAR name[128]; VARCHAR age[16]; // null indicator int age_ind;

EXEC SQL END DECLARE SECTION;

int i;

JSONB アクセス用のホスト変数

可変長テキストの場合はVARCHAR という特殊な記法で宣言する

これはホスト変数じゃない

識別子用変数(後述)

ecpg で JSONB を扱う場合は VARCHAR を使うことになる。

char* values[3] = { "{\"id\":1, \"name\": {\"first\": \"Oleg\"}, \"distribute\": [\"GIN\", \"hstore\", \"json\", \"jsonb\"]}", "{\"id\":2, \"age\": 59, \"name\": {\"last\": \"Lane\", \"first\": \"Tom\"}}", "{\"id\":3, \"name\": {\"nickname\": \"nuko\"}, \"distribute\": [\"ksj\", \"neo4jfdw\"]}"};

(中略) VARCHAR insert_data[1024];(中略)

for (i=0; i<=2; i++) { insert_data.len = strlen(values[i]); strcpy(insert_data.arr, values[i]); EXEC SQL INSERT INTO jsonb_t VALUES ( :insert_data ); }

ホスト変数を使って JSONB を INSERT

長さ (len) とデータ実体 (arr) を事前にセットする

SQL内でホスト変数を使うときには

”:” を前につける

/* select jsonb data (use cursor) */ EXEC SQL DECLARE cur CURSOR FOR SELECT data->>'id', data->>'name', data->>'age' FROM jsonb_t;

EXEC SQL OPEN cur; while (true) { EXEC SQL FETCH NEXT FROM cur INTO :id, :name, :age:age_ind; if (sqlca.sqlcode > 0) break;

if ( age_ind < 0) { // "age" is null printf("id=%s, name=%s\n", id.arr, name.arr); } else { // "age" is not null printf("id=%s, name=%s, age=%s\n", id.arr, name.arr, age.arr); } } EXEC SQL CLOSE cur;

ホスト変数を使って JSONB を SELECT(カーソルの細かい説明は割愛。すいません)

JSONB 演算子を使ったSELECT 文でカーソルを定義

FETCH文内にホスト変数を記述

指示子はホスト変数の後ろに記述する

値参照時にはarr をつける

/* select jsonb data (use cursor) */ EXEC SQL DECLARE cur CURSOR FOR SELECT data->>'id', data->>'name', data->>'age' FROM jsonb_t;

EXEC SQL OPEN cur; while (true) { EXEC SQL FETCH NEXT FROM cur INTO :id, :name, :age:age_ind; if (sqlca.sqlcode > 0) break;

if ( age_ind < 0) { // "age" is null printf("id=%s, name=%s\n", id.arr, name.arr); } else { // "age" is not null printf("id=%s, name=%s, age=%s\n", id.arr, name.arr, age.arr); } } EXEC SQL CLOSE cur;

指示子について(主に null の判別のために使う変数)

指示子変数が負の値ならnull である 指示子はホスト変数の

後ろに記述する

$ ecpg jsonb.pgc$ cc -o jsonb -I${HOME}/pgsql/include -L${HOME}/pgsql/lib -lecpg jsonb.c$ ./jsonb id=1, name={"first": "Oleg"}id=2, name={"last": "Lane", "first": "Tom"}, age=59id=3, name={"nickname": "nuko"}$

プリコンパイル・コンパイル・実行

埋め込み SQL でも JSONB はフツーに使えました!

ということで、レガシーな埋め込み SQL というフレームワークでも

最新機能である JSONB が使えますよ、という話でした。

ecpg 自体、それなりにメリットがあるかもしれないが

いまどき、あえて新規アプリケーションを

ecpg で組む理由はあまりない気がする・・・が。

でも、そんな機能でも「 SQL 標準である」「互換性を重視する」

という理由でサポートしてるPostgreSQL は可愛いなあと

PostgreSQL is lovely...

おしまい