BigQueryエミュレータの作り方
Merpay Architect
goccy
2022/10/15 Go Conference mini 2022 Autumn IN SENDAI
Repository | Star |
goccy/go-json | 1.8k |
goccy/go-yaml | 754 |
goccy/go-reflect | 392 |
goccy/go-graphviz | 408 |
Agenda
Agenda
BigQuery
ZetaSQL
Agenda
Motivation
Goal
Agenda
goccy/bigquery-emulator
サポートしている機能 ( データ型 / 文 / 句 )
サポートしている機能 ( 標準関数 )
Aggregate | 15/15 |
Statistical aggregate | 9/9 |
Approximate aggregate | 0/4 |
HyperLogLog++ | 0/4 |
Numbering | 3/6 |
Bit | 0/1 |
Conversion | 17/18 |
Mathematical | 41/41 |
Navigation | 3/7 |
Hash | 5/5 |
String | 50/52 |
JSON | 7/16 |
Array | 8/8 |
Date | 12/12 |
Datetime | 10/10 |
Time | 9/9 |
Timestamp | 16/16 |
Interval | 5/5 |
Geography | 0/63 |
Security | 0/1 |
UUID | 1/1 |
Net | 0/10 |
Debugging | 0/1 |
AEAD Encryption | 0/13 |
63% ( 206 / 329 )
※ サポート数 / 標準関数の数
今後サポート予定の機能
Agenda
Architecture Overview
Client SDK
Python
Go
Java
bigquery-emulator
go-zetasqlite
REST API Handler
SQLite
Pass ZetaSQL
Query
ZetaSQL Query
Parse and Analyze
SQLite Query
go-sqlite3
Convert
Pass SQLite
Query
bq
Access DB
BigQuery API
goccy/go-zetasqlite
Architecture
SELECT * FROM TABLE WHERE id = 1
Parse & Analyze� by go-zetasql
SELECT * FROM TABLE
WHERE zetasqlite_equal(`id`, 1)
Generate
ZetaSQL クエリを SQLite 向けに変換するときの注意
ZetaSQL クエリを SQLite 向けに変換するときの注意
SQLite に存在しない型の扱い
データ型の変換フロー
Decode
Argument Values
Custom Function
SQLite
go-sqlite3
load data
Literal value in query
go-zetasqlite
Pass interface{} values as argument
db.Query(�`SELECT * FROM Table WHERE id = “goccy”`)
db.Exec(“
UPDATE SET age = ? Table WHERE id = ?”, 20, “goccy”)
Driver library arguments
Call go-sqlite3’s� db.Query() or db.Exec()� with encoded literal value
Encode
Literal Value
Receive interface{}
value as return value
Logic
Encode
Return Value
store data
load data
Decode
driver.Rows value
Pass interface{} values as driver.Rows
store data
データ型の変換アルゴリズム
"2022-10-14"
{"header":"DATE","body":"2022-10-14"}
eyJoZWFkZXIiOiJEQVRFIiwiYm9keSI6IjIwMjItMTAtMTQifQo=
例) DATE 型の値 "2022-10-14" を変換する場合
ZetaSQL クエリを SQLite 向けに変換するときの注意
ARRAY 型の値の展開
1:1
N:1
( COUNT など )
SELECT * FROM ( SELECT json_each.value FROM json_each("[1, 2, 3]") )
ZetaSQL クエリを SQLite 向けに変換するときの注意
WINDOW 関数
go-zetasqlite の WINDOW 関数
(
SELECT CUSTOM_WINDOW(col, window_rowid(`row_id`)) FROM (SELECT col FROM TABLE)
) FROM (
SELECT *, ROW_NUMBER() OVER() AS `row_id` FROM TABLE
)
ZetaSQL クエリを SQLite 向けに変換するときの注意
ORDER BY による SQLite に存在しない型の比較と COLLATE
goccy/go-zetasql
cgo
cgo を使って C/C++ ライブラリをソースからビルドする
バインディング時のファイル構成
マルチパッケージ構成のライブラリのバインディング(1/2)
├── a.c ( library A のソースコード )
├── b.c ( library B のソースコード )
├── test.c ( main 関数がある )
└── util.c ( a.c と b.c から参照される関数がある )
マルチパッケージ構成のライブラリのバインディング(2/2)
.
├── a
│ ├── bind.c
│ └── bind.go
├── b
│ ├── bind.c
│ └── bind.go
└── src
├── a.c
├── b.c
├── test.c
└── util.c
package a
/*
#cgo CFLAGS: -I../src
void FuncA();
*/
import "C"
func FuncA() {
C.FuncA()
}
#include "a.c"
#include "util.c"
a/bind.go
a/bind.c
パッケージ b の bind.go と bind.c は
A を B に変えただけ
util.c には FuncA と FuncB から�参照される関数が定義されている
a と b を import するとどうなるか
Q.
マルチパッケージのシンボル解決問題
シンボル解決テクニック ( その1 )
#define UtilFunc UtilFuncA
#include "a.c"
#include "util.c"
#undef UtilFunc
問題点
#define UtilFunc UtilFuncB
#include "b.c"
#include "util.c"
#undef UtilFunc
a/bind.c
b/bind.c
シンボル解決テクニック ( その2 )
├── internal
│ ├── bind.c
│ └── bind.go
└── src
├── a.c
├── b.c
├── test.c
└── util.c
package internal
/*
#cgo CFLAGS: -I../src
void FuncA();
void FuncB();
*/
import "C"
func FuncA() {
C.FuncA()
}
func FuncB() {
C.FuncB()
}
#include "a.c"
#include "b.c"
#include "util.c"
問題点
シンボル解決テクニック ( その3 )
├── a
│ ├── bind.c
│ └── bind.go
├── b
│ ├── bind.c
│ └── bind.go
├── go.mod
(略)
└── util : 新しく追加
├── bind.c
└── bind.go
package a
/*
#cgo CFLAGS: -I../src
void FuncA();
*/
import "C"
import (
"example/util"
)
//export export_UtilFuncA
func export_UtilFuncA(v C.int) {
util.UtilFunc(int(v))
}
func FuncA() {
C.FuncA()
}
#include "_cgo_export.h"
#define UtilFunc export_UtilFuncA
#include "a.c"
#undef UtilFunc
util package で作られた関数を Go 側で import したあと、export directive で C側に�その関数を公開する
a/bind.go
a/bind.c
この組み合わせで C => Go の関数�呼び出しができる
go-zetasql はどうやっているか
おまけ: cgoを使っていても静的バイナリは作れる
まとめ