Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
SpannerのSQL解析ライブラリ "memefish" の紹介/gcpug-memefish-introduction
TSUYUSATO Kitsune
February 06, 2020
Programming
3
860
SpannerのSQL解析ライブラリ "memefish" の紹介/gcpug-memefish-introduction
GCPUG Tokyo Handy Spanner Day February 2020 (2020/02/06)での発表資料
TSUYUSATO Kitsune
February 06, 2020
Tweet
Share
More Decks by TSUYUSATO Kitsune
See All by TSUYUSATO Kitsune
ReDoS 検出の最先端 recheck の紹介 / State of the Art of ReDoS Detection
makenowjust
9
2.6k
ReDoS 検出プログラム recheck の開発 / recheck: ReDoS check program
makenowjust
0
62
Integrated Shrinking による 高階関数の自動生成 / PRO-2020-4 Generating Higher-Order Functions by Integrated Shrinking
makenowjust
0
67
ハイブリッド型ReDoS検出プログラムの実装 / PROSYM62 - A Hybrid Approach to Detect ReDoS
makenowjust
1
370
dali introduction
makenowjust
1
110
memefish the Spanner SQL parser and analyzer
makenowjust
0
2.7k
«le noir que j'ai créé»
makenowjust
0
690
先読みと後読みの可能な、O(N)の正規表現エンジンの実装
makenowjust
2
1k
C Compiler in Crystal on Compile Time
makenowjust
0
950
Other Decks in Programming
See All in Programming
なぜRubyコミュニティにコミットするのか?
luccafort
0
340
Milestoner
bkuhlmann
1
250
T3 Stack and TypeScript ecosystem
quramy
3
820
Next.js 13 Layout / Streaming SSR 仕組み解説
sumiren
1
280
Becoming an Android Librarian (Android World Wide 2023 Jan)
skydoves
2
230
フロントエンドで学んだことをデータ分析で使ってみた話
daichi_igarashi
0
190
Unity+C#で学ぶ! メモリレイアウトとvtableのすゝめ 〜動的ポリモーフィズムを実現する仕組み〜
rossam
1
390
良質な技術記事を量産する秘訣 / #MeetsPro
jnchito
16
5k
爆速の日経電子版開発の今
shinyaigeek
2
680
PHP でガチの電卓を作る
memory1994
PRO
2
170
Swift Expression Macros: a practical introduction
kishikawakatsumi
2
740
Findy - エンジニア向け会社紹介 / Findy Letter for Engineers
findyinc
2
42k
Featured
See All Featured
5 minutes of I Can Smell Your CMS
philhawksworth
198
18k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
217
21k
Agile that works and the tools we love
rasmusluckow
321
20k
Building Applications with DynamoDB
mza
85
5k
Web development in the modern age
philhawksworth
197
9.6k
Designing Experiences People Love
moore
130
22k
Creatively Recalculating Your Daily Design Routine
revolveconf
207
11k
Building Your Own Lightsaber
phodgson
96
4.9k
Happy Clients
brianwarren
90
5.8k
How GitHub Uses GitHub to Build GitHub
holman
465
280k
Art, The Web, and Tiny UX
lynnandtonic
284
18k
Navigating Team Friction
lara
177
12k
Transcript
SpannerのSQL解析ライブラリ "memefish" の紹介 @MakeNowJust at GCPUG Tokyo Handy Spanner Day
February 2020 (2020/02/06) SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 1
⾃⼰紹介 HN: MakeNowJust 学⽣ 去年の夏にメルカリのインターンに参加していた 今回紹介するものも、インターンで開発したもの SpannerのSQL解析ライブラリ "memefish" の紹介 by
@MakeNowJust 2
SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 3
紹介するもの SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 4
méméfish méméfish méméfish https://makenowjust.github.io/memefish SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 5
memefish Spannerで使えるSQLを解析するためのGo⾔語のライブラリ (OSS) https://github.com/MakeNowJust/memefishで公開されている 主な機能 1. SQLのパース 2. 構⽂⽊からのSQLの⽣成 3.
SQLの意味解析・型チェック SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 6
memefishを使っているプロジェクト gcpug/handy-spanner mercari/yo SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 7
パッケージ構成 github.com/MakeNowJust/memefish/pkg 以下の重要なパッケージ token : パースに使うトークンやファイルの構造体を定義したパッケージ ast : パース結果のAST (抽象構⽂⽊)を定義したパッケージ
parser : パーサーの実装があるパッケージ analyzer : 型チェッカーのあるパッケージ go/token, go/ast, go/parser などのパッケージを参考にしている SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 8
主な機能 1. SQLのパース 2. 構⽂⽊からのSQLの⽣成 3. SQLの意味解析・型チェック 今⽇の主題 これらの機能がどのようなものなのか memefishでそれらの機能をどうやって使うのか
を説明します (前提知識: Go⾔語を知ってること) SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 9
1. SQLのパース・2. 構⽂⽊からのSQLの⽣成 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 10
パースとは 与えられた⽂字列をSQLとして解釈して、対応する抽象構⽂⽊を組み⽴てること。 例: パースしたい⽂字列 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 11
パース: Lexer そのまま抽象構⽂⽊を組み⽴てるのは難しいので、 まず意味のある最⼩の単位(トークン)に⽂字列を分割する。 トークンには、その部分の役割(キーワードか?識別⼦か?⽂字列リテラルか?など)も 記録されている。 SpannerのSQL解析ライブラリ "memefish" の紹介 by
@MakeNowJust 12
パース: Lexer この処理を⾏うオブジェクトを Lexer と呼ぶ。 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust
13
パース: Parser Lexer によって分割されたトークンの列から抽象構⽂⽊を組み⽴てていく。 抽象構⽂⽊: SQLの各要素に対応する構造体 (⽊構造) この処理を⾏うオブジェクトを Parser と呼ぶ。
SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 14
使い⽅: import ⽂ まずは import 。 ast, token, parser を使う。
import ( "fmt" "log" "github.com/MakenowJust/memefish/pkg/ast" "github.com/MakenowJust/memefish/pkg/parser" "github.com/MakenowJust/memefish/pkg/token" ) SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 15
使い⽅: Parser の組み⽴て Parser は構造体なので必要なフィールドを埋めていく。 必要なフィールドは Lexer だけ。 ( Parser
が Lexer を使うので) また、パースしたい⽂字列は File という構造体に包む。 file := &token.File{ Buffer: "SELECT * FROM Singers WHERE FirstName = \"Foo\"", } p := &parser.Parser{ Lexer: &parser.Lexer{File: file}, } SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 16
使い⽅: ParseQuery の呼び出し 実際のパースは ParseQuery メソッドの呼び出しで⾏なわれる。 ParseQuery の返り値は抽象構⽂⽊とパース時のエラー。 stmt, err
:= p.ParseQuery() if err != nil { log.Fatal(err) } あとは、この stmt を使ってやりたいことをする。 stmt.Query.(*ast.Select).From.Source.(*ast.TableName).Table.Name == "Singers" // => true SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 17
使い⽅: まとめ パースしたい⽂字列を構造体 File に包む ↑を使って Lexer 、 Parser を作る
ParseQuery を呼び出してパースを実⾏する (DMLのパースなら ParseDML 、DDLのパースなら ParseDDL が使える) 結果の抽象構⽂⽊を使ってごにょごにょする SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 18
SQLの⽣成 抽象構⽂⽊の型 ast.Node の定義 type Node interface { Pos() token.Pos
End() token.Pos // Convert AST node into SQL source string (a.k.a. Unparse). SQL() string } この SQL() メソッドで、抽象構⽂⽊からSQLの⽂字列を⽣成できる。 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 19
例: SQLの⽣成 さきほどまでのパースの例の続き // Singers の部分を Albums に差し替える stmt.Query.(*ast.Select).From.Source.(*ast.TableName).Table.Name =
"Albums" fmt.Println(stmt.SQL()) // Output: // SELECT * FROM Albums WHERE FirstName = "Foo" SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 20
3. SQLの意味解析・型チェック SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 21
意味解析・型チェック SQL中の 識別⼦がテーブル・カラム・変数名のどれなのか? 具体的には何を指しているのか? 式の結果がどのような型になるか? を求める機能。 (パースなどと⽐べるとやや不安定です……) SpannerのSQL解析ライブラリ "memefish" の紹介
by @MakeNowJust 22
意味解析・型チェック 意味解析の途中で、 存在しないテーブル・カラムを参照している おかしな操作をしている 例: ⽂字列と数値を⾜し合わせようとしている 例: WHERE の式が BOOL
値を返していない などのエラーが分かる。 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 23
意味解析・型チェック: 疑問 Q. 実際にSpanner送って実⾏計画を⾒るなりすれば、それらのエラーは分かるのでは? memefishで意味解析をする場合、 1. ローカルで全て完結する (通信を伴わない) 2. 型情報や参照の情報をプログラムから利⽤できる
といったメリットがある。特に2番が重要。 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 24
意味解析・型チェック: 使⽤イメージ 例えば、意味解析ではクエリの結果の各カラムがどのような名前・型なのかを取得できる。 これを利⽤すると、クエリからその結果に⼀致する構造体を⾃動⽣成する、 といったことができる。 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust
25
意味解析・型チェック: 使⽤イメージ 例: SELECT CONCAT(FirstName, " ", LastName) AS Name,
BirthDate FROM Singers から type Result struct { Name string BirthDate time.Time } を⾃動⽣成 SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 26
→次のスライドから、これを作っていきます SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 27
使い⽅: import ⽂ 意味解析は抽象構⽂⽊に対して⾏うので、 parser, token のインポートも必要 import ( "fmt"
"log" "github.com/MakeNowJust/memefish/pkg/analyzer" "github.com/MakeNowJust/memefish/pkg/parser" "github.com/MakeNowJust/memefish/pkg/token" ) SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 28
使い⽅: パース まずはパースをする。(さっきと同じ) file := &token.File{ Buffer: "SELECT CONCAT(FirstName, \"
\", LastName) AS Name, BirthDate FROM Singers", } p := &parser.Parser{ Lexer: &parser.Lexer{File: file}, } stmt, err := p.ParseQuery() if err != nil { log.Fatal(err) } SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 29
使い⽅: Catalog の組み⽴て テーブルのスキーマ定義をまとめたものを Catalog と呼ぶ。 catalog := &analyzer.Catalog{ Tables:
map[string]*analyzer.TableSchema{ "SINGERS": { Name: "Singers", Columns: []*analyzer.ColumnSchema{ {Name: "FirstName", Type: analyzer.StringType}, {Name: "LastName", Type: analyzer.StringType}, {Name: "BirthDate", Type: analyzer.DateType}, }, }, }, } SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 30
使い⽅: Analyzer の組み⽴て Catalog と File を渡す必要がある。 File はエラー位置を実際のソースコードに対応させるために必要。 a
:= &analyzer.Analyzer{ File: file, Catalog: catalog, } SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 31
使い⽅: 意味解析の実⾏ AnalyzeQueryStatement に解析したい抽象構⽂⽊を渡すことで意味解析を実⾏できる。 返り値はエラーのみで、意味解析の結果は Analyzer ⾃⾝に保存される。 err = a.AnalyzeQueryStatement(stmt)
if err != nil { log.Fatal(err) } SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 32
使い⽅: 意味解析の結果を使う 例えば、 a.NameList には結果の各カラムの情報が保存されている。 これを使って、SQLの結果に対応する構造体の定義を出⼒する。 columns := a.NameLists[stmt.Query] fmt.Printf("type
Result struct {\n"); for _, column := range columns { fmt.Printf("\t%-9s %s\n", column.Text, mapSQLType(column.Type)) } fmt.Printf("}\n"); ( mapSQLType はSQLの型をGo⾔語の型に対応させる関数。省略) SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 33
使い⽅: まとめ 解析したいSQLをパースする スキーマをまとめた Catalog を組み⽴てる Catalog と File から
Analyzer を組み⽴てる AnalyzeQueryStatement に解析したい抽象構⽂⽊を渡す 意味解析の結果は Analyzer ⾃⾝に保存されている 他には Types フィールドに各式の型が保存されていたりする。 今回の例の全体は次のGo Playgroundから確認できます。 https://play.golang.org/p/PN5JuJKB-Mo SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 34
今回のまとめ SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 35
今回のまとめ memefishはSpannerのSQLをパース・意味解析できるGo⾔語のライブラリ SQL⽂をプログラムで扱うことができる ⾊々と⾯⽩いことに使えると思うので、記憶に⽚隅にでも留めてもらえたら その他、希望など memefishはOSSなので、バグ報告・修正などはいつでもウェルカムです memefishを使って何か作ったよ、みたいな報告もありがたいです ただ、 analyzer 周りはやや開発途中な部分があるので、
後⽅互換性の無い変更などあるかもしれません (悪しからず) SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 36
Q&A? SpannerのSQL解析ライブラリ "memefish" の紹介 by @MakeNowJust 37