memeduck

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2021 License: Apache-2.0 Imports: 5 Imported by: 0

README

memeduck

ci status Go Reference

duck

The memeduck is a SQL query builder for Cloud Spanner. (named after MakeNowJust/memefish)

This project is currently in an experimental state and any API will be changed in an backward-incompatible way.

Examples

Select
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		Where(memeduck.Eq(memeduck.Ident("good_at"), "cooking")).
		LimitOffset(10, 3).
		SQL()
	fmt.Println(query)
	// Output: SELECT name, created_at FROM user WHERE good_at = "cooking" LIMIT 10 OFFSET 3
    // user has many items
	itemStmt := memeduck.Select("user_item", []string{"item_id", "count"}).
		Where(memeduck.Eq(memeduck.Ident("user_id"), "user-id")).
		AsStruct()
	// user has one status
	statusStmt := memeduck.Select("user_status", []string{"state"}).
		Where(memeduck.Eq(memeduck.Ident("user_id"), "user-id")).
		AsStruct()
	query.Stmt, _ := memeduck.Select("user", []string{"name"}).
		SubQuery(
			memeduck.ArraySubQuery(itemStmt).As("user_item"),
			memeduck.ScalarSubQuery(statusStmt).As("user_status"),
		).
		Where(memeduck.Eq(memeduck.Ident("user_id"), "user-id")).
		SQL()
	// Output:
	// SELECT
	// name,
	//	ARRAY(SELECT AS STRUCT item_id, count FROM user_item WHERE user_id = "user-id") AS user_item,
	//	ARRAY(SELECT AS STRUCT state FROM user_status WHERE user_id = "user-id") AS user_status
	// FROM user WHERE user_id = "user-id"
Insert
type ExampleUserStruct struct {
	Name string `spanner:"UserName"`
	Papa string `spanner:"PapaName"`
}
	query, _ := memeduck.Insert("users", []string{"UserName", "PapaName"}).Values([]ExampleUserStruct{
		{Name: "Kiara", Papa: "huke"},
	}).SQL()
	fmt.Println(query)
	// Output: INSERT INTO users (UserName, PapaName) VALUES ("Kiara", "huke")
Update
	query, _ := memeduck.Update("user").
		Set(memeduck.Ident("age"), memeduck.Param("age")).
		Where(memeduck.Eq(memeduck.Ident("shark"), true)).
		SQL()
	fmt.Println(query)
	// Output: UPDATE user SET age = @age WHERE shark = TRUE
Delete
	query, _ := memeduck.Delete("user").
		Where(memeduck.Eq(memeduck.Ident("id"), 123)).
		Where(memeduck.Eq(memeduck.Ident("unused"), true)).
		SQL()
	fmt.Println(query)
	// Output: DELETE FROM user WHERE id = 123 AND unused = TRUE

You can see more examples in the examples sectionss of the documentation.

License

Distributed under the Apache License, Version 2.0. See LICENSE for more information.

Documentation

Overview

Package memeduck provides tools to build Spanner SQL queries.

Supported Types

The following types can be used as a SQL expression:

  • If a value implements `ToASTExpr() (*ast.Expr, error)`, memeduck uses this method to convert Go values into SQL expressions.
  • If a value is nil (of any type), it is converted into NULL.
  • If a value is one of string, *string, or spanner.NullString, it is converted into STRING literal.
  • If a value is []byte, it is converted into BYTES literal.
  • If a value is one of int, *int, int64, or *int64, spanner.NullInt64, it is converted into INT64 literal.
  • If a value is one of bool, *bool, or spanner.NullBool, it is converted into BOOL literal.
  • If a value is one of float64, *float64, or spanner.NullFloat64, it is converted into FLOAT64 literal.
  • If a value is one of time.Time, *time.Time, or spanner.NullTime, it is converted into TIMESTAMP literal.
  • If a value is one of civil.Date, *civil.Date, or spanner.NullDate, it is converted into DATE literal.
  • If a value is a slice of the above types, it is converted into ARRAY<T> literal.

Struct Tags

You can add `spanner:"Name"` tag to struct fields to indicate which field in struct corresponds to which column, otherwise memeduck uses field name as column name. See examples section of Insert function for more details.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ArraySubQueryStmt added in v0.2.0

type ArraySubQueryStmt struct {
	// contains filtered or unexported fields
}

func ArraySubQuery added in v0.2.0

func ArraySubQuery(stmt *SelectStmt) *ArraySubQueryStmt

func (*ArraySubQueryStmt) As added in v0.2.0

func (*ArraySubQueryStmt) ToAST added in v0.2.0

func (s *ArraySubQueryStmt) ToAST() (ast.SelectItem, error)

type BetweenCond

type BetweenCond struct {
	// contains filtered or unexported fields
}

BetweenCond represents BETWEEN or NOT BETWEEN predicates.

func Between

func Between(x, min, max interface{}) *BetweenCond

Between(x, min, max) creates `x BETWEEN min AND max` predicate.

func NotBetween

func NotBetween(x, min, max interface{}) *BetweenCond

NotBetween(x, min, max) creates `x NOT BETWEEN min AND max` predicate.

func (*BetweenCond) ToASTWhere

func (c *BetweenCond) ToASTWhere() (*ast.Where, error)

type DeleteStmt

type DeleteStmt struct {
	// contains filtered or unexported fields
}

DeleteStmt builds DELETE statements.

func Delete

func Delete(table string) *DeleteStmt

Delete creates a new DeleteStmt with given table name.

Example
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Delete("user").Where(
		memeduck.Eq(memeduck.Ident("id"), 123),
		memeduck.Eq(memeduck.Ident("unused"), true),
	).SQL()
	fmt.Println(query)
}
Output:

DELETE FROM user WHERE id = 123 AND unused = TRUE
Example (MultipleWhere)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Delete("user").
		Where(memeduck.Eq(memeduck.Ident("id"), 123)).
		Where(memeduck.Eq(memeduck.Ident("unused"), true)).
		SQL()
	fmt.Println(query)
}
Output:

DELETE FROM user WHERE id = 123 AND unused = TRUE
Example (QueryParameter)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Delete("user").
		Where(memeduck.Eq(memeduck.Ident("id"), memeduck.Param("id"))).
		SQL()
	fmt.Println(query)
}
Output:

DELETE FROM user WHERE id = @id

func (*DeleteStmt) SQL

func (s *DeleteStmt) SQL() (string, error)

func (*DeleteStmt) Where

func (s *DeleteStmt) Where(conds ...WhereCond) *DeleteStmt

Where appends given conditional expressions to the DELETE statement.

type Direction

type Direction ast.Direction

Direction is an ordering direction used by ORDER BY clause.

type ExprCond

type ExprCond struct {
	// contains filtered or unexported fields
}

ExprCond is a boolean expression to filter records.

func Bool

func Bool(v bool) *ExprCond

Bool creates a new boolean literal.

func (*ExprCond) ToASTWhere

func (c *ExprCond) ToASTWhere() (*ast.Where, error)

type IdentExpr

type IdentExpr struct {
	// contains filtered or unexported fields
}

IdentExpr is an identifier.

func Ident

func Ident(names ...string) *IdentExpr

Ident creates a new IdentExpr. Path expression can be created by passing more than one elements.

func (*IdentExpr) ToASTExpr

func (e *IdentExpr) ToASTExpr() (ast.Expr, error)

type InCond added in v0.3.0

type InCond struct {
	// contains filtered or unexported fields
}

InCond represents IN or NOT IN predicates.

func In added in v0.3.0

func In(x interface{}, y InConditionValue) *InCond

In(x, y) creates `x IN y` predicate.

func NotIn added in v0.3.0

func NotIn(x interface{}, y InConditionValue) *InCond

In(x, y) creates `x NOT IN y` predicate.

func (*InCond) ToASTWhere added in v0.3.0

func (c *InCond) ToASTWhere() (*ast.Where, error)

type InConditionValue added in v0.3.0

type InConditionValue interface {
	ToASTInConditionValue() (ast.InCondition, error)
}

InConditionValue is a value expression in IN clauses.

type InsertStmt

type InsertStmt struct {
	// contains filtered or unexported fields
}

InsertStmt builds INSERT statements.

func Insert

func Insert(table string, cols []string) *InsertStmt

Insert creates a new InsertStmt with given table name. and column names.

Example
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Insert("user", []string{"name", "oshi_mark"}).Values([][]string{
		{"Subaru", ":ambulance:"},
		{"Watame", ":sheep:"},
	}).SQL()
	fmt.Println(query)
}
Output:

INSERT INTO user (name, oshi_mark) VALUES ("Subaru", ":ambulance:"), ("Watame", ":sheep:")
Example (QueryParameter)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Insert("user", []string{"name", "weight", "is_onion"}).Values([][]interface{}{
		{memeduck.Param("name"), memeduck.Param("weight"), memeduck.Param("is_onion")},
	}).SQL()
	fmt.Println(query)
}
Output:

INSERT INTO user (name, weight, is_onion) VALUES (@name, @weight, @is_onion)
Example (Struct)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

type ExampleUserStruct struct {
	Name string `spanner:"UserName"`
	Papa string `spanner:"PapaName"`
}

func main() {
	query, _ := memeduck.Insert("users", []string{"UserName", "PapaName"}).Values([]ExampleUserStruct{
		{Name: "Kiara", Papa: "huke"},
	}).SQL()
	fmt.Println(query)
}
Output:

INSERT INTO users (UserName, PapaName) VALUES ("Kiara", "huke")

func (*InsertStmt) SQL

func (is *InsertStmt) SQL() (string, error)

func (*InsertStmt) Values

func (s *InsertStmt) Values(values interface{}) *InsertStmt

Values returns an InsertStmt with its values set to given ones. It replaces existing values.

type LogicalOpCond

type LogicalOpCond struct {
	// contains filtered or unexported fields
}

LogicalOpCond represents AND/OR operator.

func And

func And(conds ...WhereCond) *LogicalOpCond

And concatenates more than one WhereConds with AND operator.

func Or

func Or(conds ...WhereCond) *LogicalOpCond

Or concatenates more than one WhereConds with OR operator.

func (*LogicalOpCond) ToASTWhere

func (c *LogicalOpCond) ToASTWhere() (*ast.Where, error)

type NullCond

type NullCond struct {
	// contains filtered or unexported fields
}

NullCond represents IS NULL or IS NOT NULL predicate.

func IsNotNull

func IsNotNull(arg interface{}) *NullCond

IsNotNull creates `x IS NOT NULL` predicate.

func IsNull

func IsNull(arg interface{}) *NullCond

IsNull creates `x IS NULL` predicate.

func (*NullCond) ToASTWhere

func (c *NullCond) ToASTWhere() (*ast.Where, error)

type OpCond

type OpCond struct {
	// contains filtered or unexported fields
}

OpCond is a binary operator expression.

func Eq

func Eq(lhs, rhs interface{}) *OpCond

Eq(x, y) is a shorthand for Op(x, EQ, y)

func Ge

func Ge(lhs, rhs interface{}) *OpCond

Ge(x, y) is a shorthand for Op(x, GE, y)

func Gt

func Gt(lhs, rhs interface{}) *OpCond

Gt(x, y) is a shorthand for Op(x, GT, y)

func Le

func Le(lhs, rhs interface{}) *OpCond

Le(x, y) is a shorthand for Op(x, LE, y)

func Like

func Like(lhs, rhs interface{}) *OpCond

Like(x, y) is a shorthand for Op(x, LIKE, y)

func Lt

func Lt(lhs, rhs interface{}) *OpCond

Lt(x, y) is a shorthand for Op(x, LT, y)

func Ne

func Ne(lhs, rhs interface{}) *OpCond

Ne(x, y) is a shorthand for Op(x, NE, y)

func NotLike

func NotLike(lhs, rhs interface{}) *OpCond

NotLike(x, y) is a shorthand for Op(x, NOT_LIKE, y)

func Op

func Op(lhs interface{}, op BinaryOp, rhs interface{}) *OpCond

Op creates a new binary operator expression.

func (*OpCond) ToASTWhere

func (c *OpCond) ToASTWhere() (*ast.Where, error)

type ParamExpr

type ParamExpr struct {
	// contains filtered or unexported fields
}

ParamExpr is a query parameter.

func Param

func Param(name string) *ParamExpr

Param createsa new ParamExpr.

func (*ParamExpr) ToASTExpr

func (e *ParamExpr) ToASTExpr() (ast.Expr, error)

type ScalarSubQueryStmt added in v0.2.0

type ScalarSubQueryStmt struct {
	// contains filtered or unexported fields
}

func ScalarSubQuery added in v0.2.0

func ScalarSubQuery(stmt *SelectStmt) *ScalarSubQueryStmt

func (*ScalarSubQueryStmt) As added in v0.2.0

func (*ScalarSubQueryStmt) ToAST added in v0.2.0

func (s *ScalarSubQueryStmt) ToAST() (ast.SelectItem, error)

type SelectStmt

type SelectStmt struct {
	// contains filtered or unexported fields
}

SelectStmt builds SELECT statements.

func Select

func Select(table string, cols []string) *SelectStmt

Select creates a new SelectStmt with given table name and column names.

Example
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).Where(
		memeduck.Eq(memeduck.Ident("race"), "Phoenix"),
		memeduck.Eq(memeduck.Ident("work_at"), "KFP"),
	).SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at FROM user WHERE race = "Phoenix" AND work_at = "KFP"
Example (ForceIndex)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "age"}).Where(
		memeduck.Lt(memeduck.Ident("age"), 18),
	).ForceIndex("idx_age").SQL()
	fmt.Println(query)
}
Output:

SELECT name, age FROM user @{FORCE_INDEX=idx_age} WHERE age < 18
Example (Limit)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		Where(memeduck.Eq(memeduck.Ident("likes"), "alcohol")).
		Limit(10).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at FROM user WHERE likes = "alcohol" LIMIT 10
Example (LimitOffset)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		Where(memeduck.Eq(memeduck.Ident("good_at"), "cooking")).
		LimitOffset(10, 3).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at FROM user WHERE good_at = "cooking" LIMIT 10 OFFSET 3
Example (MultipleWhere)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		Where(memeduck.Eq(memeduck.Ident("job"), "detective")).
		Where(memeduck.Eq(memeduck.Ident("defective"), true)).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at FROM user WHERE job = "detective" AND defective = TRUE
Example (OrderBy)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		Where(memeduck.IsNotNull(memeduck.Ident("age"))).
		OrderBy("subscribers", memeduck.ASC).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at FROM user WHERE age IS NOT NULL ORDER BY subscribers ASC
Example (QueryArraySubquery)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	subQueryStmt := memeduck.Select("user_item", []string{"item_id", "count"}).
		Where(memeduck.Eq(memeduck.Ident("user_id"), memeduck.Param("id"))).
		AsStruct()
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		SubQuery(memeduck.ArraySubQuery(subQueryStmt).As("user_item")).
		Where(memeduck.Eq(memeduck.Ident("id"), memeduck.Param("id"))).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at, ARRAY(SELECT AS STRUCT item_id, count FROM user_item WHERE user_id = @id) AS user_item FROM user WHERE id = @id
Example (QueryMultiSubQuery)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	itemStmt := memeduck.Select("user_item", []string{"item_id", "count"}).
		Where(memeduck.Eq(memeduck.Ident("user_id"), "user-id")).
		AsStruct()
	// user has one status
	statusStmt := memeduck.Select("user_status", []string{"state"}).
		Where(memeduck.Eq(memeduck.Ident("user_id"), "user-id")).
		AsStruct()
	query, _ := memeduck.Select("user", []string{"name"}).
		SubQuery(
			memeduck.ArraySubQuery(itemStmt).As("user_item"),
			memeduck.ArraySubQuery(statusStmt).As("user_status"),
		).
		Where(memeduck.Eq(memeduck.Ident("user_id"), "user-id")).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, ARRAY(SELECT AS STRUCT item_id, count FROM user_item WHERE user_id = "user-id") AS user_item, ARRAY(SELECT AS STRUCT state FROM user_status WHERE user_id = "user-id") AS user_status FROM user WHERE user_id = "user-id"
Example (QueryParameter)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		Where(memeduck.Gt(memeduck.Ident("age"), memeduck.Param("age"))).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at FROM user WHERE age > @age
Example (QueryScalarSubquery)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	subQueryStmt := memeduck.Select("user_status", []string{"state"}).
		Where(memeduck.Eq(memeduck.Ident("user_id"), memeduck.Param("id")))
	query, _ := memeduck.Select("user", []string{"name", "created_at"}).
		SubQuery(memeduck.ScalarSubQuery(subQueryStmt).As("state")).
		Where(memeduck.Eq(memeduck.Ident("id"), memeduck.Param("id"))).
		SQL()
	fmt.Println(query)
}
Output:

SELECT name, created_at, (SELECT state FROM user_status WHERE user_id = @id) AS state FROM user WHERE id = @id

func (*SelectStmt) AsStruct added in v0.2.0

func (s *SelectStmt) AsStruct() *SelectStmt

func (*SelectStmt) ForceIndex added in v0.3.0

func (s *SelectStmt) ForceIndex(idx string) *SelectStmt

ForceIndex add a OFRCE_INDEX clause.

func (*SelectStmt) Limit

func (s *SelectStmt) Limit(limit int) *SelectStmt

Limit adds a LIMIT clause to the SELECT statement. It replaces existing LIMIT clauses.

func (*SelectStmt) LimitOffset

func (s *SelectStmt) LimitOffset(limit, offset int) *SelectStmt

LimitOffset adds a LIMIT ... OFFSET ... clause to the SELECT statement. It replaces existing LIMIT clauses.

func (*SelectStmt) OrderBy

func (s *SelectStmt) OrderBy(col string, dir Direction) *SelectStmt

OrderBy appends a column to its ORDER BY clause.

func (*SelectStmt) SQL

func (s *SelectStmt) SQL() (string, error)

func (*SelectStmt) SubQuery added in v0.2.0

func (s *SelectStmt) SubQuery(queries ...SubQuery) *SelectStmt

func (*SelectStmt) Where

func (s *SelectStmt) Where(conds ...WhereCond) *SelectStmt

Where appends given codintional expressions to the SELECT statement.

type SubQuery added in v0.2.0

type SubQuery interface {
	ToAST() (ast.SelectItem, error)
}

type UnnestInConditionValue added in v0.3.0

type UnnestInConditionValue struct {
	// contains filtered or unexported fields
}

UnnestInConditionValue is a UNNEST operator in IN clauses.

func Unnest added in v0.3.0

func Unnest(v interface{}) *UnnestInConditionValue

Unnest(v) creates `UNNEST(v)` predicate.

func (*UnnestInConditionValue) ToASTInConditionValue added in v0.3.0

func (v *UnnestInConditionValue) ToASTInConditionValue() (ast.InCondition, error)

type UpdateStmt

type UpdateStmt struct {
	// contains filtered or unexported fields
}

UpdateStmt builds UPDATE statements.

func Update

func Update(table string) *UpdateStmt

Update creates a new UpdateStmt with given table name.

Example
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Update("user").
		Set(memeduck.Ident("position"), "BOTTOM LEFT").
		Set(memeduck.Ident("immortal"), true).
		Where(
			memeduck.Eq(memeduck.Ident("color"), "orange"),
			memeduck.Eq(memeduck.Ident("manager"), true),
		).
		SQL()
	fmt.Println(query)
}
Output:

UPDATE user SET position = "BOTTOM LEFT", immortal = TRUE WHERE color = "orange" AND manager = TRUE
Example (MultipleWhere)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Update("user").
		Set(memeduck.Ident("race"), "gorilla").
		Where(memeduck.Eq(memeduck.Ident("race"), "angel")).
		Where(memeduck.Ge(memeduck.Ident("grip_strength_kg"), 50)).
		SQL()
	fmt.Println(query)
}
Output:

UPDATE user SET race = "gorilla" WHERE race = "angel" AND grip_strength_kg >= 50
Example (QueryParameter)
package main

import (
	"fmt"

	"github.com/genkami/memeduck"
)

func main() {
	query, _ := memeduck.Update("user").
		Set(memeduck.Ident("age"), memeduck.Param("age")).
		Where(memeduck.Eq(memeduck.Ident("shark"), true)).
		SQL()
	fmt.Println(query)
}
Output:

UPDATE user SET age = @age WHERE shark = TRUE

func (*UpdateStmt) SQL

func (s *UpdateStmt) SQL() (string, error)

func (*UpdateStmt) Set

func (s *UpdateStmt) Set(id *IdentExpr, value interface{}) *UpdateStmt

Set adds a assignment clause to the UPDATE statement.

func (*UpdateStmt) Where

func (s *UpdateStmt) Where(conds ...WhereCond) *UpdateStmt

Where adds a WHERE clause to the UPDATE statement.

type WhereCond

type WhereCond interface {
	ToASTWhere() (*ast.Where, error)
}

WhereCond is a conditional expression that appears in WHERE clauses.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL