20200908のGoに関する記事は5件です。

GORMでの主要メソッドによるゼロ値の取り扱いについて

前提

gorm v2.0 で確認しています。

"gorm.io/gorm"

User 構造体は以下のものを使用しています。

type User struct {
    ID        int       `gorm:"column:id;primary_key:yes"`
    Email     string    `gorm:"column:email"`
    Age       int       `gorm:"column:age"`
    IsAdmin   bool      `gorm:"column:is_admin"`
    CreatedAt time.Time `gorm:"column:created_at"`
    UpdatedAt time.Time `gorm:"column:updated_at"`
}

Where

構造体を利用した書き方ですとフィールドがポインタ型でない場合ゼロ値("", 0, false)は検索できないため、事故を防ぐならベタで書くのが一番安全かと思います。

ただ、DB の構造体のフィールドをすべてポインタ型で宣言するようなルールになっている場合は構造体を利用した書き方で統一しても良いかと思います。

db.Where(User{Email: "example", Age: 10, IsAdmin: true}).Find(&user)
// => SELECT * FROM `users` WHERE `users`.`email` = "example" AND `users`.`age` = 10 AND `users`.`is_admin` = true;

db.Where(User{Email: "", Age: 0, IsAdmin: false}).Find(&user)
// => SELECT * FROM `users`;

db.Debug().Where("email = ?", "").Where("age = ?", 0).Where("is_admin", false).Find(&user)
// => SELECT * FROM `users` WHERE email = "" AND age = 0 AND `is_admin` = false;

Update

v1 では Where 句と同様に更新するフィールドがポインタ型でない場合はゼロ値の更新はできませんでした。

db.Where("id = ?", 1).Updates(User{Email: "", Age: 0, IsAdmin: false})
// => UPDATE `users` SET `updated_at`="2020-09-08 17:26:26.386" WHERE id = 1;
db.Where("id = ?", 1).Updates(User{Email: "example", Age: 10, IsAdmin: true})
// => UPDATE `users` SET `email`="example",`age`=10,`is_admin`=true,`updated_at`="2020-09-08 17:25:07.501" WHERE id = 1;

しかし、v2 で更新するカラムを明示的に選択した状態だとゼロ値でも更新することが出来るようになりました。

db.Select("is_admin").Where("id = ?", 1).Updates(User{Email: "", Age: 0, IsAdmin: false})
// => UPDATE `users` SET `is_admin`=false WHERE id = 1;

Save

save はゼロ値に関わらず構造体に設定してる値を INSERT・UPDATE します

db.Debug().Save(&user)
// => INSERT INTO `users` (`email`,`age`,`is_admin`,`created_at`,`updated_at`) VALUES ("",0,false,"2020-09-08 08:01:09","2020-09-08 13:34:23");
// => UPDATE `users` SET `email`="",`age`=0,`is_admin`=false,`created_at`="2020-09-08 08:01:09",`updated_at`="2020-09-08 22:34:22.664" WHERE `id` = 1;

Delete

delete は特にゼロ値は関係ありませんので割愛。

オマケ

v2からUpdateとDeleteに条件文が必須になりましたので、全件更新・削除の事故がなくなりました。
primary_key:yesつけ忘れて全件削除事故が起きた後にO/Rの仕様をしって驚愕したのはいい思い出です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

goでクロスコンパイル

環境変数に指定すると良いようだ。

対応OS

sample
$ go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
illumos/amd64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
windows/386
windows/amd64
windows/arm

クロスコンパイル

Windows用にコンパイル

Windows,64bit
$ GOOS=windows GOARCH=amd64 go build -o hello.exe hello.go
$ file hello.exe
hello.exe:       PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

Linux用にコンパイル

Linux,x86_64
$ GOOS=linux GOARCH=amd64 go build -o hello-linux,x64 hello.go
$ file hello-linux,x64
hello-linux,x64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=3PtKe3LN9_4rxlzxXgHa/1uVXnXx5yplTTQQgjFlz/CgwczejsGQh2_tbwoQtP/i5_2ofMgEmSF8t-M_DMC, not stripped
Linux,arm64
$ GOOS=linux GOARCH=arm64 go build -o hello-linux,arm hello.go
$ file hello-linux,arm
hello-linux,arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, Go BuildID=VovlihMkDllgU6NAxm-Z/kyuuVg3MXeB_YJcbORcv/QafLZDijlnE-kvNk7EjY/TtzedUs_SNx9-ovthhf3, not stripped
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CentOS8にGolang/MariaDBをインストールし、DB接続してSQLを実行する

概要

Golang学習用備忘録です。
今回以下URLを参考にさせていただきました。

Go 言語から MariaDB Server を使ってみる
GoでMySQLに接続する

Golangインストール/動作確認

よろしければ、以下を参考にしてください。
CentOS8にGolangをインストールする

MariaDBインストール/GoからMariadbへの接続準備

ターミナル
#Mariadbインストール
dnf install -y mariadb mariadb-server

#MariaDBを起動・自動起動設定
systemctl start mariadb
systemctl enable mariadb

#MariaDBのrootユーザーの初期パスワードを設定
/usr/bin/mysql_secure_installation

#Set root password? [Y/n] y →「y」を選択
#New password:  →入力
#Re-enter new password: →再入力
#Password updated successfully!

#Mariadb バージョン確認
mysql --version

#バージョン情報が表示されること

#gitがインストールされていない場合には、下記コマンドにて
#インストールを行う
dnf -y install git

#MariaDBからGo用Connectorが提供されていないため、
#GitHubで公開されているMySQL用ドライバを使用
go get github.com/go-sql-driver/mysql
  • 接続用Goモジュール作成
check_mariadb_ver.go
package main

import(
        "database/sql"
        "fmt"
        _ "github.com/go-sql-driver/mysql"
)

func main(){
        //データベースに接続
        db, err := sql.Open("mysql", "ユーザ名:パスワード@/データベース名")
        if err != nil {
                fmt.Println(err.Error())
        }
        defer db.Close()

        //バージョン情報確認
        var version string
        db.QueryRow("SELECT VERSION()").Scan(&version)
        //バージョン情報出力
        fmt.Println("connected to:", version)
}
  • モジュール実行
ターミナル
#実行
go run check_mariadb_ver.go

#バージョン情報が出力されること
connected to: 10.3.17-MariaDB

まとめ

GoからMariadbに接続して、SQLを実行するところまでをみていきました。
今後は、Goを活用してのAPI作成等行い、
最終的には、簡単なアプリケーションを開発してみたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AtCoder Library Practice Contest 参戦記 (Go)

AtCoder Library Practice Contest 参戦記 (Go)

ACL Contest は 1200 から rated なので rated で出場できるかどうしようと考えつつ、AtCoder Library Practice Contest を解いてみるテスト.

PRACTICE2A - Disjoint Set Union

ACL に Union Find 入ってないような、見落としてる??? あ、DSU = Disjoint Set Union = Union Find か.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func find(parent []int, i int) int {
    if parent[i] < 0 {
        return i
    }
    parent[i] = find(parent, parent[i])
    return parent[i]
}

func unite(parent []int, i, j int) {
    i = find(parent, i)
    j = find(parent, j)
    if i == j {
        return
    }
    parent[j] += parent[i]
    parent[i] = j
}

func main() {
    defer flush()

    N := readInt()
    Q := readInt()

    parent := make([]int, N)
    for i := 0; i < N; i++ {
        parent[i] = -1
    }

    for i := 0; i < Q; i++ {
        t := readInt()
        u := readInt()
        v := readInt()
        if t == 0 {
            unite(parent, u, v)
        } else if t == 1 {
            if find(parent, u) == find(parent, v) {
                println(1)
            } else {
                println(0)
            }
        }
    }
}

const (
    ioBufferSize = 1 * 1024 * 1024 // 1 MB
)

var stdinScanner = func() *bufio.Scanner {
    result := bufio.NewScanner(os.Stdin)
    result.Buffer(make([]byte, ioBufferSize), ioBufferSize)
    result.Split(bufio.ScanWords)
    return result
}()

func readString() string {
    stdinScanner.Scan()
    return stdinScanner.Text()
}

func readInt() int {
    result, err := strconv.Atoi(readString())
    if err != nil {
        panic(err)
    }
    return result
}

var stdoutWriter = bufio.NewWriter(os.Stdout)

func flush() {
    stdoutWriter.Flush()
}

func println(args ...interface{}) (int, error) {
    return fmt.Fprintln(stdoutWriter, args...)
}

PRACTICE2B - Fenwick Tree

3度目の登板.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

// BIT stands for binary indexed tree.
type BIT []int

func newBIT(n int) BIT {
    return make([]int, n)
}

func (bit BIT) add(i, v int) {
    for i++; i <= len(bit); i += i & -i {
        bit[i-1] += v
    }
}

func (bit BIT) sum(i int) int {
    result := 0
    for i++; i > 0; i -= i & -i {
        result += bit[i-1]
    }
    return result
}

func (bit BIT) query(start int, stop int) int {
    return bit.sum(stop-1) - bit.sum(start-1)
}

func main() {
    defer flush()

    N := readInt()
    Q := readInt()

    a := make([]int, N)
    for i := 0; i < N; i++ {
        a[i] = readInt()
    }

    bit := newBIT(N)
    for i := 0; i < N; i++ {
        bit.add(i, a[i])
    }

    for i := 0; i < Q; i++ {
        t := readInt()
        if t == 0 {
            p := readInt()
            x := readInt()
            bit.add(p, x)
        } else if t == 1 {
            l := readInt()
            r := readInt()
            println(bit.query(l, r))
        }
    }
}

const (
    ioBufferSize = 1 * 1024 * 1024 // 1 MB
)

var stdinScanner = func() *bufio.Scanner {
    result := bufio.NewScanner(os.Stdin)
    result.Buffer(make([]byte, ioBufferSize), ioBufferSize)
    result.Split(bufio.ScanWords)
    return result
}()

func readString() string {
    stdinScanner.Scan()
    return stdinScanner.Text()
}

func readInt() int {
    result, err := strconv.Atoi(readString())
    if err != nil {
        panic(err)
    }
    return result
}

var stdoutWriter = bufio.NewWriter(os.Stdout)

func flush() {
    stdoutWriter.Flush()
}

func println(args ...interface{}) (int, error) {
    return fmt.Fprintln(stdoutWriter, args...)
}

PRACTICE2J - Segment Tree

min_left もメソッドにするべきかな.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}

type segmentTree struct {
    offset int
    data   []int
    op     func(x, y int) int
    e      int
}

func newSegmentTree(n int, op func(x, y int) int, e int) segmentTree {
    var result segmentTree
    t := 1
    for t < n {
        t *= 2
    }
    result.offset = t - 1
    result.data = make([]int, 2*t-1)
    for i := 0; i < len(result.data); i++ {
        result.data[i] = e
    }
    result.op = op
    result.e = e
    return result
}

func (st segmentTree) build(a []int) {
    for i, v := range a {
        st.data[st.offset+i] = v
    }
    for i := st.offset - 1; i > -1; i-- {
        st.data[i] = st.op(st.data[i*2+1], st.data[i*2+2])
    }
}

func (st segmentTree) update(index, value int) {
    i := st.offset + index
    st.data[i] = value
    for i >= 1 {
        i = (i - 1) / 2
        st.data[i] = st.op(st.data[i*2+1], st.data[i*2+2])
    }
}

func (st segmentTree) query(start, stop int) int {
    result := st.e
    l := start + st.offset
    r := stop + st.offset
    for l < r {
        if l&1 == 0 {
            result = st.op(result, st.data[l])
        }
        if r&1 == 0 {
            result = st.op(result, st.data[r-1])
        }
        l = l / 2
        r = (r - 1) / 2
    }
    return result
}

func main() {
    defer flush()

    N := readInt()
    Q := readInt()

    A := make([]int, N)
    for i := 0; i < N; i++ {
        A[i] = readInt()
    }

    st := newSegmentTree(N, max, -1)
    st.build(A)

    for i := 0; i < Q; i++ {
        T := readInt()
        if T == 1 || T == 3 {
            X := readInt()
            V := readInt()
            if T == 1 {
                st.update(X-1, V)
            } else if T == 3 {
                ok := N + 1
                ng := X - 1
                for ok-ng > 1 {
                    m := ng + (ok-ng)/2
                    if st.query(X-1, m) >= V {
                        ok = m
                    } else {
                        ng = m
                    }
                }
                println(ok)
            }
        } else if T == 2 {
            L := readInt()
            R := readInt()
            println(st.query(L-1, R))
        }
    }
}

const (
    ioBufferSize = 1 * 1024 * 1024 // 1 MB
)

var stdinScanner = func() *bufio.Scanner {
    result := bufio.NewScanner(os.Stdin)
    result.Buffer(make([]byte, ioBufferSize), ioBufferSize)
    result.Split(bufio.ScanWords)
    return result
}()

func readString() string {
    stdinScanner.Scan()
    return stdinScanner.Text()
}

func readInt() int {
    result, err := strconv.Atoi(readString())
    if err != nil {
        panic(err)
    }
    return result
}

var stdoutWriter = bufio.NewWriter(os.Stdout)

func flush() {
    stdoutWriter.Flush()
}

func println(args ...interface{}) (int, error) {
    return fmt.Fprintln(stdoutWriter, args...)
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go言語で kintone REST API から データを取得する

Go言語からkintone REST API を叩いて、kintoneアプリのフィールド情報を取得するサンプルです。

APIは下記参照
https://developer.cybozu.io/hc/ja/articles/204783170#anchor_getform_fields

環境

  • go version go1.14.2 darwin/amd64
  • Mac

設定ファイル

GoDotEnvライブラリを利用。
.envファイルにkintone接続情報を設定します。
https://github.com/joho/godotenv

サンプルコード

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"

    "github.com/joho/godotenv"
)

func main() {
    err := godotenv.Load()
    if err != nil {
        log.Fatal("Error loading .env file")
    }
    ba, _ := fetchFieldsJson(os.Getenv("APP"), os.Getenv("SUB_DOMAIN"), os.Getenv("API_TOKEN"))
    var out bytes.Buffer
    if err := json.Indent(&out, ba, "", "  "); err != nil {
        log.Fatal("Error json indent", err)
    }
    out.WriteTo(os.Stdout)
}


func fetchFieldsJson(appid string, subdomain string, apitoken string) ([]byte, error) {
    client := &http.Client{}
    type Body struct {
        App string `json:"app"`
    }
    data := Body{appid}
    body, err := json.Marshal(data)
    if err != nil {
        log.Fatal("Error JSON Encode")
    }
    req, err := http.NewRequest("GET", "https://"+subdomain+".cybozu.com/k/v1/app/form/fields.json", bytes.NewReader(body))
    if err != nil {
        log.Fatal("Error request")
    }
    req.Header.Add("X-Cybozu-API-Token", apitoken)
    req.Header.Add("Content-Type", "application/json")
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal("Error client")
    }
    defer resp.Body.Close()
    fmt.Println(resp.Status)
    ba, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal("Error read")
    }
    return ba, err
}

参考

Go言語からkintoneのレコード取得の方法は、下記記事が参考になります。
kintone REST API について (GET編)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む