2019-12-09

SOQLのdatabase/sqlドライバーを作った

database/sqlのsqlドライバーを使ってSalesforceのSOQLを動かしてみましたー

ということで使い方とdatabase/sqlのドライバーの作り方を備忘として残しておきます。

使い方

$ go get github.com/tzmfreedom/go-soql-driver

して、以下のようにして使います

package main

import (
	"database/sql"
	"fmt"
	"net/url"
	"os"

	soqlDriver "github.com/tzmfreedom/go-soql-driver"
)

func main() {
	username := os.Getenv("SFDC_USERNAME")
	password := os.Getenv("SFDC_PASSWORD")
	dsn := soqlDriver.CreateDsn(url.QueryEscape(username), url.QueryEscape(password), "login.salesforce.com")
	db, err := sql.Open("soql", dsn)
	if err != nil {
		panic(err)
	}
	rows, err := db.Query("SELECT Id, Name FROM Account")
	if err != nil {
		panic(err)
	}
	for rows.Next() {
		var id, name string
		rows.Scan(&id, &name)
		fmt.Println(id, name)
	}
}

func insert(db *sql.DB) {
	r, err := db.Exec("INSERT INTO Account(Name) VALUES ('Created By sql driver')")
	if err != nil {
		panic(err)
	}
	fmt.Println(r.RowsAffected())
}

func update(db *sql.DB) {
	r, err := db.Exec("UPDATE Account SET Name = 'Updated By sql driver' WHERE Name = 'Created By sql driver'")
	if err != nil {
		panic(err)
	}
	fmt.Println(r.RowsAffected())
}

func delete(db *sql.DB) {
	r, err := db.Exec("DELETE FROM Account WHERE Name = 'Updated By sql driver'")
	if err != nil {
		panic(err)
	}
	fmt.Println(r.RowsAffected())
}

SOQLにはINSERT, UPDATE, DELETE構文はありませんが、よしなに変換してDMLを発行しています。

Execに引数をバインドするのは未対応なのでこれからって感じっす

database/sqlのdriverの作り方

まずはDriverをRegisterします。importしたときにRegisterしたい場合はinitで実行します。

func init() {
	sql.Register("soql", &SOQLDriver{})
}

DriverのインターフェースとしてOpen関数を実装する必要があります。

type Driver interface {
    Open(name string) (Conn, error)
}

SOQLDriverの場合は、dsnに応じてSalesforceにログインする処理をしています。 戻り値はdriver.Connインターフェースになります。

driver.Connインターフェースは以下の定義になります。

type Conn interface {
    Prepare(query string) (Stmt, error)
    Close() error
    Begin() (Tx, error)
}

SOQLDriverの場合はトランザクション無し、CloseもなしなのでPrepareのみ実装しています。 Prepareはクエリ文字列を引数に受け取り、driver.Stmtインターフェースを返します。

driver.Stmtインターフェースは以下の定義になります。

type Stmt interface {
    Close() error
    NumInput() int
    Exec(args []Value) (Result, error)
    Query(args []Value) (Rows, error)
}

QueryはSELECT実行、ExecはSELECT以外のDMLの実行をする関数です。

driver.Rowsインターフェースは以下の定義になります。

type Rows interface {
    Columns() []string
    Close() error
    Next(dest []Value) error
}

Columnsは現在のカーソルの値を返す関数、Nextは次のレコードが存在するかを判定する関数です。

driver.Resultインターフェースは以下のような定義です。

type Result interface {
    LastInsertId() (int64, error)
    RowsAffected() (int64, error)
}

SOQLDriverの場合、IDは文字列型でLastInsertIdはアンマッチだったので使っていません。

このエントリーをはてなブックマークに追加