Home

Awesome

BigQuery SQL Driver & GORM Dialect for Golang

This is an implementation of the BigQuery Client as a database/sql/driver for easy integration and usage.

Goals of project

This module implements a BigQuery SQL driver and GORM dialect.

Usage

As this is using the Google Cloud Go SDK, you will need to have your credentials available via the GOOGLE_APPLICATION_CREDENTIALS environment variable point to your credential JSON file.

Vanilla *sql.DB usage

Just like any other database/sql driver you'll need to import it

package main

import (
    "database/sql"
    _ "gorm.io/driver/bigquery/driver"
    "log"
)

func main() {
    db, err := sql.Open("bigquery",
        "bigquery://projectid/dataset")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    // Do Something with the DB

}

Gorm Usage

Opening a Gorm bigquery db

package main

import (
    "gorm.io/driver/bigquery"
    "gorm.io/gorm"
    "log"
)

func main() {
    // You can also use the location format: "bigquery://projectid/location/dataset"
    db, err := gorm.Open(bigquery.Open("bigquery://go-bigquery-driver/playground"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    // Do Something with the DB

}

Using gorm with a BigQuery query that has a record

package main

import (
    "gorm.io/driver/bigquery"
    "gorm.io/gorm"
    "log"
)


type ComplexRecord struct {
	Name   string           `gorm:"column:Name"`
	Record ComplexSubRecord `gorm:"column:Record:type:RECORD"`
}

type ComplexSubRecord struct {
	Name string `gorm:"column:Name"`
	Age  int    `gorm:"column:Age"`
}


func main() {
    // You can also pass custom endpoint and/or skip authentication by using query parameters like this:
    // bigquery://go-bigquery-driver/playground?endpoint=http://localhost:56758&disable_auth=true
    db, err := gorm.Open(bigquery.Open("bigquery://go-bigquery-driver/playground"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }

    var records []ComplexRecord

    // Delete complex record table if exists
    db.Migrator().DropTable(&ComplexRecord{})

    // Make sure we have a complex_records table
    db.AutoMigrate(&ComplexRecord{})

    // Insert new records to table
    db.Create(&ComplexRecord{Name: "test", Record: ComplexSubRecord{Name: "dd", Age: 1}})
    db.Create(&ComplexRecord{Name: "test2", Record: ComplexSubRecord{Name: "dd2", Age: 444}})

    // Select records from table
    db.Order("Name").Find(&records)

}

Using gorm with a BigQuery query that has an array

package main

import (
    "gorm.io/driver/bigquery"
    "gorm.io/gorm"
    "log"
)

type ArrayRecord struct {
	Name    string            `gorm:"column:Name"`
	Records []ComplexSubRecord `gorm:"column:Records;type:ARRAY"`
}

type ComplexSubRecord struct {
	Name string `gorm:"column:Name"`
	Age  int    `gorm:"column:Age"`
}

func main() {
    db, err := gorm.Open(bigquery.Open("bigquery://go-bigquery-driver/playground"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }

    var records []ArrayRecord

    // Delete array_records table if exists
    db.Migrator().DropTable(&ArrayRecord{})

    // Make sure we have an array_records table
    db.AutoMigrate(&ArrayRecord{})

    // Insert new records to table
    db.Create(&ArrayRecord{Name: "test", Records: []ComplexSubRecord{{Name: "dd", Age: 1}, {Name: "dd1", Age: 1}}})
    db.Create(&ArrayRecord{Name: "test2", Records: []ComplexSubRecord{{Name: "dd2", Age: 444}, {Name: "dd3", Age: 1}}})

    // Select records from table ordered by name
    db.Order("Name").Find(&records)


}

Using gorm with a BigQuery query that uses unnest

package main

import (
    "gorm.io/driver/bigquery"
    "gorm.io/gorm"
    "log"
)

type Version struct {
	Label string `gorm:"column:Label"`
}

func main() {
    db, err := gorm.Open(bigquery.Open("bigquery://go-bigquery-driver/playground"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }

    var versions []Version

    query := db.Table("charts, UNNEST(Samples) as sample")

    query = query.Select("DISTINCT CONCAT(" +
        "CAST(sample.MajorVersion AS STRING), '.'," +
        "CAST(sample.MinorVersion AS STRING), '.'," +
        "CAST(sample.RevisionVersion AS STRING)" +
        ") as Label")

    err = query.Find(&versions).Error
    if err != nil {
        log.Fatal(err)
    }

}