init - add project files

This commit is contained in:
2025-03-06 23:54:11 -05:00
commit e724ff1120
1363 changed files with 897467 additions and 0 deletions

22
vendor/github.com/aymerick/douceur/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Aymerick JEHANNE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

60
vendor/github.com/aymerick/douceur/css/declaration.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
package css
import "fmt"
// Declaration represents a parsed style property
type Declaration struct {
Property string
Value string
Important bool
}
// NewDeclaration instanciates a new Declaration
func NewDeclaration() *Declaration {
return &Declaration{}
}
// Returns string representation of the Declaration
func (decl *Declaration) String() string {
return decl.StringWithImportant(true)
}
// StringWithImportant returns string representation with optional !important part
func (decl *Declaration) StringWithImportant(option bool) string {
result := fmt.Sprintf("%s: %s", decl.Property, decl.Value)
if option && decl.Important {
result += " !important"
}
result += ";"
return result
}
// Equal returns true if both Declarations are equals
func (decl *Declaration) Equal(other *Declaration) bool {
return (decl.Property == other.Property) && (decl.Value == other.Value) && (decl.Important == other.Important)
}
//
// DeclarationsByProperty
//
// DeclarationsByProperty represents sortable style declarations
type DeclarationsByProperty []*Declaration
// Implements sort.Interface
func (declarations DeclarationsByProperty) Len() int {
return len(declarations)
}
// Implements sort.Interface
func (declarations DeclarationsByProperty) Swap(i, j int) {
declarations[i], declarations[j] = declarations[j], declarations[i]
}
// Implements sort.Interface
func (declarations DeclarationsByProperty) Less(i, j int) bool {
return declarations[i].Property < declarations[j].Property
}

230
vendor/github.com/aymerick/douceur/css/rule.go generated vendored Normal file
View File

@@ -0,0 +1,230 @@
package css
import (
"fmt"
"strings"
)
const (
indentSpace = 2
)
// RuleKind represents a Rule kind
type RuleKind int
// Rule kinds
const (
QualifiedRule RuleKind = iota
AtRule
)
// At Rules than have Rules inside their block instead of Declarations
var atRulesWithRulesBlock = []string{
"@document", "@font-feature-values", "@keyframes", "@media", "@supports",
}
// Rule represents a parsed CSS rule
type Rule struct {
Kind RuleKind
// At Rule name (eg: "@media")
Name string
// Raw prelude
Prelude string
// Qualified Rule selectors parsed from prelude
Selectors []string
// Style properties
Declarations []*Declaration
// At Rule embedded rules
Rules []*Rule
// Current rule embedding level
EmbedLevel int
}
// NewRule instanciates a new Rule
func NewRule(kind RuleKind) *Rule {
return &Rule{
Kind: kind,
}
}
// Returns string representation of rule kind
func (kind RuleKind) String() string {
switch kind {
case QualifiedRule:
return "Qualified Rule"
case AtRule:
return "At Rule"
default:
return "WAT"
}
}
// EmbedsRules returns true if this rule embeds another rules
func (rule *Rule) EmbedsRules() bool {
if rule.Kind == AtRule {
for _, atRuleName := range atRulesWithRulesBlock {
if rule.Name == atRuleName {
return true
}
}
}
return false
}
// Equal returns true if both rules are equals
func (rule *Rule) Equal(other *Rule) bool {
if (rule.Kind != other.Kind) ||
(rule.Prelude != other.Prelude) ||
(rule.Name != other.Name) {
return false
}
if (len(rule.Selectors) != len(other.Selectors)) ||
(len(rule.Declarations) != len(other.Declarations)) ||
(len(rule.Rules) != len(other.Rules)) {
return false
}
for i, sel := range rule.Selectors {
if sel != other.Selectors[i] {
return false
}
}
for i, decl := range rule.Declarations {
if !decl.Equal(other.Declarations[i]) {
return false
}
}
for i, rule := range rule.Rules {
if !rule.Equal(other.Rules[i]) {
return false
}
}
return true
}
// Diff returns a string representation of rules differences
func (rule *Rule) Diff(other *Rule) []string {
result := []string{}
if rule.Kind != other.Kind {
result = append(result, fmt.Sprintf("Kind: %s | %s", rule.Kind.String(), other.Kind.String()))
}
if rule.Prelude != other.Prelude {
result = append(result, fmt.Sprintf("Prelude: \"%s\" | \"%s\"", rule.Prelude, other.Prelude))
}
if rule.Name != other.Name {
result = append(result, fmt.Sprintf("Name: \"%s\" | \"%s\"", rule.Name, other.Name))
}
if len(rule.Selectors) != len(other.Selectors) {
result = append(result, fmt.Sprintf("Selectors: %v | %v", strings.Join(rule.Selectors, ", "), strings.Join(other.Selectors, ", ")))
} else {
for i, sel := range rule.Selectors {
if sel != other.Selectors[i] {
result = append(result, fmt.Sprintf("Selector: \"%s\" | \"%s\"", sel, other.Selectors[i]))
}
}
}
if len(rule.Declarations) != len(other.Declarations) {
result = append(result, fmt.Sprintf("Declarations Nb: %d | %d", len(rule.Declarations), len(other.Declarations)))
} else {
for i, decl := range rule.Declarations {
if !decl.Equal(other.Declarations[i]) {
result = append(result, fmt.Sprintf("Declaration: \"%s\" | \"%s\"", decl.String(), other.Declarations[i].String()))
}
}
}
if len(rule.Rules) != len(other.Rules) {
result = append(result, fmt.Sprintf("Rules Nb: %d | %d", len(rule.Rules), len(other.Rules)))
} else {
for i, rule := range rule.Rules {
if !rule.Equal(other.Rules[i]) {
result = append(result, fmt.Sprintf("Rule: \"%s\" | \"%s\"", rule.String(), other.Rules[i].String()))
}
}
}
return result
}
// Returns the string representation of a rule
func (rule *Rule) String() string {
result := ""
if rule.Kind == QualifiedRule {
for i, sel := range rule.Selectors {
if i != 0 {
result += ", "
}
result += sel
}
} else {
// AtRule
result += fmt.Sprintf("%s", rule.Name)
if rule.Prelude != "" {
if result != "" {
result += " "
}
result += fmt.Sprintf("%s", rule.Prelude)
}
}
if (len(rule.Declarations) == 0) && (len(rule.Rules) == 0) {
result += ";"
} else {
result += " {\n"
if rule.EmbedsRules() {
for _, subRule := range rule.Rules {
result += fmt.Sprintf("%s%s\n", rule.indent(), subRule.String())
}
} else {
for _, decl := range rule.Declarations {
result += fmt.Sprintf("%s%s\n", rule.indent(), decl.String())
}
}
result += fmt.Sprintf("%s}", rule.indentEndBlock())
}
return result
}
// Returns identation spaces for declarations and rules
func (rule *Rule) indent() string {
result := ""
for i := 0; i < ((rule.EmbedLevel + 1) * indentSpace); i++ {
result += " "
}
return result
}
// Returns identation spaces for end of block character
func (rule *Rule) indentEndBlock() string {
result := ""
for i := 0; i < (rule.EmbedLevel * indentSpace); i++ {
result += " "
}
return result
}

25
vendor/github.com/aymerick/douceur/css/stylesheet.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
package css
// Stylesheet represents a parsed stylesheet
type Stylesheet struct {
Rules []*Rule
}
// NewStylesheet instanciate a new Stylesheet
func NewStylesheet() *Stylesheet {
return &Stylesheet{}
}
// Returns string representation of the Stylesheet
func (sheet *Stylesheet) String() string {
result := ""
for _, rule := range sheet.Rules {
if result != "" {
result += "\n"
}
result += rule.String()
}
return result
}

409
vendor/github.com/aymerick/douceur/parser/parser.go generated vendored Normal file
View File

@@ -0,0 +1,409 @@
package parser
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/gorilla/css/scanner"
"github.com/aymerick/douceur/css"
)
const (
importantSuffixRegexp = `(?i)\s*!important\s*$`
)
var (
importantRegexp *regexp.Regexp
)
// Parser represents a CSS parser
type Parser struct {
scan *scanner.Scanner // Tokenizer
// Tokens parsed but not consumed yet
tokens []*scanner.Token
// Rule embedding level
embedLevel int
}
func init() {
importantRegexp = regexp.MustCompile(importantSuffixRegexp)
}
// NewParser instanciates a new parser
func NewParser(txt string) *Parser {
return &Parser{
scan: scanner.New(txt),
}
}
// Parse parses a whole stylesheet
func Parse(text string) (*css.Stylesheet, error) {
result, err := NewParser(text).ParseStylesheet()
if err != nil {
return nil, err
}
return result, nil
}
// ParseDeclarations parses CSS declarations
func ParseDeclarations(text string) ([]*css.Declaration, error) {
result, err := NewParser(text).ParseDeclarations()
if err != nil {
return nil, err
}
return result, nil
}
// ParseStylesheet parses a stylesheet
func (parser *Parser) ParseStylesheet() (*css.Stylesheet, error) {
result := css.NewStylesheet()
// Parse BOM
if _, err := parser.parseBOM(); err != nil {
return result, err
}
// Parse list of rules
rules, err := parser.ParseRules()
if err != nil {
return result, err
}
result.Rules = rules
return result, nil
}
// ParseRules parses a list of rules
func (parser *Parser) ParseRules() ([]*css.Rule, error) {
result := []*css.Rule{}
inBlock := false
if parser.tokenChar("{") {
// parsing a block of rules
inBlock = true
parser.embedLevel++
parser.shiftToken()
}
for parser.tokenParsable() {
if parser.tokenIgnorable() {
parser.shiftToken()
} else if parser.tokenChar("}") {
if !inBlock {
errMsg := fmt.Sprintf("Unexpected } character: %s", parser.nextToken().String())
return result, errors.New(errMsg)
}
parser.shiftToken()
parser.embedLevel--
// finished
break
} else {
rule, err := parser.ParseRule()
if err != nil {
return result, err
}
rule.EmbedLevel = parser.embedLevel
result = append(result, rule)
}
}
return result, parser.err()
}
// ParseRule parses a rule
func (parser *Parser) ParseRule() (*css.Rule, error) {
if parser.tokenAtKeyword() {
return parser.parseAtRule()
}
return parser.parseQualifiedRule()
}
// ParseDeclarations parses a list of declarations
func (parser *Parser) ParseDeclarations() ([]*css.Declaration, error) {
result := []*css.Declaration{}
if parser.tokenChar("{") {
parser.shiftToken()
}
for parser.tokenParsable() {
if parser.tokenIgnorable() {
parser.shiftToken()
} else if parser.tokenChar("}") {
// end of block
parser.shiftToken()
break
} else {
declaration, err := parser.ParseDeclaration()
if err != nil {
return result, err
}
result = append(result, declaration)
}
}
return result, parser.err()
}
// ParseDeclaration parses a declaration
func (parser *Parser) ParseDeclaration() (*css.Declaration, error) {
result := css.NewDeclaration()
curValue := ""
for parser.tokenParsable() {
if parser.tokenChar(":") {
result.Property = strings.TrimSpace(curValue)
curValue = ""
parser.shiftToken()
} else if parser.tokenChar(";") || parser.tokenChar("}") {
if result.Property == "" {
errMsg := fmt.Sprintf("Unexpected ; character: %s", parser.nextToken().String())
return result, errors.New(errMsg)
}
if importantRegexp.MatchString(curValue) {
result.Important = true
curValue = importantRegexp.ReplaceAllString(curValue, "")
}
result.Value = strings.TrimSpace(curValue)
if parser.tokenChar(";") {
parser.shiftToken()
}
// finished
break
} else {
token := parser.shiftToken()
curValue += token.Value
}
}
// log.Printf("[parsed] Declaration: %s", result.String())
return result, parser.err()
}
// Parse an At Rule
func (parser *Parser) parseAtRule() (*css.Rule, error) {
// parse rule name (eg: "@import")
token := parser.shiftToken()
result := css.NewRule(css.AtRule)
result.Name = token.Value
for parser.tokenParsable() {
if parser.tokenChar(";") {
parser.shiftToken()
// finished
break
} else if parser.tokenChar("{") {
if result.EmbedsRules() {
// parse rules block
rules, err := parser.ParseRules()
if err != nil {
return result, err
}
result.Rules = rules
} else {
// parse declarations block
declarations, err := parser.ParseDeclarations()
if err != nil {
return result, err
}
result.Declarations = declarations
}
// finished
break
} else {
// parse prelude
prelude, err := parser.parsePrelude()
if err != nil {
return result, err
}
result.Prelude = prelude
}
}
// log.Printf("[parsed] Rule: %s", result.String())
return result, parser.err()
}
// Parse a Qualified Rule
func (parser *Parser) parseQualifiedRule() (*css.Rule, error) {
result := css.NewRule(css.QualifiedRule)
for parser.tokenParsable() {
if parser.tokenChar("{") {
if result.Prelude == "" {
errMsg := fmt.Sprintf("Unexpected { character: %s", parser.nextToken().String())
return result, errors.New(errMsg)
}
// parse declarations block
declarations, err := parser.ParseDeclarations()
if err != nil {
return result, err
}
result.Declarations = declarations
// finished
break
} else {
// parse prelude
prelude, err := parser.parsePrelude()
if err != nil {
return result, err
}
result.Prelude = prelude
}
}
result.Selectors = strings.Split(result.Prelude, ",")
for i, sel := range result.Selectors {
result.Selectors[i] = strings.TrimSpace(sel)
}
// log.Printf("[parsed] Rule: %s", result.String())
return result, parser.err()
}
// Parse Rule prelude
func (parser *Parser) parsePrelude() (string, error) {
result := ""
for parser.tokenParsable() && !parser.tokenEndOfPrelude() {
token := parser.shiftToken()
result += token.Value
}
result = strings.TrimSpace(result)
// log.Printf("[parsed] prelude: %s", result)
return result, parser.err()
}
// Parse BOM
func (parser *Parser) parseBOM() (bool, error) {
if parser.nextToken().Type == scanner.TokenBOM {
parser.shiftToken()
return true, nil
}
return false, parser.err()
}
// Returns next token without removing it from tokens buffer
func (parser *Parser) nextToken() *scanner.Token {
if len(parser.tokens) == 0 {
// fetch next token
nextToken := parser.scan.Next()
// log.Printf("[token] %s => %v", nextToken.Type.String(), nextToken.Value)
// queue it
parser.tokens = append(parser.tokens, nextToken)
}
return parser.tokens[0]
}
// Returns next token and remove it from the tokens buffer
func (parser *Parser) shiftToken() *scanner.Token {
var result *scanner.Token
result, parser.tokens = parser.tokens[0], parser.tokens[1:]
return result
}
// Returns tokenizer error, or nil if no error
func (parser *Parser) err() error {
if parser.tokenError() {
token := parser.nextToken()
return fmt.Errorf("Tokenizer error: %s", token.String())
}
return nil
}
// Returns true if next token is Error
func (parser *Parser) tokenError() bool {
return parser.nextToken().Type == scanner.TokenError
}
// Returns true if next token is EOF
func (parser *Parser) tokenEOF() bool {
return parser.nextToken().Type == scanner.TokenEOF
}
// Returns true if next token is a whitespace
func (parser *Parser) tokenWS() bool {
return parser.nextToken().Type == scanner.TokenS
}
// Returns true if next token is a comment
func (parser *Parser) tokenComment() bool {
return parser.nextToken().Type == scanner.TokenComment
}
// Returns true if next token is a CDO or a CDC
func (parser *Parser) tokenCDOorCDC() bool {
switch parser.nextToken().Type {
case scanner.TokenCDO, scanner.TokenCDC:
return true
default:
return false
}
}
// Returns true if next token is ignorable
func (parser *Parser) tokenIgnorable() bool {
return parser.tokenWS() || parser.tokenComment() || parser.tokenCDOorCDC()
}
// Returns true if next token is parsable
func (parser *Parser) tokenParsable() bool {
return !parser.tokenEOF() && !parser.tokenError()
}
// Returns true if next token is an At Rule keyword
func (parser *Parser) tokenAtKeyword() bool {
return parser.nextToken().Type == scanner.TokenAtKeyword
}
// Returns true if next token is given character
func (parser *Parser) tokenChar(value string) bool {
token := parser.nextToken()
return (token.Type == scanner.TokenChar) && (token.Value == value)
}
// Returns true if next token marks the end of a prelude
func (parser *Parser) tokenEndOfPrelude() bool {
return parser.tokenChar(";") || parser.tokenChar("{")
}

16
vendor/github.com/btcsuite/btcutil/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,16 @@
ISC License
Copyright (c) 2013-2017 The btcsuite developers
Copyright (c) 2016-2017 The Lightning Network Developers
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

34
vendor/github.com/btcsuite/btcutil/base58/README.md generated vendored Normal file
View File

@@ -0,0 +1,34 @@
base58
==========
[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/base58)
Package base58 provides an API for encoding and decoding to and from the
modified base58 encoding. It also provides an API to do Base58Check encoding,
as described [here](https://en.bitcoin.it/wiki/Base58Check_encoding).
A comprehensive suite of tests is provided to ensure proper functionality.
## Installation and Updating
```bash
$ go get -u github.com/btcsuite/btcutil/base58
```
## Examples
* [Decode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Decode)
Demonstrates how to decode modified base58 encoded data.
* [Encode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Encode)
Demonstrates how to encode data using the modified base58 encoding scheme.
* [CheckDecode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckDecode)
Demonstrates how to decode Base58Check encoded data.
* [CheckEncode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckEncode)
Demonstrates how to encode data using the Base58Check encoding scheme.
## License
Package base58 is licensed under the [copyfree](http://copyfree.org) ISC
License.

49
vendor/github.com/btcsuite/btcutil/base58/alphabet.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// AUTOGENERATED by genalphabet.go; do not edit.
package base58
const (
// alphabet is the modified base58 alphabet used by Bitcoin.
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
alphabetIdx0 = '1'
)
var b58 = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 255, 255, 255, 255, 255, 255,
255, 9, 10, 11, 12, 13, 14, 15,
16, 255, 17, 18, 19, 20, 21, 255,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 255, 255, 255, 255, 255,
255, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 255, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
}

75
vendor/github.com/btcsuite/btcutil/base58/base58.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
import (
"math/big"
)
//go:generate go run genalphabet.go
var bigRadix = big.NewInt(58)
var bigZero = big.NewInt(0)
// Decode decodes a modified base58 string to a byte slice.
func Decode(b string) []byte {
answer := big.NewInt(0)
j := big.NewInt(1)
scratch := new(big.Int)
for i := len(b) - 1; i >= 0; i-- {
tmp := b58[b[i]]
if tmp == 255 {
return []byte("")
}
scratch.SetInt64(int64(tmp))
scratch.Mul(j, scratch)
answer.Add(answer, scratch)
j.Mul(j, bigRadix)
}
tmpval := answer.Bytes()
var numZeros int
for numZeros = 0; numZeros < len(b); numZeros++ {
if b[numZeros] != alphabetIdx0 {
break
}
}
flen := numZeros + len(tmpval)
val := make([]byte, flen)
copy(val[numZeros:], tmpval)
return val
}
// Encode encodes a byte slice to a modified base58 string.
func Encode(b []byte) string {
x := new(big.Int)
x.SetBytes(b)
answer := make([]byte, 0, len(b)*136/100)
for x.Cmp(bigZero) > 0 {
mod := new(big.Int)
x.DivMod(x, bigRadix, mod)
answer = append(answer, alphabet[mod.Int64()])
}
// leading zero bytes
for _, i := range b {
if i != 0 {
break
}
answer = append(answer, alphabetIdx0)
}
// reverse
alen := len(answer)
for i := 0; i < alen/2; i++ {
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
}
return string(answer)
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
import (
"crypto/sha256"
"errors"
)
// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
// the checksum.
var ErrChecksum = errors.New("checksum error")
// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
// checksum: first four bytes of sha256^2
func checksum(input []byte) (cksum [4]byte) {
h := sha256.Sum256(input)
h2 := sha256.Sum256(h[:])
copy(cksum[:], h2[:4])
return
}
// CheckEncode prepends a version byte and appends a four byte checksum.
func CheckEncode(input []byte, version byte) string {
b := make([]byte, 0, 1+len(input)+4)
b = append(b, version)
b = append(b, input[:]...)
cksum := checksum(b)
b = append(b, cksum[:]...)
return Encode(b)
}
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
func CheckDecode(input string) (result []byte, version byte, err error) {
decoded := Decode(input)
if len(decoded) < 5 {
return nil, 0, ErrInvalidFormat
}
version = decoded[0]
var cksum [4]byte
copy(cksum[:], decoded[len(decoded)-4:])
if checksum(decoded[:len(decoded)-4]) != cksum {
return nil, 0, ErrChecksum
}
payload := decoded[1 : len(decoded)-4]
result = append(result, payload...)
return
}

View File

@@ -0,0 +1,17 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
type gocov >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
gocov test | gocov report

29
vendor/github.com/btcsuite/btcutil/base58/doc.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
Package base58 provides an API for working with modified base58 and Base58Check
encodings.
Modified Base58 Encoding
Standard base58 encoding is similar to standard base64 encoding except, as the
name implies, it uses a 58 character alphabet which results in an alphanumeric
string and allows some characters which are problematic for humans to be
excluded. Due to this, there can be various base58 alphabets.
The modified base58 alphabet used by Bitcoin, and hence this package, omits the
0, O, I, and l characters that look the same in many fonts and are therefore
hard to humans to distinguish.
Base58Check Encoding Scheme
The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
time of this writing, however it can be used to generically encode arbitrary
byte arrays into human-readable strings along with a version byte that can be
used to differentiate the same payload. For Bitcoin addresses, the extra
version is used to differentiate the network of otherwise identical public keys
which helps prevent using an address intended for one network on another.
*/
package base58

28
vendor/github.com/caarlos0/env/v11/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,28 @@
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
max_line_length = 120
[{go.mod,go.sum,*.go}]
insert_final_newline = true
indent_size = tab
indent_style = tab
tab_width = 4
[Makefile]
max_line_length = off
insert_final_newline = true
indent_size = tab
indent_style = tab
tab_width = 4
[*.md]
max_line_length = off
trim_trailing_whitespace = false
indent_size = tab
indent_style = space
tab_width = 2
[.mailmap]
max_line_length = off

5
vendor/github.com/caarlos0/env/v11/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
coverage.txt
bin
card.png
dist
codecov*

32
vendor/github.com/caarlos0/env/v11/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,32 @@
linters-settings:
gocritic:
enabled-checks:
- emptyStringTest
- evalOrder
- paramTypeCombine
- preferStringWriter
- sprintfQuotedString
- stringConcatSimplify
- yodaStyleExpr
revive:
rules:
- name: line-length-limit
arguments: [120]
issues:
exclude-rules:
- path: _test\.go
linters:
- revive
text: "line-length-limit:"
linters:
enable:
- thelper
- gofumpt
- gocritic
- tparallel
- unconvert
- unparam
- wastedassign
- revive

5
vendor/github.com/caarlos0/env/v11/.goreleaser.yml generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
version: 2
includes:
- from_url:
url: https://raw.githubusercontent.com/caarlos0/.goreleaserfiles/main/lib.yml

7
vendor/github.com/caarlos0/env/v11/.mailmap generated vendored Normal file
View File

@@ -0,0 +1,7 @@
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos A Becker <caarlos0@gmail.com>
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos A Becker <caarlos0@users.noreply.github.com>
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos Alexandro Becker <caarlos0@gmail.com>
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos Becker <caarlos0@gmail.com>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
actions-user <actions@github.com> github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

21
vendor/github.com/caarlos0/env/v11/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2024 Carlos Alexandro Becker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

37
vendor/github.com/caarlos0/env/v11/Makefile generated vendored Normal file
View File

@@ -0,0 +1,37 @@
SOURCE_FILES?=./...
TEST_PATTERN?=.
export GO111MODULE := on
setup:
go mod tidy
.PHONY: setup
build:
go build
.PHONY: build
test:
go test -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
.PHONY: test
cover: test
go tool cover -html=coverage.txt
.PHONY: cover
fmt:
gofumpt -w -l .
.PHONY: fmt
lint:
golangci-lint run ./...
.PHONY: lint
ci: build test
.PHONY: ci
card:
wget -O card.png -c "https://og.caarlos0.dev/**env**: parse envs to structs.png?theme=light&md=1&fontSize=100px&images=https://github.com/caarlos0.png"
.PHONY: card
.DEFAULT_GOAL := ci

156
vendor/github.com/caarlos0/env/v11/README.md generated vendored Normal file
View File

@@ -0,0 +1,156 @@
<p align="center">
<img alt="GoReleaser Logo" src="https://becker.software/env.png" height="140" />
<p align="center">A simple, zero-dependencies library to parse environment variables into structs.</p>
</p>
###### Installation
```bash
go get github.com/caarlos0/env/v11
```
###### Getting started
```go
type config struct {
Home string `env:"HOME"`
}
// parse
var cfg config
err := env.Parse(&cfg)
// parse with generics
cfg, err := env.ParseAs[config]()
```
You can see the full documentation and list of examples at [pkg.go.dev](https://pkg.go.dev/github.com/caarlos0/env/v11).
---
## Used and supported by
<p>
<a href="https://encore.dev">
<img src="https://user-images.githubusercontent.com/78424526/214602214-52e0483a-b5fc-4d4c-b03e-0b7b23e012df.svg" width="120px" alt="encore icon" />
</a>
<br/>
<br/>
<b>Encore the platform for building Go-based cloud backends.</b>
<br/>
</p>
## Usage
### Caveats
> [!CAUTION]
>
> _Unexported fields_ will be **ignored** by `env`.
> This is by design and will not change.
### Functions
- `Parse`: parse the current environment into a type
- `ParseAs`: parse the current environment into a type using generics
- `ParseWithOptions`: parse the current environment into a type with custom options
- `ParseAsWithOptions`: parse the current environment into a type with custom options and using generics
- `Must`: can be used to wrap `Parse.*` calls to panic on error
- `GetFieldParams`: get the `env` parsed options for a type
- `GetFieldParamsWithOptions`: get the `env` parsed options for a type with custom options
### Supported types
Out of the box all built-in types are supported, plus a few others that are commonly used.
Complete list:
- `bool`
- `float32`
- `float64`
- `int16`
- `int32`
- `int64`
- `int8`
- `int`
- `string`
- `uint16`
- `uint32`
- `uint64`
- `uint8`
- `uint`
- `time.Duration`
- `time.Location`
- `encoding.TextUnmarshaler`
- `url.URL`
Pointers, slices and slices of pointers, and maps of those types are also supported.
You may also add custom parsers for your types.
### Tags
The following tags are provided:
- `env`: sets the environment variable name and optionally takes the tag options described below
- `envDefault`: sets the default value for the field
- `envPrefix`: can be used in a field that is a complex type to set a prefix to all environment variables used in it
- `envSeparator`: sets the character to be used to separate items in slices and maps (default: `,`)
- `envKeyValSeparator`: sets the character to be used to separate keys and their values in maps (default: `:`)
### `env` tag options
Here are all the options available for the `env` tag:
- `,expand`: expands environment variables, e.g. `FOO_${BAR}`
- `,file`: instructs that the content of the variable is a path to a file that should be read
- `,init`: initialize nil pointers
- `,notEmpty`: make the field errors if the environment variable is empty
- `,required`: make the field errors if the environment variable is not set
- `,unset`: unset the environment variable after use
### Parse Options
There are a few options available in the functions that end with `WithOptions`:
- `Environment`: keys and values to be used instead of `os.Environ()`
- `TagName`: specifies another tag name to use rather than the default `env`
- `PrefixTagName`: specifies another prefix tag name to use rather than the default `envPrefix`
- `DefaultValueTagName`: specifies another default tag name to use rather than the default `envDefault`
- `RequiredIfNoDef`: set all `env` fields as required if they do not declare `envDefault`
- `OnSet`: allows to hook into the `env` parsing and do something when a value is set
- `Prefix`: prefix to be used in all environment variables
- `UseFieldNameByDefault`: defines whether or not `env` should use the field name by default if the `env` key is missing
- `FuncMap`: custom parse functions for custom types
### Documentation and examples
Examples are live in [pkg.go.dev](https://pkg.go.dev/github.com/caarlos0/env/v11),
and also in the [example test file](./example_test.go).
## Current state
`env` is considered feature-complete.
I do not intent to add any new features unless they really make sense, and are
requested by many people.
Eventual bug fixes will keep being merged.
## Badges
[![Release](https://img.shields.io/github/release/caarlos0/env.svg?style=for-the-badge)](https://github.com/goreleaser/goreleaser/releases/latest)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=for-the-badge)](/LICENSE.md)
[![Build status](https://img.shields.io/github/actions/workflow/status/caarlos0/env/build.yml?style=for-the-badge&branch=main)](https://github.com/caarlos0/env/actions?workflow=build)
[![Codecov branch](https://img.shields.io/codecov/c/github/caarlos0/env/main.svg?style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
[![Go docs](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](http://godoc.org/github.com/caarlos0/env/v11)
[![Powered By: GoReleaser](https://img.shields.io/badge/powered%20by-goreleaser-green.svg?style=for-the-badge)](https://github.com/goreleaser)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=for-the-badge)](https://conventionalcommits.org)
## Related projects
- [envdoc](https://github.com/g4s8/envdoc) - generate documentation for environment variables from `env` tags
## Stargazers over time
[![Stargazers over time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env)

845
vendor/github.com/caarlos0/env/v11/env.go generated vendored Normal file
View File

@@ -0,0 +1,845 @@
// Package env is a simple, zero-dependencies library to parse environment
// variables into structs.
//
// Example:
//
// type config struct {
// Home string `env:"HOME"`
// }
// // parse
// var cfg config
// err := env.Parse(&cfg)
// // or parse with generics
// cfg, err := env.ParseAs[config]()
//
// Check the examples and README for more detailed usage.
package env
import (
"encoding"
"fmt"
"net/url"
"os"
"reflect"
"strconv"
"strings"
"time"
"unicode"
)
// nolint: gochecknoglobals
var (
defaultBuiltInParsers = map[reflect.Kind]ParserFunc{
reflect.Bool: func(v string) (interface{}, error) {
return strconv.ParseBool(v)
},
reflect.String: func(v string) (interface{}, error) {
return v, nil
},
reflect.Int: func(v string) (interface{}, error) {
i, err := strconv.ParseInt(v, 10, 32)
return int(i), err
},
reflect.Int16: func(v string) (interface{}, error) {
i, err := strconv.ParseInt(v, 10, 16)
return int16(i), err
},
reflect.Int32: func(v string) (interface{}, error) {
i, err := strconv.ParseInt(v, 10, 32)
return int32(i), err
},
reflect.Int64: func(v string) (interface{}, error) {
return strconv.ParseInt(v, 10, 64)
},
reflect.Int8: func(v string) (interface{}, error) {
i, err := strconv.ParseInt(v, 10, 8)
return int8(i), err
},
reflect.Uint: func(v string) (interface{}, error) {
i, err := strconv.ParseUint(v, 10, 32)
return uint(i), err
},
reflect.Uint16: func(v string) (interface{}, error) {
i, err := strconv.ParseUint(v, 10, 16)
return uint16(i), err
},
reflect.Uint32: func(v string) (interface{}, error) {
i, err := strconv.ParseUint(v, 10, 32)
return uint32(i), err
},
reflect.Uint64: func(v string) (interface{}, error) {
i, err := strconv.ParseUint(v, 10, 64)
return i, err
},
reflect.Uint8: func(v string) (interface{}, error) {
i, err := strconv.ParseUint(v, 10, 8)
return uint8(i), err
},
reflect.Float64: func(v string) (interface{}, error) {
return strconv.ParseFloat(v, 64)
},
reflect.Float32: func(v string) (interface{}, error) {
f, err := strconv.ParseFloat(v, 32)
return float32(f), err
},
}
)
func defaultTypeParsers() map[reflect.Type]ParserFunc {
return map[reflect.Type]ParserFunc{
reflect.TypeOf(url.URL{}): parseURL,
reflect.TypeOf(time.Nanosecond): parseDuration,
reflect.TypeOf(time.Location{}): parseLocation,
}
}
func parseURL(v string) (interface{}, error) {
u, err := url.Parse(v)
if err != nil {
return nil, newParseValueError("unable to parse URL", err)
}
return *u, nil
}
func parseDuration(v string) (interface{}, error) {
d, err := time.ParseDuration(v)
if err != nil {
return nil, newParseValueError("unable to parse duration", err)
}
return d, err
}
func parseLocation(v string) (interface{}, error) {
loc, err := time.LoadLocation(v)
if err != nil {
return nil, newParseValueError("unable to parse location", err)
}
return *loc, nil
}
// ParserFunc defines the signature of a function that can be used within
// `Options`' `FuncMap`.
type ParserFunc func(v string) (interface{}, error)
// OnSetFn is a hook that can be run when a value is set.
type OnSetFn func(tag string, value interface{}, isDefault bool)
// processFieldFn is a function which takes all information about a field and processes it.
type processFieldFn func(
refField reflect.Value,
refTypeField reflect.StructField,
opts Options,
fieldParams FieldParams,
) error
// Options for the parser.
type Options struct {
// Environment keys and values that will be accessible for the service.
Environment map[string]string
// TagName specifies another tag name to use rather than the default 'env'.
TagName string
// PrefixTagName specifies another prefix tag name to use rather than the default 'envPrefix'.
PrefixTagName string
// DefaultValueTagName specifies another default tag name to use rather than the default 'envDefault'.
DefaultValueTagName string
// RequiredIfNoDef automatically sets all fields as required if they do not
// declare 'envDefault'.
RequiredIfNoDef bool
// OnSet allows to run a function when a value is set.
OnSet OnSetFn
// Prefix define a prefix for every key.
Prefix string
// UseFieldNameByDefault defines whether or not `env` should use the field
// name by default if the `env` key is missing.
// Note that the field name will be "converted" to conform with environment
// variable names conventions.
UseFieldNameByDefault bool
// Custom parse functions for different types.
FuncMap map[reflect.Type]ParserFunc
// Used internally. maps the env variable key to its resolved string value.
// (for env var expansion)
rawEnvVars map[string]string
}
func (opts *Options) getRawEnv(s string) string {
val := opts.rawEnvVars[s]
if val == "" {
val = opts.Environment[s]
}
return os.Expand(val, opts.getRawEnv)
}
func defaultOptions() Options {
return Options{
TagName: "env",
PrefixTagName: "envPrefix",
DefaultValueTagName: "envDefault",
Environment: toMap(os.Environ()),
FuncMap: defaultTypeParsers(),
rawEnvVars: make(map[string]string),
}
}
func mergeOptions[T any](target, source *T) {
targetPtr := reflect.ValueOf(target).Elem()
sourcePtr := reflect.ValueOf(source).Elem()
targetType := targetPtr.Type()
for i := 0; i < targetPtr.NumField(); i++ {
fieldName := targetType.Field(i).Name
targetField := targetPtr.Field(i)
sourceField := sourcePtr.FieldByName(fieldName)
if targetField.CanSet() && !isZero(sourceField) {
// FuncMaps are being merged, while Environments must be overwritten
if fieldName == "FuncMap" {
if !sourceField.IsZero() {
iter := sourceField.MapRange()
for iter.Next() {
targetField.SetMapIndex(iter.Key(), iter.Value())
}
}
} else {
targetField.Set(sourceField)
}
}
}
}
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Func, reflect.Map, reflect.Slice:
return v.IsNil()
default:
zero := reflect.Zero(v.Type())
return v.Interface() == zero.Interface()
}
}
func customOptions(opts Options) Options {
defOpts := defaultOptions()
mergeOptions(&defOpts, &opts)
return defOpts
}
func optionsWithSliceEnvPrefix(opts Options, index int) Options {
return Options{
Environment: opts.Environment,
TagName: opts.TagName,
PrefixTagName: opts.PrefixTagName,
DefaultValueTagName: opts.DefaultValueTagName,
RequiredIfNoDef: opts.RequiredIfNoDef,
OnSet: opts.OnSet,
Prefix: fmt.Sprintf("%s%d_", opts.Prefix, index),
UseFieldNameByDefault: opts.UseFieldNameByDefault,
FuncMap: opts.FuncMap,
rawEnvVars: opts.rawEnvVars,
}
}
func optionsWithEnvPrefix(field reflect.StructField, opts Options) Options {
return Options{
Environment: opts.Environment,
TagName: opts.TagName,
PrefixTagName: opts.PrefixTagName,
DefaultValueTagName: opts.DefaultValueTagName,
RequiredIfNoDef: opts.RequiredIfNoDef,
OnSet: opts.OnSet,
Prefix: opts.Prefix + field.Tag.Get(opts.PrefixTagName),
UseFieldNameByDefault: opts.UseFieldNameByDefault,
FuncMap: opts.FuncMap,
rawEnvVars: opts.rawEnvVars,
}
}
// Parse parses a struct containing `env` tags and loads its values from
// environment variables.
func Parse(v interface{}) error {
return parseInternal(v, setField, defaultOptions())
}
// ParseWithOptions parses a struct containing `env` tags and loads its values from
// environment variables.
func ParseWithOptions(v interface{}, opts Options) error {
return parseInternal(v, setField, customOptions(opts))
}
// ParseAs parses the given struct type containing `env` tags and loads its
// values from environment variables.
func ParseAs[T any]() (T, error) {
var t T
err := Parse(&t)
return t, err
}
// ParseWithOptions parses the given struct type containing `env` tags and
// loads its values from environment variables.
func ParseAsWithOptions[T any](opts Options) (T, error) {
var t T
err := ParseWithOptions(&t, opts)
return t, err
}
// Must panic is if err is not nil, and returns t otherwise.
func Must[T any](t T, err error) T {
if err != nil {
panic(err)
}
return t
}
// GetFieldParams parses a struct containing `env` tags and returns information about
// tags it found.
func GetFieldParams(v interface{}) ([]FieldParams, error) {
return GetFieldParamsWithOptions(v, defaultOptions())
}
// GetFieldParamsWithOptions parses a struct containing `env` tags and returns information about
// tags it found.
func GetFieldParamsWithOptions(v interface{}, opts Options) ([]FieldParams, error) {
var result []FieldParams
err := parseInternal(
v,
func(_ reflect.Value, _ reflect.StructField, _ Options, fieldParams FieldParams) error {
if fieldParams.OwnKey != "" {
result = append(result, fieldParams)
}
return nil
},
customOptions(opts),
)
if err != nil {
return nil, err
}
return result, nil
}
func parseInternal(v interface{}, processField processFieldFn, opts Options) error {
ptrRef := reflect.ValueOf(v)
if ptrRef.Kind() != reflect.Ptr {
return newAggregateError(NotStructPtrError{})
}
ref := ptrRef.Elem()
if ref.Kind() != reflect.Struct {
return newAggregateError(NotStructPtrError{})
}
return doParse(ref, processField, opts)
}
func doParse(ref reflect.Value, processField processFieldFn, opts Options) error {
refType := ref.Type()
var agrErr AggregateError
for i := 0; i < refType.NumField(); i++ {
refField := ref.Field(i)
refTypeField := refType.Field(i)
if err := doParseField(refField, refTypeField, processField, opts); err != nil {
if val, ok := err.(AggregateError); ok {
agrErr.Errors = append(agrErr.Errors, val.Errors...)
} else {
agrErr.Errors = append(agrErr.Errors, err)
}
}
}
if len(agrErr.Errors) == 0 {
return nil
}
return agrErr
}
func doParseField(
refField reflect.Value,
refTypeField reflect.StructField,
processField processFieldFn,
opts Options,
) error {
if !refField.CanSet() {
return nil
}
if refField.Kind() == reflect.Ptr && refField.Elem().Kind() == reflect.Struct && !refField.IsNil() {
return parseInternal(refField.Interface(), processField, optionsWithEnvPrefix(refTypeField, opts))
}
if refField.Kind() == reflect.Struct && refField.CanAddr() && refField.Type().Name() == "" {
return parseInternal(refField.Addr().Interface(), processField, optionsWithEnvPrefix(refTypeField, opts))
}
params, err := parseFieldParams(refTypeField, opts)
if err != nil {
return err
}
if params.Ignored {
return nil
}
if err := processField(refField, refTypeField, opts, params); err != nil {
return err
}
if params.Init && isInvalidPtr(refField) {
refField.Set(reflect.New(refField.Type().Elem()))
refField = refField.Elem()
}
if refField.Kind() == reflect.Struct {
return doParse(refField, processField, optionsWithEnvPrefix(refTypeField, opts))
}
if isSliceOfStructs(refTypeField) {
return doParseSlice(refField, processField, optionsWithEnvPrefix(refTypeField, opts))
}
return nil
}
func isSliceOfStructs(refTypeField reflect.StructField) bool {
field := refTypeField.Type
// *[]struct
if field.Kind() == reflect.Ptr {
field = field.Elem()
if field.Kind() == reflect.Slice && field.Elem().Kind() == reflect.Struct {
return true
}
}
// []struct{}
if field.Kind() == reflect.Slice && field.Elem().Kind() == reflect.Struct {
return true
}
return false
}
func doParseSlice(ref reflect.Value, processField processFieldFn, opts Options) error {
if opts.Prefix != "" && !strings.HasSuffix(opts.Prefix, string(underscore)) {
opts.Prefix += string(underscore)
}
var environments []string
for environment := range opts.Environment {
if strings.HasPrefix(environment, opts.Prefix) {
environments = append(environments, environment)
}
}
if len(environments) > 0 {
counter := 0
for finished := false; !finished; {
finished = true
prefix := fmt.Sprintf("%s%d%c", opts.Prefix, counter, underscore)
for _, variable := range environments {
if strings.HasPrefix(variable, prefix) {
counter++
finished = false
break
}
}
}
sliceType := ref.Type()
var initialized int
if reflect.Ptr == ref.Kind() {
sliceType = sliceType.Elem()
// Due to the rest of code the pre-initialized slice has no chance for this situation
initialized = 0
} else {
initialized = ref.Len()
}
var capacity int
if capacity = initialized; counter > initialized {
capacity = counter
}
result := reflect.MakeSlice(sliceType, capacity, capacity)
for i := 0; i < capacity; i++ {
item := result.Index(i)
if i < initialized {
item.Set(ref.Index(i))
}
if err := doParse(item, processField, optionsWithSliceEnvPrefix(opts, i)); err != nil {
return err
}
}
if result.Len() > 0 {
if reflect.Ptr == ref.Kind() {
resultPtr := reflect.New(sliceType)
resultPtr.Elem().Set(result)
result = resultPtr
}
ref.Set(result)
}
}
return nil
}
func setField(refField reflect.Value, refTypeField reflect.StructField, opts Options, fieldParams FieldParams) error {
value, err := get(fieldParams, opts)
if err != nil {
return err
}
if value != "" {
return set(refField, refTypeField, value, opts.FuncMap)
}
return nil
}
const underscore rune = '_'
func toEnvName(input string) string {
var output []rune
for i, c := range input {
if c == underscore {
continue
}
if len(output) > 0 && unicode.IsUpper(c) {
if len(input) > i+1 {
peek := rune(input[i+1])
if unicode.IsLower(peek) || unicode.IsLower(rune(input[i-1])) {
output = append(output, underscore)
}
}
}
output = append(output, unicode.ToUpper(c))
}
return string(output)
}
// FieldParams contains information about parsed field tags.
type FieldParams struct {
OwnKey string
Key string
DefaultValue string
HasDefaultValue bool
Required bool
LoadFile bool
Unset bool
NotEmpty bool
Expand bool
Init bool
Ignored bool
}
func parseFieldParams(field reflect.StructField, opts Options) (FieldParams, error) {
ownKey, tags := parseKeyForOption(field.Tag.Get(opts.TagName))
if ownKey == "" && opts.UseFieldNameByDefault {
ownKey = toEnvName(field.Name)
}
defaultValue, hasDefaultValue := field.Tag.Lookup(opts.DefaultValueTagName)
result := FieldParams{
OwnKey: ownKey,
Key: opts.Prefix + ownKey,
Required: opts.RequiredIfNoDef,
DefaultValue: defaultValue,
HasDefaultValue: hasDefaultValue,
Ignored: ownKey == "-",
}
for _, tag := range tags {
switch tag {
case "":
continue
case "file":
result.LoadFile = true
case "required":
result.Required = true
case "unset":
result.Unset = true
case "notEmpty":
result.NotEmpty = true
case "expand":
result.Expand = true
case "init":
result.Init = true
case "-":
result.Ignored = true
default:
return FieldParams{}, newNoSupportedTagOptionError(tag)
}
}
return result, nil
}
func get(fieldParams FieldParams, opts Options) (val string, err error) {
var exists, isDefault bool
val, exists, isDefault = getOr(
fieldParams.Key,
fieldParams.DefaultValue,
fieldParams.HasDefaultValue,
opts.Environment,
)
if fieldParams.Expand {
val = os.Expand(val, opts.getRawEnv)
}
opts.rawEnvVars[fieldParams.OwnKey] = val
if fieldParams.Unset {
defer os.Unsetenv(fieldParams.Key)
}
if fieldParams.Required && !exists && fieldParams.OwnKey != "" {
return "", newVarIsNotSetError(fieldParams.Key)
}
if fieldParams.NotEmpty && val == "" {
return "", newEmptyVarError(fieldParams.Key)
}
if fieldParams.LoadFile && val != "" {
filename := val
val, err = getFromFile(filename)
if err != nil {
return "", newLoadFileContentError(filename, fieldParams.Key, err)
}
}
if opts.OnSet != nil {
if fieldParams.OwnKey != "" {
opts.OnSet(fieldParams.Key, val, isDefault)
}
}
return val, err
}
// split the env tag's key into the expected key and desired option, if any.
func parseKeyForOption(key string) (string, []string) {
opts := strings.Split(key, ",")
return opts[0], opts[1:]
}
func getFromFile(filename string) (value string, err error) {
b, err := os.ReadFile(filename)
return string(b), err
}
func getOr(key, defaultValue string, defExists bool, envs map[string]string) (val string, exists, isDefault bool) {
value, exists := envs[key]
switch {
case (!exists || key == "") && defExists:
return defaultValue, true, true
case exists && value == "" && defExists:
return defaultValue, true, true
case !exists:
return "", false, false
}
return value, true, false
}
func set(field reflect.Value, sf reflect.StructField, value string, funcMap map[reflect.Type]ParserFunc) error {
if tm := asTextUnmarshaler(field); tm != nil {
if err := tm.UnmarshalText([]byte(value)); err != nil {
return newParseError(sf, err)
}
return nil
}
typee := sf.Type
fieldee := field
if typee.Kind() == reflect.Ptr {
typee = typee.Elem()
fieldee = field.Elem()
}
parserFunc, ok := funcMap[typee]
if ok {
val, err := parserFunc(value)
if err != nil {
return newParseError(sf, err)
}
fieldee.Set(reflect.ValueOf(val))
return nil
}
parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
if ok {
val, err := parserFunc(value)
if err != nil {
return newParseError(sf, err)
}
fieldee.Set(reflect.ValueOf(val).Convert(typee))
return nil
}
switch field.Kind() {
case reflect.Slice:
return handleSlice(field, value, sf, funcMap)
case reflect.Map:
return handleMap(field, value, sf, funcMap)
}
return newNoParserError(sf)
}
func handleSlice(field reflect.Value, value string, sf reflect.StructField, funcMap map[reflect.Type]ParserFunc) error {
separator := sf.Tag.Get("envSeparator")
if separator == "" {
separator = ","
}
parts := strings.Split(value, separator)
typee := sf.Type.Elem()
if typee.Kind() == reflect.Ptr {
typee = typee.Elem()
}
if _, ok := reflect.New(typee).Interface().(encoding.TextUnmarshaler); ok {
return parseTextUnmarshalers(field, parts, sf)
}
parserFunc, ok := funcMap[typee]
if !ok {
parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
if !ok {
return newNoParserError(sf)
}
}
result := reflect.MakeSlice(sf.Type, 0, len(parts))
for _, part := range parts {
r, err := parserFunc(part)
if err != nil {
return newParseError(sf, err)
}
v := reflect.ValueOf(r).Convert(typee)
if sf.Type.Elem().Kind() == reflect.Ptr {
v = reflect.New(typee)
v.Elem().Set(reflect.ValueOf(r).Convert(typee))
}
result = reflect.Append(result, v)
}
field.Set(result)
return nil
}
func handleMap(field reflect.Value, value string, sf reflect.StructField, funcMap map[reflect.Type]ParserFunc) error {
keyType := sf.Type.Key()
keyParserFunc, ok := funcMap[keyType]
if !ok {
keyParserFunc, ok = defaultBuiltInParsers[keyType.Kind()]
if !ok {
return newNoParserError(sf)
}
}
elemType := sf.Type.Elem()
elemParserFunc, ok := funcMap[elemType]
if !ok {
elemParserFunc, ok = defaultBuiltInParsers[elemType.Kind()]
if !ok {
return newNoParserError(sf)
}
}
separator := sf.Tag.Get("envSeparator")
if separator == "" {
separator = ","
}
keyValSeparator := sf.Tag.Get("envKeyValSeparator")
if keyValSeparator == "" {
keyValSeparator = ":"
}
result := reflect.MakeMap(sf.Type)
for _, part := range strings.Split(value, separator) {
pairs := strings.SplitN(part, keyValSeparator, 2)
if len(pairs) != 2 {
return newParseError(sf, fmt.Errorf(`%q should be in "key%svalue" format`, part, keyValSeparator))
}
key, err := keyParserFunc(pairs[0])
if err != nil {
return newParseError(sf, err)
}
elem, err := elemParserFunc(pairs[1])
if err != nil {
return newParseError(sf, err)
}
result.SetMapIndex(reflect.ValueOf(key).Convert(keyType), reflect.ValueOf(elem).Convert(elemType))
}
field.Set(result)
return nil
}
func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler {
if field.Kind() == reflect.Ptr {
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
}
} else if field.CanAddr() {
field = field.Addr()
}
tm, ok := field.Interface().(encoding.TextUnmarshaler)
if !ok {
return nil
}
return tm
}
func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.StructField) error {
s := len(data)
elemType := field.Type().Elem()
slice := reflect.MakeSlice(reflect.SliceOf(elemType), s, s)
for i, v := range data {
sv := slice.Index(i)
kind := sv.Kind()
if kind == reflect.Ptr {
sv = reflect.New(elemType.Elem())
} else {
sv = sv.Addr()
}
tm := sv.Interface().(encoding.TextUnmarshaler)
if err := tm.UnmarshalText([]byte(v)); err != nil {
return newParseError(sf, err)
}
if kind == reflect.Ptr {
slice.Index(i).Set(sv)
}
}
field.Set(slice)
return nil
}
// ToMap Converts list of env vars as provided by os.Environ() to map you
// can use as Options.Environment field
func ToMap(env []string) map[string]string {
return toMap(env)
}
func isInvalidPtr(v reflect.Value) bool {
return reflect.Ptr == v.Kind() && v.Elem().Kind() == reflect.Invalid
}

16
vendor/github.com/caarlos0/env/v11/env_tomap.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
//go:build !windows
package env
import "strings"
func toMap(env []string) map[string]string {
r := map[string]string{}
for _, e := range env {
p := strings.SplitN(e, "=", 2)
if len(p) == 2 {
r[p[0]] = p[1]
}
}
return r
}

View File

@@ -0,0 +1,29 @@
//go:build windows
package env
import "strings"
func toMap(env []string) map[string]string {
r := map[string]string{}
for _, e := range env {
p := strings.SplitN(e, "=", 2)
// On Windows, environment variables can start with '='. If so, Split at next character.
// See env_windows.go in the Go source: https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L58
prefixEqualSign := false
if len(e) > 0 && e[0] == '=' {
e = e[1:]
prefixEqualSign = true
}
p = strings.SplitN(e, "=", 2)
if prefixEqualSign {
p[0] = "=" + p[0]
}
if len(p) == 2 {
r[p[0]] = p[1]
}
}
return r
}

173
vendor/github.com/caarlos0/env/v11/error.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
package env
import (
"fmt"
"reflect"
"strings"
)
// An aggregated error wrapper to combine gathered errors.
// This allows either to display all errors or convert them individually
// List of the available errors
// ParseError
// NotStructPtrError
// NoParserError
// NoSupportedTagOptionError
// VarIsNotSetError
// EmptyVarError
// LoadFileContentError
// ParseValueError
type AggregateError struct {
Errors []error
}
func newAggregateError(initErr error) error {
return AggregateError{
[]error{
initErr,
},
}
}
func (e AggregateError) Error() string {
var sb strings.Builder
sb.WriteString("env:")
for _, err := range e.Errors {
sb.WriteString(fmt.Sprintf(" %v;", err.Error()))
}
return strings.TrimRight(sb.String(), ";")
}
// Unwrap implements std errors.Join go1.20 compatibility
func (e AggregateError) Unwrap() []error {
return e.Errors
}
// Is conforms with errors.Is.
func (e AggregateError) Is(err error) bool {
for _, ie := range e.Errors {
if reflect.TypeOf(ie) == reflect.TypeOf(err) {
return true
}
}
return false
}
// The error occurs when it's impossible to convert the value for given type.
type ParseError struct {
Name string
Type reflect.Type
Err error
}
func newParseError(sf reflect.StructField, err error) error {
return ParseError{sf.Name, sf.Type, err}
}
func (e ParseError) Error() string {
return fmt.Sprintf("parse error on field %q of type %q: %v", e.Name, e.Type, e.Err)
}
// The error occurs when pass something that is not a pointer to a struct to Parse
type NotStructPtrError struct{}
func (e NotStructPtrError) Error() string {
return "expected a pointer to a Struct"
}
// This error occurs when there is no parser provided for given type.
type NoParserError struct {
Name string
Type reflect.Type
}
func newNoParserError(sf reflect.StructField) error {
return NoParserError{sf.Name, sf.Type}
}
func (e NoParserError) Error() string {
return fmt.Sprintf("no parser found for field %q of type %q", e.Name, e.Type)
}
// This error occurs when the given tag is not supported.
// Built-in supported tags: "", "file", "required", "unset", "notEmpty",
// "expand", "envDefault", and "envSeparator".
type NoSupportedTagOptionError struct {
Tag string
}
func newNoSupportedTagOptionError(tag string) error {
return NoSupportedTagOptionError{tag}
}
func (e NoSupportedTagOptionError) Error() string {
return fmt.Sprintf("tag option %q not supported", e.Tag)
}
// This error occurs when the required variable is not set.
//
// Deprecated: use VarIsNotSetError.
type EnvVarIsNotSetError = VarIsNotSetError
// This error occurs when the required variable is not set.
type VarIsNotSetError struct {
Key string
}
func newVarIsNotSetError(key string) error {
return VarIsNotSetError{key}
}
func (e VarIsNotSetError) Error() string {
return fmt.Sprintf(`required environment variable %q is not set`, e.Key)
}
// This error occurs when the variable which must be not empty is existing but has an empty value
//
// Deprecated: use EmptyVarError.
type EmptyEnvVarError = EmptyVarError
// This error occurs when the variable which must be not empty is existing but has an empty value
type EmptyVarError struct {
Key string
}
func newEmptyVarError(key string) error {
return EmptyVarError{key}
}
func (e EmptyVarError) Error() string {
return fmt.Sprintf("environment variable %q should not be empty", e.Key)
}
// This error occurs when it's impossible to load the value from the file.
type LoadFileContentError struct {
Filename string
Key string
Err error
}
func newLoadFileContentError(filename, key string, err error) error {
return LoadFileContentError{filename, key, err}
}
func (e LoadFileContentError) Error() string {
return fmt.Sprintf("could not load content of file %q from variable %s: %v", e.Filename, e.Key, e.Err)
}
// This error occurs when it's impossible to convert value using given parser.
type ParseValueError struct {
Msg string
Err error
}
func newParseValueError(message string, err error) error {
return ParseValueError{message, err}
}
func (e ParseValueError) Error() string {
return fmt.Sprintf("%s: %v", e.Msg, e.Err)
}

15
vendor/github.com/davecgh/go-spew/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

145
vendor/github.com/davecgh/go-spew/spew/bypass.go generated vendored Normal file
View File

@@ -0,0 +1,145 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
import (
"reflect"
"unsafe"
)
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = false
// ptrSize is the size of a pointer on the current arch.
ptrSize = unsafe.Sizeof((*byte)(nil))
)
type flag uintptr
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and
// unexported data. It works by digging the raw pointer to the underlying
// value out of the protected value and generating a new unprotected (unsafe)
// reflect.Value to it.
//
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
panic("reflect.Value read-only flag has changed semantics")
}

38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe !go1.4
package spew
import "reflect"
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = true
)
// unsafeReflectValue typically converts the passed reflect.Value into a one
// that bypasses the typical safety restrictions preventing access to
// unaddressable and unexported data. However, doing this relies on access to
// the unsafe package. This is a stub version which simply returns the passed
// reflect.Value when the unsafe package is not available.
func unsafeReflectValue(v reflect.Value) reflect.Value {
return v
}

341
vendor/github.com/davecgh/go-spew/spew/common.go generated vendored Normal file
View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strconv"
)
// Some constants in the form of bytes to avoid string overhead. This mirrors
// the technique used in the fmt package.
var (
panicBytes = []byte("(PANIC=")
plusBytes = []byte("+")
iBytes = []byte("i")
trueBytes = []byte("true")
falseBytes = []byte("false")
interfaceBytes = []byte("(interface {})")
commaNewlineBytes = []byte(",\n")
newlineBytes = []byte("\n")
openBraceBytes = []byte("{")
openBraceNewlineBytes = []byte("{\n")
closeBraceBytes = []byte("}")
asteriskBytes = []byte("*")
colonBytes = []byte(":")
colonSpaceBytes = []byte(": ")
openParenBytes = []byte("(")
closeParenBytes = []byte(")")
spaceBytes = []byte(" ")
pointerChainBytes = []byte("->")
nilAngleBytes = []byte("<nil>")
maxNewlineBytes = []byte("<max depth reached>\n")
maxShortBytes = []byte("<max>")
circularBytes = []byte("<already shown>")
circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>")
openBracketBytes = []byte("[")
closeBracketBytes = []byte("]")
percentBytes = []byte("%")
precisionBytes = []byte(".")
openAngleBytes = []byte("<")
closeAngleBytes = []byte(">")
openMapBytes = []byte("map[")
closeMapBytes = []byte("]")
lenEqualsBytes = []byte("len=")
capEqualsBytes = []byte("cap=")
)
// hexDigits is used to map a decimal value to a hex digit.
var hexDigits = "0123456789abcdef"
// catchPanic handles any panics that might occur during the handleMethods
// calls.
func catchPanic(w io.Writer, v reflect.Value) {
if err := recover(); err != nil {
w.Write(panicBytes)
fmt.Fprintf(w, "%v", err)
w.Write(closeParenBytes)
}
}
// handleMethods attempts to call the Error and String methods on the underlying
// type the passed reflect.Value represents and outputes the result to Writer w.
//
// It handles panics in any called methods by catching and displaying the error
// as the formatted value.
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// We need an interface to check if the type implements the error or
// Stringer interface. However, the reflect package won't give us an
// interface on certain things like unexported struct fields in order
// to enforce visibility rules. We use unsafe, when it's available,
// to bypass these restrictions since this package does not mutate the
// values.
if !v.CanInterface() {
if UnsafeDisabled {
return false
}
v = unsafeReflectValue(v)
}
// Choose whether or not to do error and Stringer interface lookups against
// the base type or a pointer to the base type depending on settings.
// Technically calling one of these methods with a pointer receiver can
// mutate the value, however, types which choose to satisify an error or
// Stringer interface with a pointer receiver should not be mutating their
// state inside these interface methods.
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
v = unsafeReflectValue(v)
}
if v.CanAddr() {
v = v.Addr()
}
// Is it an error or Stringer?
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.Error()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.Error()))
return true
case fmt.Stringer:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.String()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.String()))
return true
}
return false
}
// printBool outputs a boolean value as true or false to Writer w.
func printBool(w io.Writer, val bool) {
if val {
w.Write(trueBytes)
} else {
w.Write(falseBytes)
}
}
// printInt outputs a signed integer value to Writer w.
func printInt(w io.Writer, val int64, base int) {
w.Write([]byte(strconv.FormatInt(val, base)))
}
// printUint outputs an unsigned integer value to Writer w.
func printUint(w io.Writer, val uint64, base int) {
w.Write([]byte(strconv.FormatUint(val, base)))
}
// printFloat outputs a floating point value using the specified precision,
// which is expected to be 32 or 64bit, to Writer w.
func printFloat(w io.Writer, val float64, precision int) {
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
}
// printComplex outputs a complex value using the specified float precision
// for the real and imaginary parts to Writer w.
func printComplex(w io.Writer, c complex128, floatPrecision int) {
r := real(c)
w.Write(openParenBytes)
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
i := imag(c)
if i >= 0 {
w.Write(plusBytes)
}
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
w.Write(iBytes)
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.
num := uint64(p)
if num == 0 {
w.Write(nilAngleBytes)
return
}
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
buf := make([]byte, 18)
// It's simpler to construct the hex string right to left.
base := uint64(16)
i := len(buf) - 1
for num >= base {
buf[i] = hexDigits[num%base]
num /= base
i--
}
buf[i] = hexDigits[num]
// Add '0x' prefix.
i--
buf[i] = 'x'
i--
buf[i] = '0'
// Strip unused leading bytes.
buf = buf[i:]
w.Write(buf)
}
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
// elements to be sorted.
type valuesSorter struct {
values []reflect.Value
strings []string // either nil or same len and values
cs *ConfigState
}
// newValuesSorter initializes a valuesSorter instance, which holds a set of
// surrogate keys on which the data should be sorted. It uses flags in
// ConfigState to decide if and how to populate those surrogate keys.
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
vs := &valuesSorter{values: values, cs: cs}
if canSortSimply(vs.values[0].Kind()) {
return vs
}
if !cs.DisableMethods {
vs.strings = make([]string, len(values))
for i := range vs.values {
b := bytes.Buffer{}
if !handleMethods(cs, &b, vs.values[i]) {
vs.strings = nil
break
}
vs.strings[i] = b.String()
}
}
if vs.strings == nil && cs.SpewKeys {
vs.strings = make([]string, len(values))
for i := range vs.values {
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
}
}
return vs
}
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
// directly, or whether it should be considered for sorting by surrogate keys
// (if the ConfigState allows it).
func canSortSimply(kind reflect.Kind) bool {
// This switch parallels valueSortLess, except for the default case.
switch kind {
case reflect.Bool:
return true
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return true
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.String:
return true
case reflect.Uintptr:
return true
case reflect.Array:
return true
}
return false
}
// Len returns the number of values in the slice. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Len() int {
return len(s.values)
}
// Swap swaps the values at the passed indices. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Swap(i, j int) {
s.values[i], s.values[j] = s.values[j], s.values[i]
if s.strings != nil {
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
}
}
// valueSortLess returns whether the first value should sort before the second
// value. It is used by valueSorter.Less as part of the sort.Interface
// implementation.
func valueSortLess(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Bool:
return !a.Bool() && b.Bool()
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return a.Int() < b.Int()
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return a.Uint() < b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.String:
return a.String() < b.String()
case reflect.Uintptr:
return a.Uint() < b.Uint()
case reflect.Array:
// Compare the contents of both arrays.
l := a.Len()
for i := 0; i < l; i++ {
av := a.Index(i)
bv := b.Index(i)
if av.Interface() == bv.Interface() {
continue
}
return valueSortLess(av, bv)
}
}
return a.String() < b.String()
}
// Less returns whether the value at index i should sort before the
// value at index j. It is part of the sort.Interface implementation.
func (s *valuesSorter) Less(i, j int) bool {
if s.strings == nil {
return valueSortLess(s.values[i], s.values[j])
}
return s.strings[i] < s.strings[j]
}
// sortValues is a sort function that handles both native types and any type that
// can be converted to error or Stringer. Other inputs are sorted according to
// their Value.String() value to ensure display stability.
func sortValues(values []reflect.Value, cs *ConfigState) {
if len(values) == 0 {
return
}
sort.Sort(newValuesSorter(values, cs))
}

306
vendor/github.com/davecgh/go-spew/spew/config.go generated vendored Normal file
View File

@@ -0,0 +1,306 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"os"
)
// ConfigState houses the configuration options used by spew to format and
// display values. There is a global instance, Config, that is used to control
// all top-level Formatter and Dump functionality. Each ConfigState instance
// provides methods equivalent to the top-level functions.
//
// The zero value for ConfigState provides no indentation. You would typically
// want to set it to a space or a tab.
//
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
// with default settings. See the documentation of NewDefaultConfig for default
// values.
type ConfigState struct {
// Indent specifies the string to use for each indentation level. The
// global config instance that all top-level functions use set this to a
// single space by default. If you would like more indentation, you might
// set this to a tab with "\t" or perhaps two spaces with " ".
Indent string
// MaxDepth controls the maximum number of levels to descend into nested
// data structures. The default, 0, means there is no limit.
//
// NOTE: Circular data structures are properly detected, so it is not
// necessary to set this value unless you specifically want to limit deeply
// nested data structures.
MaxDepth int
// DisableMethods specifies whether or not error and Stringer interfaces are
// invoked for types that implement them.
DisableMethods bool
// DisablePointerMethods specifies whether or not to check for and invoke
// error and Stringer interfaces on types which only accept a pointer
// receiver when the current type is not a pointer.
//
// NOTE: This might be an unsafe action since calling one of these methods
// with a pointer receiver could technically mutate the value, however,
// in practice, types which choose to satisify an error or Stringer
// interface with a pointer receiver should not be mutating their state
// inside these interface methods. As a result, this option relies on
// access to the unsafe package, so it will not have any effect when
// running in environments without access to the unsafe package such as
// Google App Engine or with the "safe" build tag specified.
DisablePointerMethods bool
// DisablePointerAddresses specifies whether to disable the printing of
// pointer addresses. This is useful when diffing data structures in tests.
DisablePointerAddresses bool
// DisableCapacities specifies whether to disable the printing of capacities
// for arrays, slices, maps and channels. This is useful when diffing
// data structures in tests.
DisableCapacities bool
// ContinueOnMethod specifies whether or not recursion should continue once
// a custom error or Stringer interface is invoked. The default, false,
// means it will print the results of invoking the custom error or Stringer
// interface and return immediately instead of continuing to recurse into
// the internals of the data type.
//
// NOTE: This flag does not have any effect if method invocation is disabled
// via the DisableMethods or DisablePointerMethods options.
ContinueOnMethod bool
// SortKeys specifies map keys should be sorted before being printed. Use
// this to have a more deterministic, diffable output. Note that only
// native types (bool, int, uint, floats, uintptr and string) and types
// that support the error or Stringer interfaces (if methods are
// enabled) are supported, with other types sorted according to the
// reflect.Value.String() output which guarantees display stability.
SortKeys bool
// SpewKeys specifies that, as a last resort attempt, map keys should
// be spewed to strings and sorted by those strings. This is only
// considered if SortKeys is true.
SpewKeys bool
}
// Config is the active configuration of the top-level functions.
// The configuration can be changed by modifying the contents of spew.Config.
var Config = ConfigState{Indent: " "}
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the formatted string as a value that satisfies error. See NewFormatter
// for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, c.convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, c.convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, c.convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a Formatter interface returned by c.NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, c.convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
return fmt.Print(c.convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, c.convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
return fmt.Println(c.convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprint(a ...interface{}) string {
return fmt.Sprint(c.convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, c.convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a Formatter interface returned by c.NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintln(a ...interface{}) string {
return fmt.Sprintln(c.convertArgs(a)...)
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
c.Printf, c.Println, or c.Printf.
*/
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(c, v)
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
fdump(c, w, a...)
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by modifying the public members
of c. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func (c *ConfigState) Dump(a ...interface{}) {
fdump(c, os.Stdout, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func (c *ConfigState) Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(c, &buf, a...)
return buf.String()
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a spew Formatter interface using
// the ConfigState associated with s.
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = newFormatter(c, arg)
}
return formatters
}
// NewDefaultConfig returns a ConfigState with the following default settings.
//
// Indent: " "
// MaxDepth: 0
// DisableMethods: false
// DisablePointerMethods: false
// ContinueOnMethod: false
// SortKeys: false
func NewDefaultConfig() *ConfigState {
return &ConfigState{Indent: " "}
}

211
vendor/github.com/davecgh/go-spew/spew/doc.go generated vendored Normal file
View File

@@ -0,0 +1,211 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.
A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
There are two different approaches spew allows for dumping Go data structures:
* Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
* A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
Quick Start
This section demonstrates how to quickly get started with spew. See the
sections below for further details on formatting and configuration options.
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
via the spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
The following configuration options are available:
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of
capacities for arrays, slices, maps and channels. This is useful when
diffing data structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are
supported with other types sorted according to the
reflect.Value.String() output which guarantees display
stability. Natural map order is used by default.
* SpewKeys
Specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only
considered if SortKeys is true.
Dump Usage
Simply call spew.Dump with a list of variables you want to dump:
spew.Dump(myVar1, myVar2, ...)
You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer. For example, to dump to standard error:
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
A third option is to call spew.Sdump to get the formatted output as a string:
str := spew.Sdump(myVar1, myVar2, ...)
Sample Dump Output
See the Dump example for details on the setup of the types and variables being
shown here.
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) (len=1) {
(string) (len=3) "one": (bool) true
}
}
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) (len=32 cap=32) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
Custom Formatter
Spew provides a custom formatter that implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Custom Formatter Usage
The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
functions have syntax you are most likely already familiar with:
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Println(myVar, myVar2)
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
See the Index for the full list convenience functions.
Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
See the Printf example for details on the setup of variables being shown
here.
Errors
Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output. Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew

509
vendor/github.com/davecgh/go-spew/spew/dump.go generated vendored Normal file
View File

@@ -0,0 +1,509 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
// uint8Type is a reflect.Type representing a uint8. It is used to
// convert cgo types to uint8 slices for hexdumping.
uint8Type = reflect.TypeOf(uint8(0))
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
type dumpState struct {
w io.Writer
depth int
pointers map[uintptr]int
ignoreNextType bool
ignoreNextIndent bool
cs *ConfigState
}
// indent performs indentation according to the depth level and cs.Indent
// option.
func (d *dumpState) indent() {
if d.ignoreNextIndent {
d.ignoreNextIndent = false
return
}
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
}
// unpackValue returns values inside of non-nil interfaces when possible.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
return v
}
// dumpPtr handles formatting of pointers by indirecting them as necessary.
func (d *dumpState) dumpPtr(v reflect.Value) {
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range d.pointers {
if depth >= d.depth {
delete(d.pointers, k)
}
}
// Keep list of all dereferenced pointers to show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by dereferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
cycleFound = true
indirects--
break
}
d.pointers[addr] = d.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type information.
d.w.Write(openParenBytes)
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
d.w.Write([]byte(ve.Type().String()))
d.w.Write(closeParenBytes)
// Display pointer information.
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
d.w.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
d.w.Write(pointerChainBytes)
}
printHexPtr(d.w, addr)
}
d.w.Write(closeParenBytes)
}
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound:
d.w.Write(circularBytes)
default:
d.ignoreNextType = true
d.dump(ve)
}
d.w.Write(closeParenBytes)
}
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
// reflection) arrays and slices are dumped in hexdump -C fashion.
func (d *dumpState) dumpSlice(v reflect.Value) {
// Determine whether this type should be hex dumped or not. Also,
// for types which should be hexdumped, try to use the underlying data
// first, then fall back to trying to convert them to a uint8 slice.
var buf []uint8
doConvert := false
doHexDump := false
numEntries := v.Len()
if numEntries > 0 {
vt := v.Index(0).Type()
vts := vt.String()
switch {
// C types that need to be converted.
case cCharRE.MatchString(vts):
fallthrough
case cUnsignedCharRE.MatchString(vts):
fallthrough
case cUint8tCharRE.MatchString(vts):
doConvert = true
// Try to use existing uint8 slices and fall back to converting
// and copying if that fails.
case vt.Kind() == reflect.Uint8:
// We need an addressable interface to convert the type
// to a byte slice. However, the reflect package won't
// give us an interface on certain things like
// unexported struct fields in order to enforce
// visibility rules. We use unsafe, when available, to
// bypass these restrictions since this package does not
// mutate the values.
vs := v
if !vs.CanInterface() || !vs.CanAddr() {
vs = unsafeReflectValue(vs)
}
if !UnsafeDisabled {
vs = vs.Slice(0, numEntries)
// Use the existing uint8 slice if it can be
// type asserted.
iface := vs.Interface()
if slice, ok := iface.([]uint8); ok {
buf = slice
doHexDump = true
break
}
}
// The underlying data needs to be converted if it can't
// be type asserted to a uint8 slice.
doConvert = true
}
// Copy and convert the underlying type if needed.
if doConvert && vt.ConvertibleTo(uint8Type) {
// Convert and copy each element into a uint8 byte
// slice.
buf = make([]uint8, numEntries)
for i := 0; i < numEntries; i++ {
vv := v.Index(i)
buf[i] = uint8(vv.Convert(uint8Type).Uint())
}
doHexDump = true
}
}
// Hexdump the entire slice as needed.
if doHexDump {
indent := strings.Repeat(d.cs.Indent, d.depth)
str := indent + hex.Dump(buf)
str = strings.Replace(str, "\n", "\n"+indent, -1)
str = strings.TrimRight(str, d.cs.Indent)
d.w.Write([]byte(str))
return
}
// Recursively call dump for each item.
for i := 0; i < numEntries; i++ {
d.dump(d.unpackValue(v.Index(i)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
// dump is the main workhorse for dumping a value. It uses the passed reflect
// value to figure out what kind of object we are dealing with and formats it
// appropriately. It is a recursive function, however circular data structures
// are detected and handled properly.
func (d *dumpState) dump(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
d.w.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
d.indent()
d.dumpPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !d.ignoreNextType {
d.indent()
d.w.Write(openParenBytes)
d.w.Write([]byte(v.Type().String()))
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
d.ignoreNextType = false
// Display length and capacity if the built-in len and cap functions
// work with the value's kind and the len/cap itself is non-zero.
valueLen, valueCap := 0, 0
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan:
valueLen, valueCap = v.Len(), v.Cap()
case reflect.Map, reflect.String:
valueLen = v.Len()
}
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
d.w.Write(openParenBytes)
if valueLen != 0 {
d.w.Write(lenEqualsBytes)
printInt(d.w, int64(valueLen), 10)
}
if !d.cs.DisableCapacities && valueCap != 0 {
if valueLen != 0 {
d.w.Write(spaceBytes)
}
d.w.Write(capEqualsBytes)
printInt(d.w, int64(valueCap), 10)
}
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
// Call Stringer/error interfaces if they exist and the handle methods flag
// is enabled
if !d.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(d.cs, d.w, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(d.w, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(d.w, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(d.w, v.Uint(), 10)
case reflect.Float32:
printFloat(d.w, v.Float(), 32)
case reflect.Float64:
printFloat(d.w, v.Float(), 64)
case reflect.Complex64:
printComplex(d.w, v.Complex(), 32)
case reflect.Complex128:
printComplex(d.w, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
d.dumpSlice(v)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.String:
d.w.Write([]byte(strconv.Quote(v.String())))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
d.w.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
numEntries := v.Len()
keys := v.MapKeys()
if d.cs.SortKeys {
sortValues(keys, d.cs)
}
for i, key := range keys {
d.dump(d.unpackValue(key))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.MapIndex(key)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Struct:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
vt := v.Type()
numFields := v.NumField()
for i := 0; i < numFields; i++ {
d.indent()
vtf := vt.Field(i)
d.w.Write([]byte(vtf.Name))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.Field(i)))
if i < (numFields - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(d.w, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(d.w, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it in case any new
// types are added.
default:
if v.CanInterface() {
fmt.Fprintf(d.w, "%v", v.Interface())
} else {
fmt.Fprintf(d.w, "%v", v.String())
}
}
}
// fdump is a helper function to consolidate the logic from the various public
// methods which take varying writers and config states.
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
for _, arg := range a {
if arg == nil {
w.Write(interfaceBytes)
w.Write(spaceBytes)
w.Write(nilAngleBytes)
w.Write(newlineBytes)
continue
}
d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes)
}
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func Fdump(w io.Writer, a ...interface{}) {
fdump(&Config, w, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(&Config, &buf, a...)
return buf.String()
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by an exported package global,
spew.Config. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func Dump(a ...interface{}) {
fdump(&Config, os.Stdout, a...)
}

419
vendor/github.com/davecgh/go-spew/spew/format.go generated vendored Normal file
View File

@@ -0,0 +1,419 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"reflect"
"strconv"
"strings"
)
// supportedFlags is a list of all the character flags supported by fmt package.
const supportedFlags = "0-+# "
// formatState implements the fmt.Formatter interface and contains information
// about the state of a formatting operation. The NewFormatter function can
// be used to get a new Formatter which can be used directly as arguments
// in standard fmt package printing calls.
type formatState struct {
value interface{}
fs fmt.State
depth int
pointers map[uintptr]int
ignoreNextType bool
cs *ConfigState
}
// buildDefaultFormat recreates the original format string without precision
// and width information to pass in to fmt.Sprintf in the case of an
// unrecognized type. Unless new types are added to the language, this
// function won't ever be called.
func (f *formatState) buildDefaultFormat() (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
buf.WriteRune('v')
format = buf.String()
return format
}
// constructOrigFormat recreates the original format string including precision
// and width information to pass along to the standard fmt package. This allows
// automatic deferral of all format strings this package doesn't support.
func (f *formatState) constructOrigFormat(verb rune) (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
if width, ok := f.fs.Width(); ok {
buf.WriteString(strconv.Itoa(width))
}
if precision, ok := f.fs.Precision(); ok {
buf.Write(precisionBytes)
buf.WriteString(strconv.Itoa(precision))
}
buf.WriteRune(verb)
format = buf.String()
return format
}
// unpackValue returns values inside of non-nil interfaces when possible and
// ensures that types for values which have been unpacked from an interface
// are displayed when the show types flag is also set.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface {
f.ignoreNextType = false
if !v.IsNil() {
v = v.Elem()
}
}
return v
}
// formatPtr handles formatting of pointers by indirecting them as necessary.
func (f *formatState) formatPtr(v reflect.Value) {
// Display nil if top level pointer is nil.
showTypes := f.fs.Flag('#')
if v.IsNil() && (!showTypes || f.ignoreNextType) {
f.fs.Write(nilAngleBytes)
return
}
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range f.pointers {
if depth >= f.depth {
delete(f.pointers, k)
}
}
// Keep list of all dereferenced pointers to possibly show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by derferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
cycleFound = true
indirects--
break
}
f.pointers[addr] = f.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type or indirection level depending on flags.
if showTypes && !f.ignoreNextType {
f.fs.Write(openParenBytes)
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
f.fs.Write([]byte(ve.Type().String()))
f.fs.Write(closeParenBytes)
} else {
if nilFound || cycleFound {
indirects += strings.Count(ve.Type().String(), "*")
}
f.fs.Write(openAngleBytes)
f.fs.Write([]byte(strings.Repeat("*", indirects)))
f.fs.Write(closeAngleBytes)
}
// Display pointer information depending on flags.
if f.fs.Flag('+') && (len(pointerChain) > 0) {
f.fs.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
f.fs.Write(pointerChainBytes)
}
printHexPtr(f.fs, addr)
}
f.fs.Write(closeParenBytes)
}
// Display dereferenced value.
switch {
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound:
f.fs.Write(circularShortBytes)
default:
f.ignoreNextType = true
f.format(ve)
}
}
// format is the main workhorse for providing the Formatter interface. It
// uses the passed reflect value to figure out what kind of object we are
// dealing with and formats it appropriately. It is a recursive function,
// however circular data structures are detected and handled properly.
func (f *formatState) format(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
f.fs.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
f.formatPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !f.ignoreNextType && f.fs.Flag('#') {
f.fs.Write(openParenBytes)
f.fs.Write([]byte(v.Type().String()))
f.fs.Write(closeParenBytes)
}
f.ignoreNextType = false
// Call Stringer/error interfaces if they exist and the handle methods
// flag is enabled.
if !f.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(f.cs, f.fs, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(f.fs, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(f.fs, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(f.fs, v.Uint(), 10)
case reflect.Float32:
printFloat(f.fs, v.Float(), 32)
case reflect.Float64:
printFloat(f.fs, v.Float(), 64)
case reflect.Complex64:
printComplex(f.fs, v.Complex(), 32)
case reflect.Complex128:
printComplex(f.fs, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
f.fs.Write(openBracketBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
numEntries := v.Len()
for i := 0; i < numEntries; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(v.Index(i)))
}
}
f.depth--
f.fs.Write(closeBracketBytes)
case reflect.String:
f.fs.Write([]byte(v.String()))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
f.fs.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
f.fs.Write(openMapBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
keys := v.MapKeys()
if f.cs.SortKeys {
sortValues(keys, f.cs)
}
for i, key := range keys {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(key))
f.fs.Write(colonBytes)
f.ignoreNextType = true
f.format(f.unpackValue(v.MapIndex(key)))
}
}
f.depth--
f.fs.Write(closeMapBytes)
case reflect.Struct:
numFields := v.NumField()
f.fs.Write(openBraceBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
vt := v.Type()
for i := 0; i < numFields; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
vtf := vt.Field(i)
if f.fs.Flag('+') || f.fs.Flag('#') {
f.fs.Write([]byte(vtf.Name))
f.fs.Write(colonBytes)
}
f.format(f.unpackValue(v.Field(i)))
}
}
f.depth--
f.fs.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(f.fs, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(f.fs, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it if any get added.
default:
format := f.buildDefaultFormat()
if v.CanInterface() {
fmt.Fprintf(f.fs, format, v.Interface())
} else {
fmt.Fprintf(f.fs, format, v.String())
}
}
}
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
// details.
func (f *formatState) Format(fs fmt.State, verb rune) {
f.fs = fs
// Use standard formatting for verbs that are not v.
if verb != 'v' {
format := f.constructOrigFormat(verb)
fmt.Fprintf(fs, format, f.value)
return
}
if f.value == nil {
if fs.Flag('#') {
fs.Write(interfaceBytes)
}
fs.Write(nilAngleBytes)
return
}
f.format(reflect.ValueOf(f.value))
}
// newFormatter is a helper function to consolidate the logic from the various
// public methods which take varying config states.
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
fs := &formatState{value: v, cs: cs}
fs.pointers = make(map[uintptr]int)
return fs
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
Printf, Println, or Fprintf.
*/
func NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(&Config, v)
}

148
vendor/github.com/davecgh/go-spew/spew/spew.go generated vendored Normal file
View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"fmt"
"io"
)
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the formatted string as a value that satisfies error. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a default Formatter interface returned by NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
func Print(a ...interface{}) (n int, err error) {
return fmt.Print(convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
func Println(a ...interface{}) (n int, err error) {
return fmt.Println(convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprint(a ...interface{}) string {
return fmt.Sprint(convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintln(a ...interface{}) string {
return fmt.Sprintln(convertArgs(a)...)
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a default spew Formatter interface.
func convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = NewFormatter(arg)
}
return formatters
}

20
vendor/github.com/go-co-op/gocron/v2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,20 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
local_testing
coverage.out
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
vendor/
# IDE project files
.idea

44
vendor/github.com/go-co-op/gocron/v2/.golangci.yaml generated vendored Normal file
View File

@@ -0,0 +1,44 @@
run:
timeout: 5m
issues-exit-code: 1
tests: true
issues:
max-same-issues: 100
include:
- EXC0012
- EXC0014
exclude-dirs:
- local
exclude-rules:
- path: example_test.go
linters:
- revive
text: "seems to be unused"
fix: true
linters:
enable:
- bodyclose
- exportloopref
- gofumpt
- goimports
- gosec
- gosimple
- govet
- ineffassign
- misspell
- revive
- staticcheck
- typecheck
- unused
- whitespace
output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true
uniq-by-line: true
path-prefix: ""
sort-results: true

View File

@@ -0,0 +1,24 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/golangci/golangci-lint
rev: v1.61.0
hooks:
- id: golangci-lint
- repo: https://github.com/TekWizely/pre-commit-golang
rev: v1.0.0-rc.1
hooks:
- id: go-fumpt
args:
- -w
- id: go-mod-tidy

View File

@@ -0,0 +1,73 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone. And we mean everyone!
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and kind language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team initially on Slack to coordinate private communication. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

38
vendor/github.com/go-co-op/gocron/v2/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,38 @@
# Contributing to gocron
Thank you for coming to contribute to gocron! We welcome new ideas, PRs and general feedback.
## Reporting Bugs
If you find a bug then please let the project know by opening an issue after doing the following:
- Do a quick search of the existing issues to make sure the bug isn't already reported
- Try and make a minimal list of steps that can reliably reproduce the bug you are experiencing
- Collect as much information as you can to help identify what the issue is (project version, configuration files, etc)
## Suggesting Enhancements
If you have a use case that you don't see a way to support yet, we would welcome the feedback in an issue. Before opening the issue, please consider:
- Is this a common use case?
- Is it simple to understand?
You can help us out by doing the following before raising a new issue:
- Check that the feature hasn't been requested already by searching existing issues
- Try and reduce your enhancement into a single, concise and deliverable request, rather than a general idea
- Explain your own use cases as the basis of the request
## Adding Features
Pull requests are always welcome. However, before going through the trouble of implementing a change it's worth creating a bug or feature request issue.
This allows us to discuss the changes and make sure they are a good fit for the project.
Please always make sure a pull request has been:
- Unit tested with `make test`
- Linted with `make lint`
## Writing Tests
Tests should follow the [table driven test pattern](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go). See other tests in the code base for additional examples.

21
vendor/github.com/go-co-op/gocron/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2014, 辣椒面
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

22
vendor/github.com/go-co-op/gocron/v2/Makefile generated vendored Normal file
View File

@@ -0,0 +1,22 @@
.PHONY: fmt lint test mocks test_coverage test_ci
GO_PKGS := $(shell go list -f {{.Dir}} ./...)
fmt:
@go list -f {{.Dir}} ./... | xargs -I{} gofmt -w -s {}
lint:
@grep "^func [a-zA-Z]" example_test.go | sort -c
@golangci-lint run
test:
@go test -race -v $(GO_FLAGS) -count=1 $(GO_PKGS)
test_coverage:
@go test -race -v $(GO_FLAGS) -count=1 -coverprofile=coverage.out -covermode=atomic $(GO_PKGS)
test_ci:
@TEST_ENV=ci go test -race -v $(GO_FLAGS) -count=1 $(GO_PKGS)
mocks:
@go generate ./...

180
vendor/github.com/go-co-op/gocron/v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,180 @@
# gocron: A Golang Job Scheduling Package
[![CI State](https://github.com/go-co-op/gocron/actions/workflows/go_test.yml/badge.svg?branch=v2&event=push)](https://github.com/go-co-op/gocron/actions)
![Go Report Card](https://goreportcard.com/badge/github.com/go-co-op/gocron) [![Go Doc](https://godoc.org/github.com/go-co-op/gocron/v2?status.svg)](https://pkg.go.dev/github.com/go-co-op/gocron/v2)
gocron is a job scheduling package which lets you run Go functions at pre-determined intervals.
If you want to chat, you can find us on Slack at
[<img src="https://img.shields.io/badge/gophers-gocron-brightgreen?logo=slack">](https://gophers.slack.com/archives/CQ7T0T1FW)
## Quick Start
```
go get github.com/go-co-op/gocron/v2
```
```golang
package main
import (
"fmt"
"time"
"github.com/go-co-op/gocron/v2"
)
func main() {
// create a scheduler
s, err := gocron.NewScheduler()
if err != nil {
// handle error
}
// add a job to the scheduler
j, err := s.NewJob(
gocron.DurationJob(
10*time.Second,
),
gocron.NewTask(
func(a string, b int) {
// do things
},
"hello",
1,
),
)
if err != nil {
// handle error
}
// each job has a unique id
fmt.Println(j.ID())
// start the scheduler
s.Start()
// block until you are ready to shut down
select {
case <-time.After(time.Minute):
}
// when you're done, shut it down
err = s.Shutdown()
if err != nil {
// handle error
}
}
```
## Examples
- [Go doc examples](https://pkg.go.dev/github.com/go-co-op/gocron/v2#pkg-examples)
- [Examples directory](examples)
## Concepts
- **Job**: The job encapsulates a "task", which is made up of a go function and any function parameters. The Job then
provides the scheduler with the time the job should next be scheduled to run.
- **Scheduler**: The scheduler keeps track of all the jobs and sends each job to the executor when
it is ready to be run.
- **Executor**: The executor calls the job's task and manages the complexities of different job
execution timing requirements (e.g. singletons that shouldn't overrun each other, limiting the max number of jobs running)
## Features
### Job types
Jobs can be run at various intervals.
- [**Duration**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#DurationJob):
Jobs can be run at a fixed `time.Duration`.
- [**Random duration**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#DurationRandomJob):
Jobs can be run at a random `time.Duration` between a min and max.
- [**Cron**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#CronJob):
Jobs can be run using a crontab.
- [**Daily**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#DailyJob):
Jobs can be run every x days at specific times.
- [**Weekly**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WeeklyJob):
Jobs can be run every x weeks on specific days of the week and at specific times.
- [**Monthly**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#MonthlyJob):
Jobs can be run every x months on specific days of the month and at specific times.
- [**One time**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#OneTimeJob):
Jobs can be run at specific time(s) (either once or many times).
### Concurrency Limits
Jobs can be limited individually or across the entire scheduler.
- [**Per job limiting with singleton mode**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithSingletonMode):
Jobs can be limited to a single concurrent execution that either reschedules (skips overlapping executions)
or queues (waits for the previous execution to finish).
- [**Per scheduler limiting with limit mode**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithLimitConcurrentJobs):
Jobs can be limited to a certain number of concurrent executions across the entire scheduler
using either reschedule (skip when the limit is met) or queue (jobs are added to a queue to
wait for the limit to be available).
- **Note:** A scheduler limit and a job limit can both be enabled.
### Distributed instances of gocron
Multiple instances of gocron can be run.
- [**Elector**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithDistributedElector):
An elector can be used to elect a single instance of gocron to run as the primary with the
other instances checking to see if a new leader needs to be elected.
- Implementations: [go-co-op electors](https://github.com/go-co-op?q=-elector&type=all&language=&sort=)
(don't see what you need? request on slack to get a repo created to contribute it!)
- [**Locker**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithDistributedLocker):
A locker can be used to lock each run of a job to a single instance of gocron.
Locker can be at job or scheduler, if it is defined both at job and scheduler then locker of job will take precedence.
- Implementations: [go-co-op lockers](https://github.com/go-co-op?q=-lock&type=all&language=&sort=)
(don't see what you need? request on slack to get a repo created to contribute it!)
### Events
Job events can trigger actions.
- [**Listeners**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithEventListeners):
Can be added to a job, with [event listeners](https://pkg.go.dev/github.com/go-co-op/gocron/v2#EventListener),
or all jobs across the
[scheduler](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithGlobalJobOptions)
to listen for job events and trigger actions.
### Options
Many job and scheduler options are available.
- [**Job options**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#JobOption):
Job options can be set when creating a job using `NewJob`.
- [**Global job options**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithGlobalJobOptions):
Global job options can be set when creating a scheduler using `NewScheduler`
and the `WithGlobalJobOptions` option.
- [**Scheduler options**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#SchedulerOption):
Scheduler options can be set when creating a scheduler using `NewScheduler`.
### Logging
Logs can be enabled.
- [Logger](https://pkg.go.dev/github.com/go-co-op/gocron/v2#Logger):
The Logger interface can be implemented with your desired logging library.
The provided NewLogger uses the standard library's log package.
### Metrics
Metrics may be collected from the execution of each job.
- [**Monitor**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#Monitor):
- [**MonitorStatus**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#MonitorStatus) (includes status and error (if any) of the Job)
A monitor can be used to collect metrics for each job from a scheduler.
- Implementations: [go-co-op monitors](https://github.com/go-co-op?q=-monitor&type=all&language=&sort=)
(don't see what you need? request on slack to get a repo created to contribute it!)
### Testing
The gocron library is set up to enable testing.
- Mocks are provided in [the mock package](mocks) using [gomock](https://github.com/uber-go/mock).
- Time can be mocked by passing in a [FakeClock](https://pkg.go.dev/github.com/jonboulle/clockwork#FakeClock)
to [WithClock](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithClock) -
see the [example on WithClock](https://pkg.go.dev/github.com/go-co-op/gocron/v2#example-WithClock).
## Supporters
We appreciate the support for free and open source software!
This project is supported by:
[Jetbrains](https://www.jetbrains.com/?from=gocron)
![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png)
[Sentry](https://sentry.io/welcome/)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=go-co-op/gocron&type=Date)](https://star-history.com/#go-co-op/gocron&Date)

16
vendor/github.com/go-co-op/gocron/v2/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1,16 @@
# Security Policy
## Supported Versions
The current plan is to maintain version 2 as long as possible incorporating any necessary security patches. Version 1 is deprecated and will no longer be patched.
| Version | Supported |
| ------- | ------------------ |
| 1.x.x | :heavy_multiplication_x: |
| 2.x.x | :white_check_mark: |
## Reporting a Vulnerability
Vulnerabilities can be reported by [opening an issue](https://github.com/go-co-op/gocron/issues/new/choose) or reaching out on Slack: [<img src="https://img.shields.io/badge/gophers-gocron-brightgreen?logo=slack">](https://gophers.slack.com/archives/CQ7T0T1FW)
We will do our best to address any vulnerabilities in an expeditious manner.

30
vendor/github.com/go-co-op/gocron/v2/distributed.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
//go:generate mockgen -destination=mocks/distributed.go -package=gocronmocks . Elector,Locker,Lock
package gocron
import (
"context"
)
// Elector determines the leader from instances asking to be the leader. Only
// the leader runs jobs. If the leader goes down, a new leader will be elected.
type Elector interface {
// IsLeader should return nil if the job should be scheduled by the instance
// making the request and an error if the job should not be scheduled.
IsLeader(context.Context) error
}
// Locker represents the required interface to lock jobs when running multiple schedulers.
// The lock is held for the duration of the job's run, and it is expected that the
// locker implementation handles time splay between schedulers.
// The lock key passed is the job's name - which, if not set, defaults to the
// go function's name, e.g. "pkg.myJob" for func myJob() {} in pkg
type Locker interface {
// Lock if an error is returned by lock, the job will not be scheduled.
Lock(ctx context.Context, key string) (Lock, error)
}
// Lock represents an obtained lock. The lock is released after the execution of the job
// by the scheduler.
type Lock interface {
Unlock(ctx context.Context) error
}

65
vendor/github.com/go-co-op/gocron/v2/errors.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
package gocron
import "fmt"
// Public error definitions
var (
ErrCronJobInvalid = fmt.Errorf("gocron: CronJob: invalid crontab")
ErrCronJobParse = fmt.Errorf("gocron: CronJob: crontab parse failure")
ErrDailyJobAtTimeNil = fmt.Errorf("gocron: DailyJob: atTime within atTimes must not be nil")
ErrDailyJobAtTimesNil = fmt.Errorf("gocron: DailyJob: atTimes must not be nil")
ErrDailyJobHours = fmt.Errorf("gocron: DailyJob: atTimes hours must be between 0 and 23 inclusive")
ErrDailyJobZeroInterval = fmt.Errorf("gocron: DailyJob: interval must be greater than 0")
ErrDailyJobMinutesSeconds = fmt.Errorf("gocron: DailyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
ErrDurationJobIntervalZero = fmt.Errorf("gocron: DurationJob: time interval is 0")
ErrDurationRandomJobMinMax = fmt.Errorf("gocron: DurationRandomJob: minimum duration must be less than maximum duration")
ErrEventListenerFuncNil = fmt.Errorf("gocron: eventListenerFunc must not be nil")
ErrJobNotFound = fmt.Errorf("gocron: job not found")
ErrJobRunNowFailed = fmt.Errorf("gocron: Job: RunNow: scheduler unreachable")
ErrMonthlyJobDays = fmt.Errorf("gocron: MonthlyJob: daysOfTheMonth must be between 31 and -31 inclusive, and not 0")
ErrMonthlyJobAtTimeNil = fmt.Errorf("gocron: MonthlyJob: atTime within atTimes must not be nil")
ErrMonthlyJobAtTimesNil = fmt.Errorf("gocron: MonthlyJob: atTimes must not be nil")
ErrMonthlyJobDaysNil = fmt.Errorf("gocron: MonthlyJob: daysOfTheMonth must not be nil")
ErrMonthlyJobHours = fmt.Errorf("gocron: MonthlyJob: atTimes hours must be between 0 and 23 inclusive")
ErrMonthlyJobZeroInterval = fmt.Errorf("gocron: MonthlyJob: interval must be greater than 0")
ErrMonthlyJobMinutesSeconds = fmt.Errorf("gocron: MonthlyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
ErrNewJobTaskNil = fmt.Errorf("gocron: NewJob: Task must not be nil")
ErrNewJobTaskNotFunc = fmt.Errorf("gocron: NewJob: Task.Function must be of kind reflect.Func")
ErrNewJobWrongNumberOfParameters = fmt.Errorf("gocron: NewJob: Number of provided parameters does not match expected")
ErrNewJobWrongTypeOfParameters = fmt.Errorf("gocron: NewJob: Type of provided parameters does not match expected")
ErrOneTimeJobStartDateTimePast = fmt.Errorf("gocron: OneTimeJob: start must not be in the past")
ErrStopExecutorTimedOut = fmt.Errorf("gocron: timed out waiting for executor to stop")
ErrStopJobsTimedOut = fmt.Errorf("gocron: timed out waiting for jobs to finish")
ErrStopSchedulerTimedOut = fmt.Errorf("gocron: timed out waiting for scheduler to stop")
ErrWeeklyJobAtTimeNil = fmt.Errorf("gocron: WeeklyJob: atTime within atTimes must not be nil")
ErrWeeklyJobAtTimesNil = fmt.Errorf("gocron: WeeklyJob: atTimes must not be nil")
ErrWeeklyJobDaysOfTheWeekNil = fmt.Errorf("gocron: WeeklyJob: daysOfTheWeek must not be nil")
ErrWeeklyJobHours = fmt.Errorf("gocron: WeeklyJob: atTimes hours must be between 0 and 23 inclusive")
ErrWeeklyJobZeroInterval = fmt.Errorf("gocron: WeeklyJob: interval must be greater than 0")
ErrWeeklyJobMinutesSeconds = fmt.Errorf("gocron: WeeklyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
ErrPanicRecovered = fmt.Errorf("gocron: panic recovered")
ErrWithClockNil = fmt.Errorf("gocron: WithClock: clock must not be nil")
ErrWithContextNil = fmt.Errorf("gocron: WithContext: context must not be nil")
ErrWithDistributedElectorNil = fmt.Errorf("gocron: WithDistributedElector: elector must not be nil")
ErrWithDistributedLockerNil = fmt.Errorf("gocron: WithDistributedLocker: locker must not be nil")
ErrWithDistributedJobLockerNil = fmt.Errorf("gocron: WithDistributedJobLocker: locker must not be nil")
ErrWithIdentifierNil = fmt.Errorf("gocron: WithIdentifier: identifier must not be nil")
ErrWithLimitConcurrentJobsZero = fmt.Errorf("gocron: WithLimitConcurrentJobs: limit must be greater than 0")
ErrWithLocationNil = fmt.Errorf("gocron: WithLocation: location must not be nil")
ErrWithLoggerNil = fmt.Errorf("gocron: WithLogger: logger must not be nil")
ErrWithMonitorNil = fmt.Errorf("gocron: WithMonitor: monitor must not be nil")
ErrWithNameEmpty = fmt.Errorf("gocron: WithName: name must not be empty")
ErrWithStartDateTimePast = fmt.Errorf("gocron: WithStartDateTime: start must not be in the past")
ErrWithStopDateTimePast = fmt.Errorf("gocron: WithStopDateTime: end must not be in the past")
ErrStartTimeLaterThanEndTime = fmt.Errorf("gocron: WithStartDateTime: start must not be later than end")
ErrStopTimeEarlierThanStartTime = fmt.Errorf("gocron: WithStopDateTime: end must not be earlier than start")
ErrWithStopTimeoutZeroOrNegative = fmt.Errorf("gocron: WithStopTimeout: timeout must be greater than 0")
)
// internal errors
var (
errAtTimeNil = fmt.Errorf("errAtTimeNil")
errAtTimesNil = fmt.Errorf("errAtTimesNil")
errAtTimeHours = fmt.Errorf("errAtTimeHours")
errAtTimeMinSec = fmt.Errorf("errAtTimeMinSec")
)

545
vendor/github.com/go-co-op/gocron/v2/executor.go generated vendored Normal file
View File

@@ -0,0 +1,545 @@
package gocron
import (
"context"
"fmt"
"strconv"
"sync"
"time"
"github.com/jonboulle/clockwork"
"github.com/google/uuid"
)
type executor struct {
// context used for shutting down
ctx context.Context
// cancel used by the executor to signal a stop of it's functions
cancel context.CancelFunc
// clock used for regular time or mocking time
clock clockwork.Clock
// the executor's logger
logger Logger
// receives jobs scheduled to execute
jobsIn chan jobIn
// sends out jobs for rescheduling
jobsOutForRescheduling chan uuid.UUID
// sends out jobs once completed
jobsOutCompleted chan uuid.UUID
// used to request jobs from the scheduler
jobOutRequest chan jobOutRequest
// used by the executor to receive a stop signal from the scheduler
stopCh chan struct{}
// the timeout value when stopping
stopTimeout time.Duration
// used to signal that the executor has completed shutdown
done chan error
// runners for any singleton type jobs
// map[uuid.UUID]singletonRunner
singletonRunners *sync.Map
// config for limit mode
limitMode *limitModeConfig
// the elector when running distributed instances
elector Elector
// the locker when running distributed instances
locker Locker
// monitor for reporting metrics
monitor Monitor
// monitorStatus for reporting metrics
monitorStatus MonitorStatus
}
type jobIn struct {
id uuid.UUID
shouldSendOut bool
}
type singletonRunner struct {
in chan jobIn
rescheduleLimiter chan struct{}
}
type limitModeConfig struct {
started bool
mode LimitMode
limit uint
rescheduleLimiter chan struct{}
in chan jobIn
// singletonJobs is used to track singleton jobs that are running
// in the limit mode runner. This is used to prevent the same job
// from running multiple times across limit mode runners when both
// a limit mode and singleton mode are enabled.
singletonJobs map[uuid.UUID]struct{}
singletonJobsMu sync.Mutex
}
func (e *executor) start() {
e.logger.Debug("gocron: executor started")
// creating the executor's context here as the executor
// is the only goroutine that should access this context
// any other uses within the executor should create a context
// using the executor context as parent.
e.ctx, e.cancel = context.WithCancel(context.Background())
// the standardJobsWg tracks
standardJobsWg := &waitGroupWithMutex{}
singletonJobsWg := &waitGroupWithMutex{}
limitModeJobsWg := &waitGroupWithMutex{}
// create a fresh map for tracking singleton runners
e.singletonRunners = &sync.Map{}
// start the for leap that is the executor
// selecting on channels for work to do
for {
select {
// job ids in are sent from 1 of 2 places:
// 1. the scheduler sends directly when jobs
// are run immediately.
// 2. sent from time.AfterFuncs in which job schedules
// are spun up by the scheduler
case jIn := <-e.jobsIn:
select {
case <-e.stopCh:
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
return
default:
}
// this context is used to handle cancellation of the executor
// on requests for a job to the scheduler via requestJobCtx
ctx, cancel := context.WithCancel(e.ctx)
if e.limitMode != nil && !e.limitMode.started {
// check if we are already running the limit mode runners
// if not, spin up the required number i.e. limit!
e.limitMode.started = true
for i := e.limitMode.limit; i > 0; i-- {
limitModeJobsWg.Add(1)
go e.limitModeRunner("limitMode-"+strconv.Itoa(int(i)), e.limitMode.in, limitModeJobsWg, e.limitMode.mode, e.limitMode.rescheduleLimiter)
}
}
// spin off into a goroutine to unblock the executor and
// allow for processing for more work
go func() {
// make sure to cancel the above context per the docs
// // Canceling this context releases resources associated with it, so code should
// // call cancel as soon as the operations running in this Context complete.
defer cancel()
// check for limit mode - this spins up a separate runner which handles
// limiting the total number of concurrently running jobs
if e.limitMode != nil {
if e.limitMode.mode == LimitModeReschedule {
select {
// rescheduleLimiter is a channel the size of the limit
// this blocks publishing to the channel and keeps
// the executor from building up a waiting queue
// and forces rescheduling
case e.limitMode.rescheduleLimiter <- struct{}{}:
e.limitMode.in <- jIn
default:
// all runners are busy, reschedule the work for later
// which means we just skip it here and do nothing
// TODO when metrics are added, this should increment a rescheduled metric
e.sendOutForRescheduling(&jIn)
}
} else {
// since we're not using LimitModeReschedule, but instead using LimitModeWait
// we do want to queue up the work to the limit mode runners and allow them
// to work through the channel backlog. A hard limit of 1000 is in place
// at which point this call would block.
// TODO when metrics are added, this should increment a wait metric
e.sendOutForRescheduling(&jIn)
e.limitMode.in <- jIn
}
} else {
// no limit mode, so we're either running a regular job or
// a job with a singleton mode
//
// get the job, so we can figure out what kind it is and how
// to execute it
j := requestJobCtx(ctx, jIn.id, e.jobOutRequest)
if j == nil {
// safety check as it'd be strange bug if this occurred
return
}
if j.singletonMode {
// for singleton mode, get the existing runner for the job
// or spin up a new one
runner := &singletonRunner{}
runnerSrc, ok := e.singletonRunners.Load(jIn.id)
if !ok {
runner.in = make(chan jobIn, 1000)
if j.singletonLimitMode == LimitModeReschedule {
runner.rescheduleLimiter = make(chan struct{}, 1)
}
e.singletonRunners.Store(jIn.id, runner)
singletonJobsWg.Add(1)
go e.singletonModeRunner("singleton-"+jIn.id.String(), runner.in, singletonJobsWg, j.singletonLimitMode, runner.rescheduleLimiter)
} else {
runner = runnerSrc.(*singletonRunner)
}
if j.singletonLimitMode == LimitModeReschedule {
// reschedule mode uses the limiter channel to check
// for a running job and reschedules if the channel is full.
select {
case runner.rescheduleLimiter <- struct{}{}:
runner.in <- jIn
e.sendOutForRescheduling(&jIn)
default:
// runner is busy, reschedule the work for later
// which means we just skip it here and do nothing
e.incrementJobCounter(*j, SingletonRescheduled)
e.sendOutForRescheduling(&jIn)
}
} else {
// wait mode, fill up that queue (buffered channel, so it's ok)
runner.in <- jIn
e.sendOutForRescheduling(&jIn)
}
} else {
select {
case <-e.stopCh:
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
return
default:
}
// we've gotten to the basic / standard jobs --
// the ones without anything special that just want
// to be run. Add to the WaitGroup so that
// stopping or shutting down can wait for the jobs to
// complete.
standardJobsWg.Add(1)
go func(j internalJob) {
e.runJob(j, jIn)
standardJobsWg.Done()
}(*j)
}
}
}()
case <-e.stopCh:
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
return
}
}
}
func (e *executor) sendOutForRescheduling(jIn *jobIn) {
if jIn.shouldSendOut {
select {
case e.jobsOutForRescheduling <- jIn.id:
case <-e.ctx.Done():
return
}
}
// we need to set this to false now, because to handle
// non-limit jobs, we send out from the e.runJob function
// and in this case we don't want to send out twice.
jIn.shouldSendOut = false
}
func (e *executor) limitModeRunner(name string, in chan jobIn, wg *waitGroupWithMutex, limitMode LimitMode, rescheduleLimiter chan struct{}) {
e.logger.Debug("gocron: limitModeRunner starting", "name", name)
for {
select {
case jIn := <-in:
select {
case <-e.ctx.Done():
e.logger.Debug("gocron: limitModeRunner shutting down", "name", name)
wg.Done()
return
default:
}
ctx, cancel := context.WithCancel(e.ctx)
j := requestJobCtx(ctx, jIn.id, e.jobOutRequest)
cancel()
if j != nil {
if j.singletonMode {
e.limitMode.singletonJobsMu.Lock()
_, ok := e.limitMode.singletonJobs[jIn.id]
if ok {
// this job is already running, so don't run it
// but instead reschedule it
e.limitMode.singletonJobsMu.Unlock()
if jIn.shouldSendOut {
select {
case <-e.ctx.Done():
return
case <-j.ctx.Done():
return
case e.jobsOutForRescheduling <- j.id:
}
}
// remove the limiter block, as this particular job
// was a singleton already running, and we want to
// allow another job to be scheduled
if limitMode == LimitModeReschedule {
<-rescheduleLimiter
}
continue
}
e.limitMode.singletonJobs[jIn.id] = struct{}{}
e.limitMode.singletonJobsMu.Unlock()
}
e.runJob(*j, jIn)
if j.singletonMode {
e.limitMode.singletonJobsMu.Lock()
delete(e.limitMode.singletonJobs, jIn.id)
e.limitMode.singletonJobsMu.Unlock()
}
}
// remove the limiter block to allow another job to be scheduled
if limitMode == LimitModeReschedule {
<-rescheduleLimiter
}
case <-e.ctx.Done():
e.logger.Debug("limitModeRunner shutting down", "name", name)
wg.Done()
return
}
}
}
func (e *executor) singletonModeRunner(name string, in chan jobIn, wg *waitGroupWithMutex, limitMode LimitMode, rescheduleLimiter chan struct{}) {
e.logger.Debug("gocron: singletonModeRunner starting", "name", name)
for {
select {
case jIn := <-in:
select {
case <-e.ctx.Done():
e.logger.Debug("gocron: singletonModeRunner shutting down", "name", name)
wg.Done()
return
default:
}
ctx, cancel := context.WithCancel(e.ctx)
j := requestJobCtx(ctx, jIn.id, e.jobOutRequest)
cancel()
if j != nil {
// need to set shouldSendOut = false here, as there is a duplicative call to sendOutForRescheduling
// inside the runJob function that needs to be skipped. sendOutForRescheduling is previously called
// when the job is sent to the singleton mode runner.
jIn.shouldSendOut = false
e.runJob(*j, jIn)
}
// remove the limiter block to allow another job to be scheduled
if limitMode == LimitModeReschedule {
<-rescheduleLimiter
}
case <-e.ctx.Done():
e.logger.Debug("singletonModeRunner shutting down", "name", name)
wg.Done()
return
}
}
}
func (e *executor) runJob(j internalJob, jIn jobIn) {
if j.ctx == nil {
return
}
select {
case <-e.ctx.Done():
return
case <-j.ctx.Done():
return
default:
}
if j.stopTimeReached(e.clock.Now()) {
return
}
if e.elector != nil {
if err := e.elector.IsLeader(j.ctx); err != nil {
e.sendOutForRescheduling(&jIn)
e.incrementJobCounter(j, Skip)
return
}
} else if !j.disabledLocker && j.locker != nil {
lock, err := j.locker.Lock(j.ctx, j.name)
if err != nil {
_ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err)
e.sendOutForRescheduling(&jIn)
e.incrementJobCounter(j, Skip)
return
}
defer func() { _ = lock.Unlock(j.ctx) }()
} else if !j.disabledLocker && e.locker != nil {
lock, err := e.locker.Lock(j.ctx, j.name)
if err != nil {
_ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err)
e.sendOutForRescheduling(&jIn)
e.incrementJobCounter(j, Skip)
return
}
defer func() { _ = lock.Unlock(j.ctx) }()
}
_ = callJobFuncWithParams(j.beforeJobRuns, j.id, j.name)
err := callJobFuncWithParams(j.beforeJobRunsSkipIfBeforeFuncErrors, j.id, j.name)
if err != nil {
e.sendOutForRescheduling(&jIn)
select {
case e.jobsOutCompleted <- j.id:
case <-e.ctx.Done():
}
return
}
e.sendOutForRescheduling(&jIn)
select {
case e.jobsOutCompleted <- j.id:
case <-e.ctx.Done():
}
startTime := time.Now()
if j.afterJobRunsWithPanic != nil {
err = e.callJobWithRecover(j)
} else {
err = callJobFuncWithParams(j.function, j.parameters...)
}
e.recordJobTiming(startTime, time.Now(), j)
if err != nil {
_ = callJobFuncWithParams(j.afterJobRunsWithError, j.id, j.name, err)
e.incrementJobCounter(j, Fail)
e.recordJobTimingWithStatus(startTime, time.Now(), j, Fail, err)
} else {
_ = callJobFuncWithParams(j.afterJobRuns, j.id, j.name)
e.incrementJobCounter(j, Success)
e.recordJobTimingWithStatus(startTime, time.Now(), j, Success, nil)
}
}
func (e *executor) callJobWithRecover(j internalJob) (err error) {
defer func() {
if recoverData := recover(); recoverData != nil {
_ = callJobFuncWithParams(j.afterJobRunsWithPanic, j.id, j.name, recoverData)
// if panic is occurred, we should return an error
err = fmt.Errorf("%w from %v", ErrPanicRecovered, recoverData)
}
}()
return callJobFuncWithParams(j.function, j.parameters...)
}
func (e *executor) recordJobTiming(start time.Time, end time.Time, j internalJob) {
if e.monitor != nil {
e.monitor.RecordJobTiming(start, end, j.id, j.name, j.tags)
}
}
func (e *executor) recordJobTimingWithStatus(start time.Time, end time.Time, j internalJob, status JobStatus, err error) {
if e.monitorStatus != nil {
e.monitorStatus.RecordJobTimingWithStatus(start, end, j.id, j.name, j.tags, status, err)
}
}
func (e *executor) incrementJobCounter(j internalJob, status JobStatus) {
if e.monitor != nil {
e.monitor.IncrementJob(j.id, j.name, j.tags, status)
}
}
func (e *executor) stop(standardJobsWg, singletonJobsWg, limitModeJobsWg *waitGroupWithMutex) {
e.logger.Debug("gocron: stopping executor")
// we've been asked to stop. This is either because the scheduler has been told
// to stop all jobs or the scheduler has been asked to completely shutdown.
//
// cancel tells all the functions to stop their work and send in a done response
e.cancel()
// the wait for job channels are used to report back whether we successfully waited
// for all jobs to complete or if we hit the configured timeout.
waitForJobs := make(chan struct{}, 1)
waitForSingletons := make(chan struct{}, 1)
waitForLimitMode := make(chan struct{}, 1)
// the waiter context is used to cancel the functions waiting on jobs.
// this is done to avoid goroutine leaks.
waiterCtx, waiterCancel := context.WithCancel(context.Background())
// wait for standard jobs to complete
go func() {
e.logger.Debug("gocron: waiting for standard jobs to complete")
go func() {
// this is done in a separate goroutine, so we aren't
// blocked by the WaitGroup's Wait call in the event
// that the waiter context is cancelled.
// This particular goroutine could leak in the event that
// some long-running standard job doesn't complete.
standardJobsWg.Wait()
e.logger.Debug("gocron: standard jobs completed")
waitForJobs <- struct{}{}
}()
<-waiterCtx.Done()
}()
// wait for per job singleton limit mode runner jobs to complete
go func() {
e.logger.Debug("gocron: waiting for singleton jobs to complete")
go func() {
singletonJobsWg.Wait()
e.logger.Debug("gocron: singleton jobs completed")
waitForSingletons <- struct{}{}
}()
<-waiterCtx.Done()
}()
// wait for limit mode runners to complete
go func() {
e.logger.Debug("gocron: waiting for limit mode jobs to complete")
go func() {
limitModeJobsWg.Wait()
e.logger.Debug("gocron: limitMode jobs completed")
waitForLimitMode <- struct{}{}
}()
<-waiterCtx.Done()
}()
// now either wait for all the jobs to complete,
// or hit the timeout.
var count int
timeout := time.Now().Add(e.stopTimeout)
for time.Now().Before(timeout) && count < 3 {
select {
case <-waitForJobs:
count++
case <-waitForSingletons:
count++
case <-waitForLimitMode:
count++
default:
}
}
if count < 3 {
e.done <- ErrStopJobsTimedOut
e.logger.Debug("gocron: executor stopped - timed out")
} else {
e.done <- nil
e.logger.Debug("gocron: executor stopped")
}
waiterCancel()
if e.limitMode != nil {
e.limitMode.started = false
}
}

1171
vendor/github.com/go-co-op/gocron/v2/job.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

101
vendor/github.com/go-co-op/gocron/v2/logger.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
//go:generate mockgen -destination=mocks/logger.go -package=gocronmocks . Logger
package gocron
import (
"fmt"
"log"
"os"
"strings"
)
// Logger is the interface that wraps the basic logging methods
// used by gocron. The methods are modeled after the standard
// library slog package. The default logger is a no-op logger.
// To enable logging, use one of the provided New*Logger functions
// or implement your own Logger. The actual level of Log that is logged
// is handled by the implementation.
type Logger interface {
Debug(msg string, args ...any)
Error(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
}
var _ Logger = (*noOpLogger)(nil)
type noOpLogger struct{}
func (l noOpLogger) Debug(_ string, _ ...any) {}
func (l noOpLogger) Error(_ string, _ ...any) {}
func (l noOpLogger) Info(_ string, _ ...any) {}
func (l noOpLogger) Warn(_ string, _ ...any) {}
var _ Logger = (*logger)(nil)
// LogLevel is the level of logging that should be logged
// when using the basic NewLogger.
type LogLevel int
// The different log levels that can be used.
const (
LogLevelError LogLevel = iota
LogLevelWarn
LogLevelInfo
LogLevelDebug
)
type logger struct {
log *log.Logger
level LogLevel
}
// NewLogger returns a new Logger that logs at the given level.
func NewLogger(level LogLevel) Logger {
l := log.New(os.Stdout, "", log.LstdFlags)
return &logger{
log: l,
level: level,
}
}
func (l *logger) Debug(msg string, args ...any) {
if l.level < LogLevelDebug {
return
}
l.log.Printf("DEBUG: %s%s\n", msg, logFormatArgs(args...))
}
func (l *logger) Error(msg string, args ...any) {
if l.level < LogLevelError {
return
}
l.log.Printf("ERROR: %s%s\n", msg, logFormatArgs(args...))
}
func (l *logger) Info(msg string, args ...any) {
if l.level < LogLevelInfo {
return
}
l.log.Printf("INFO: %s%s\n", msg, logFormatArgs(args...))
}
func (l *logger) Warn(msg string, args ...any) {
if l.level < LogLevelWarn {
return
}
l.log.Printf("WARN: %s%s\n", msg, logFormatArgs(args...))
}
func logFormatArgs(args ...any) string {
if len(args) == 0 {
return ""
}
if len(args)%2 != 0 {
return ", " + fmt.Sprint(args...)
}
var pairs []string
for i := 0; i < len(args); i += 2 {
pairs = append(pairs, fmt.Sprintf("%s=%v", args[i], args[i+1]))
}
return ", " + strings.Join(pairs, ", ")
}

36
vendor/github.com/go-co-op/gocron/v2/monitor.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package gocron
import (
"time"
"github.com/google/uuid"
)
// JobStatus is the status of job run that should be collected with the metric.
type JobStatus string
// The different statuses of job that can be used.
const (
Fail JobStatus = "fail"
Success JobStatus = "success"
Skip JobStatus = "skip"
SingletonRescheduled JobStatus = "singleton_rescheduled"
)
// Monitor represents the interface to collect jobs metrics.
type Monitor interface {
// IncrementJob will provide details about the job and expects the underlying implementation
// to handle instantiating and incrementing a value
IncrementJob(id uuid.UUID, name string, tags []string, status JobStatus)
// RecordJobTiming will provide details about the job and the timing and expects the underlying implementation
// to handle instantiating and recording the value
RecordJobTiming(startTime, endTime time.Time, id uuid.UUID, name string, tags []string)
}
// MonitorStatus extends RecordJobTiming with the job status.
type MonitorStatus interface {
Monitor
// RecordJobTimingWithStatus will provide details about the job, its status, error and the timing and expects the underlying implementation
// to handle instantiating and recording the value
RecordJobTimingWithStatus(startTime, endTime time.Time, id uuid.UUID, name string, tags []string, status JobStatus, err error)
}

989
vendor/github.com/go-co-op/gocron/v2/scheduler.go generated vendored Normal file
View File

@@ -0,0 +1,989 @@
//go:generate mockgen -destination=mocks/scheduler.go -package=gocronmocks . Scheduler
package gocron
import (
"context"
"reflect"
"runtime"
"time"
"github.com/google/uuid"
"github.com/jonboulle/clockwork"
"golang.org/x/exp/slices"
)
var _ Scheduler = (*scheduler)(nil)
// Scheduler defines the interface for the Scheduler.
type Scheduler interface {
// Jobs returns all the jobs currently in the scheduler.
Jobs() []Job
// NewJob creates a new job in the Scheduler. The job is scheduled per the provided
// definition when the Scheduler is started. If the Scheduler is already running
// the job will be scheduled when the Scheduler is started.
// If you set the first argument of your Task func to be a context.Context,
// gocron will pass in a context (either the default Job context, or one
// provided via WithContext) to the job and will cancel the context on shutdown.
// This allows you to listen for and handle cancellation within your job.
NewJob(JobDefinition, Task, ...JobOption) (Job, error)
// RemoveByTags removes all jobs that have at least one of the provided tags.
RemoveByTags(...string)
// RemoveJob removes the job with the provided id.
RemoveJob(uuid.UUID) error
// Shutdown should be called when you no longer need
// the Scheduler or Job's as the Scheduler cannot
// be restarted after calling Shutdown. This is similar
// to a Close or Cleanup method and is often deferred after
// starting the scheduler.
Shutdown() error
// Start begins scheduling jobs for execution based
// on each job's definition. Job's added to an already
// running scheduler will be scheduled immediately based
// on definition. Start is non-blocking.
Start()
// StopJobs stops the execution of all jobs in the scheduler.
// This can be useful in situations where jobs need to be
// paused globally and then restarted with Start().
StopJobs() error
// Update replaces the existing Job's JobDefinition with the provided
// JobDefinition. The Job's Job.ID() remains the same.
Update(uuid.UUID, JobDefinition, Task, ...JobOption) (Job, error)
// JobsWaitingInQueue number of jobs waiting in Queue in case of LimitModeWait
// In case of LimitModeReschedule or no limit it will be always zero
JobsWaitingInQueue() int
}
// -----------------------------------------------
// -----------------------------------------------
// ----------------- Scheduler -------------------
// -----------------------------------------------
// -----------------------------------------------
type scheduler struct {
// context used for shutting down
shutdownCtx context.Context
// cancel used to signal scheduler should shut down
shutdownCancel context.CancelFunc
// the executor, which actually runs the jobs sent to it via the scheduler
exec executor
// the map of jobs registered in the scheduler
jobs map[uuid.UUID]internalJob
// the location used by the scheduler for scheduling when relevant
location *time.Location
// whether the scheduler has been started or not
started bool
// globally applied JobOption's set on all jobs added to the scheduler
// note: individually set JobOption's take precedence.
globalJobOptions []JobOption
// the scheduler's logger
logger Logger
// used to tell the scheduler to start
startCh chan struct{}
// used to report that the scheduler has started
startedCh chan struct{}
// used to tell the scheduler to stop
stopCh chan struct{}
// used to report that the scheduler has stopped
stopErrCh chan error
// used to send all the jobs out when a request is made by the client
allJobsOutRequest chan allJobsOutRequest
// used to send a jobs out when a request is made by the client
jobOutRequestCh chan jobOutRequest
// used to run a job on-demand when requested by the client
runJobRequestCh chan runJobRequest
// new jobs are received here
newJobCh chan newJobIn
// requests from the client to remove jobs by ID are received here
removeJobCh chan uuid.UUID
// requests from the client to remove jobs by tags are received here
removeJobsByTagsCh chan []string
}
type newJobIn struct {
ctx context.Context
cancel context.CancelFunc
job internalJob
}
type jobOutRequest struct {
id uuid.UUID
outChan chan internalJob
}
type runJobRequest struct {
id uuid.UUID
outChan chan error
}
type allJobsOutRequest struct {
outChan chan []Job
}
// NewScheduler creates a new Scheduler instance.
// The Scheduler is not started until Start() is called.
//
// NewJob will add jobs to the Scheduler, but they will not
// be scheduled until Start() is called.
func NewScheduler(options ...SchedulerOption) (Scheduler, error) {
schCtx, cancel := context.WithCancel(context.Background())
exec := executor{
stopCh: make(chan struct{}),
stopTimeout: time.Second * 10,
singletonRunners: nil,
logger: &noOpLogger{},
clock: clockwork.NewRealClock(),
jobsIn: make(chan jobIn),
jobsOutForRescheduling: make(chan uuid.UUID),
jobsOutCompleted: make(chan uuid.UUID),
jobOutRequest: make(chan jobOutRequest, 1000),
done: make(chan error),
}
s := &scheduler{
shutdownCtx: schCtx,
shutdownCancel: cancel,
exec: exec,
jobs: make(map[uuid.UUID]internalJob),
location: time.Local,
logger: &noOpLogger{},
newJobCh: make(chan newJobIn),
removeJobCh: make(chan uuid.UUID),
removeJobsByTagsCh: make(chan []string),
startCh: make(chan struct{}),
startedCh: make(chan struct{}),
stopCh: make(chan struct{}),
stopErrCh: make(chan error, 1),
jobOutRequestCh: make(chan jobOutRequest),
runJobRequestCh: make(chan runJobRequest),
allJobsOutRequest: make(chan allJobsOutRequest),
}
for _, option := range options {
err := option(s)
if err != nil {
return nil, err
}
}
go func() {
s.logger.Info("gocron: new scheduler created")
for {
select {
case id := <-s.exec.jobsOutForRescheduling:
s.selectExecJobsOutForRescheduling(id)
case id := <-s.exec.jobsOutCompleted:
s.selectExecJobsOutCompleted(id)
case in := <-s.newJobCh:
s.selectNewJob(in)
case id := <-s.removeJobCh:
s.selectRemoveJob(id)
case tags := <-s.removeJobsByTagsCh:
s.selectRemoveJobsByTags(tags)
case out := <-s.exec.jobOutRequest:
s.selectJobOutRequest(out)
case out := <-s.jobOutRequestCh:
s.selectJobOutRequest(out)
case out := <-s.allJobsOutRequest:
s.selectAllJobsOutRequest(out)
case run := <-s.runJobRequestCh:
s.selectRunJobRequest(run)
case <-s.startCh:
s.selectStart()
case <-s.stopCh:
s.stopScheduler()
case <-s.shutdownCtx.Done():
s.stopScheduler()
return
}
}
}()
return s, nil
}
// -----------------------------------------------
// -----------------------------------------------
// --------- Scheduler Channel Methods -----------
// -----------------------------------------------
// -----------------------------------------------
// The scheduler's channel functions are broken out here
// to allow prioritizing within the select blocks. The idea
// being that we want to make sure that scheduling tasks
// are not blocked by requests from the caller for information
// about jobs.
func (s *scheduler) stopScheduler() {
s.logger.Debug("gocron: stopping scheduler")
if s.started {
s.exec.stopCh <- struct{}{}
}
for _, j := range s.jobs {
j.stop()
}
for id, j := range s.jobs {
<-j.ctx.Done()
j.ctx, j.cancel = context.WithCancel(s.shutdownCtx)
s.jobs[id] = j
}
var err error
if s.started {
t := time.NewTimer(s.exec.stopTimeout + 1*time.Second)
select {
case err = <-s.exec.done:
t.Stop()
case <-t.C:
err = ErrStopExecutorTimedOut
}
}
s.stopErrCh <- err
s.started = false
s.logger.Debug("gocron: scheduler stopped")
}
func (s *scheduler) selectAllJobsOutRequest(out allJobsOutRequest) {
outJobs := make([]Job, len(s.jobs))
var counter int
for _, j := range s.jobs {
outJobs[counter] = s.jobFromInternalJob(j)
counter++
}
slices.SortFunc(outJobs, func(a, b Job) int {
aID, bID := a.ID().String(), b.ID().String()
switch {
case aID < bID:
return -1
case aID > bID:
return 1
default:
return 0
}
})
select {
case <-s.shutdownCtx.Done():
case out.outChan <- outJobs:
}
}
func (s *scheduler) selectRunJobRequest(run runJobRequest) {
j, ok := s.jobs[run.id]
if !ok {
select {
case run.outChan <- ErrJobNotFound:
default:
}
}
select {
case <-s.shutdownCtx.Done():
select {
case run.outChan <- ErrJobRunNowFailed:
default:
}
case s.exec.jobsIn <- jobIn{
id: j.id,
shouldSendOut: false,
}:
select {
case run.outChan <- nil:
default:
}
}
}
func (s *scheduler) selectRemoveJob(id uuid.UUID) {
j, ok := s.jobs[id]
if !ok {
return
}
j.stop()
delete(s.jobs, id)
}
// Jobs coming back from the executor to the scheduler that
// need to be evaluated for rescheduling.
func (s *scheduler) selectExecJobsOutForRescheduling(id uuid.UUID) {
select {
case <-s.shutdownCtx.Done():
return
default:
}
j, ok := s.jobs[id]
if !ok {
// the job was removed while it was running, and
// so we don't need to reschedule it.
return
}
if j.stopTimeReached(s.now()) {
return
}
scheduleFrom := j.lastRun
if len(j.nextScheduled) > 0 {
// always grab the last element in the slice as that is the furthest
// out in the future and the time from which we want to calculate
// the subsequent next run time.
slices.SortStableFunc(j.nextScheduled, ascendingTime)
scheduleFrom = j.nextScheduled[len(j.nextScheduled)-1]
}
if scheduleFrom.IsZero() {
scheduleFrom = j.startTime
}
next := j.next(scheduleFrom)
if next.IsZero() {
// the job's next function will return zero for OneTime jobs.
// since they are one time only, they do not need rescheduling.
return
}
if next.Before(s.now()) {
// in some cases the next run time can be in the past, for example:
// - the time on the machine was incorrect and has been synced with ntp
// - the machine went to sleep, and woke up some time later
// in those cases, we want to increment to the next run in the future
// and schedule the job for that time.
for next.Before(s.now()) {
next = j.next(next)
}
}
// Clean up any existing timer to prevent leaks
if j.timer != nil {
j.timer.Stop()
j.timer = nil // Ensure timer is cleared for GC
}
j.nextScheduled = append(j.nextScheduled, next)
j.timer = s.exec.clock.AfterFunc(next.Sub(s.now()), func() {
// set the actual timer on the job here and listen for
// shut down events so that the job doesn't attempt to
// run if the scheduler has been shutdown.
select {
case <-s.shutdownCtx.Done():
return
case s.exec.jobsIn <- jobIn{
id: j.id,
shouldSendOut: true,
}:
}
})
// update the job with its new next and last run times and timer.
s.jobs[id] = j
}
func (s *scheduler) selectExecJobsOutCompleted(id uuid.UUID) {
j, ok := s.jobs[id]
if !ok {
return
}
// if the job has nextScheduled time in the past,
// we need to remove any that are in the past.
var newNextScheduled []time.Time
for _, t := range j.nextScheduled {
if t.Before(s.now()) {
continue
}
newNextScheduled = append(newNextScheduled, t)
}
j.nextScheduled = newNextScheduled
// if the job has a limited number of runs set, we need to
// check how many runs have occurred and stop running this
// job if it has reached the limit.
if j.limitRunsTo != nil {
j.limitRunsTo.runCount = j.limitRunsTo.runCount + 1
if j.limitRunsTo.runCount == j.limitRunsTo.limit {
go func() {
select {
case <-s.shutdownCtx.Done():
return
case s.removeJobCh <- id:
}
}()
return
}
}
j.lastRun = s.now()
s.jobs[id] = j
}
func (s *scheduler) selectJobOutRequest(out jobOutRequest) {
if j, ok := s.jobs[out.id]; ok {
select {
case out.outChan <- j:
case <-s.shutdownCtx.Done():
}
}
close(out.outChan)
}
func (s *scheduler) selectNewJob(in newJobIn) {
j := in.job
if s.started {
next := j.startTime
if j.startImmediately {
next = s.now()
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: j.id,
shouldSendOut: true,
}:
}
} else {
if next.IsZero() {
next = j.next(s.now())
}
id := j.id
j.timer = s.exec.clock.AfterFunc(next.Sub(s.now()), func() {
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: id,
shouldSendOut: true,
}:
}
})
}
j.startTime = next
j.nextScheduled = append(j.nextScheduled, next)
}
s.jobs[j.id] = j
in.cancel()
}
func (s *scheduler) selectRemoveJobsByTags(tags []string) {
for _, j := range s.jobs {
for _, tag := range tags {
if slices.Contains(j.tags, tag) {
j.stop()
delete(s.jobs, j.id)
break
}
}
}
}
func (s *scheduler) selectStart() {
s.logger.Debug("gocron: scheduler starting")
go s.exec.start()
s.started = true
for id, j := range s.jobs {
next := j.startTime
if j.startImmediately {
next = s.now()
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: id,
shouldSendOut: true,
}:
}
} else {
if next.IsZero() {
next = j.next(s.now())
}
jobID := id
j.timer = s.exec.clock.AfterFunc(next.Sub(s.now()), func() {
select {
case <-s.shutdownCtx.Done():
case s.exec.jobsIn <- jobIn{
id: jobID,
shouldSendOut: true,
}:
}
})
}
j.startTime = next
j.nextScheduled = append(j.nextScheduled, next)
s.jobs[id] = j
}
select {
case <-s.shutdownCtx.Done():
case s.startedCh <- struct{}{}:
s.logger.Info("gocron: scheduler started")
}
}
// -----------------------------------------------
// -----------------------------------------------
// ------------- Scheduler Methods ---------------
// -----------------------------------------------
// -----------------------------------------------
func (s *scheduler) now() time.Time {
return s.exec.clock.Now().In(s.location)
}
func (s *scheduler) jobFromInternalJob(in internalJob) job {
return job{
in.id,
in.name,
slices.Clone(in.tags),
s.jobOutRequestCh,
s.runJobRequestCh,
}
}
func (s *scheduler) Jobs() []Job {
outChan := make(chan []Job)
select {
case <-s.shutdownCtx.Done():
case s.allJobsOutRequest <- allJobsOutRequest{outChan: outChan}:
}
var jobs []Job
select {
case <-s.shutdownCtx.Done():
case jobs = <-outChan:
}
return jobs
}
func (s *scheduler) NewJob(jobDefinition JobDefinition, task Task, options ...JobOption) (Job, error) {
return s.addOrUpdateJob(uuid.Nil, jobDefinition, task, options)
}
func (s *scheduler) verifyInterfaceVariadic(taskFunc reflect.Value, tsk task, variadicStart int) error {
ifaceType := taskFunc.Type().In(variadicStart).Elem()
for i := variadicStart; i < len(tsk.parameters); i++ {
if !reflect.TypeOf(tsk.parameters[i]).Implements(ifaceType) {
return ErrNewJobWrongTypeOfParameters
}
}
return nil
}
func (s *scheduler) verifyVariadic(taskFunc reflect.Value, tsk task, variadicStart int) error {
if err := s.verifyNonVariadic(taskFunc, tsk, variadicStart); err != nil {
return err
}
parameterType := taskFunc.Type().In(variadicStart).Elem().Kind()
if parameterType == reflect.Interface {
return s.verifyInterfaceVariadic(taskFunc, tsk, variadicStart)
}
if parameterType == reflect.Pointer {
parameterType = reflect.Indirect(reflect.ValueOf(taskFunc.Type().In(variadicStart))).Kind()
}
for i := variadicStart; i < len(tsk.parameters); i++ {
argumentType := reflect.TypeOf(tsk.parameters[i]).Kind()
if argumentType == reflect.Interface || argumentType == reflect.Pointer {
argumentType = reflect.TypeOf(tsk.parameters[i]).Elem().Kind()
}
if argumentType != parameterType {
return ErrNewJobWrongTypeOfParameters
}
}
return nil
}
func (s *scheduler) verifyNonVariadic(taskFunc reflect.Value, tsk task, length int) error {
for i := 0; i < length; i++ {
t1 := reflect.TypeOf(tsk.parameters[i]).Kind()
if t1 == reflect.Interface || t1 == reflect.Pointer {
t1 = reflect.TypeOf(tsk.parameters[i]).Elem().Kind()
}
t2 := reflect.New(taskFunc.Type().In(i)).Elem().Kind()
if t2 == reflect.Interface || t2 == reflect.Pointer {
t2 = reflect.Indirect(reflect.ValueOf(taskFunc.Type().In(i))).Kind()
}
if t1 != t2 {
return ErrNewJobWrongTypeOfParameters
}
}
return nil
}
func (s *scheduler) verifyParameterType(taskFunc reflect.Value, tsk task) error {
isVariadic := taskFunc.Type().IsVariadic()
if isVariadic {
variadicStart := taskFunc.Type().NumIn() - 1
return s.verifyVariadic(taskFunc, tsk, variadicStart)
}
expectedParameterLength := taskFunc.Type().NumIn()
if len(tsk.parameters) != expectedParameterLength {
return ErrNewJobWrongNumberOfParameters
}
return s.verifyNonVariadic(taskFunc, tsk, expectedParameterLength)
}
func (s *scheduler) addOrUpdateJob(id uuid.UUID, definition JobDefinition, taskWrapper Task, options []JobOption) (Job, error) {
j := internalJob{}
if id == uuid.Nil {
j.id = uuid.New()
} else {
currentJob := requestJobCtx(s.shutdownCtx, id, s.jobOutRequestCh)
if currentJob != nil && currentJob.id != uuid.Nil {
select {
case <-s.shutdownCtx.Done():
return nil, nil
case s.removeJobCh <- id:
<-currentJob.ctx.Done()
}
}
j.id = id
}
if taskWrapper == nil {
return nil, ErrNewJobTaskNil
}
tsk := taskWrapper()
taskFunc := reflect.ValueOf(tsk.function)
for taskFunc.Kind() == reflect.Ptr {
taskFunc = taskFunc.Elem()
}
if taskFunc.Kind() != reflect.Func {
return nil, ErrNewJobTaskNotFunc
}
j.name = runtime.FuncForPC(taskFunc.Pointer()).Name()
j.function = tsk.function
j.parameters = tsk.parameters
// apply global job options
for _, option := range s.globalJobOptions {
if err := option(&j, s.now()); err != nil {
return nil, err
}
}
// apply job specific options, which take precedence
for _, option := range options {
if err := option(&j, s.now()); err != nil {
return nil, err
}
}
if j.parentCtx == nil {
j.parentCtx = s.shutdownCtx
}
j.ctx, j.cancel = context.WithCancel(j.parentCtx)
if !taskFunc.IsZero() && taskFunc.Type().NumIn() > 0 {
// if the first parameter is a context.Context and params have no context.Context, add current ctx to the params
if taskFunc.Type().In(0) == reflect.TypeOf((*context.Context)(nil)).Elem() {
if len(tsk.parameters) == 0 {
tsk.parameters = []any{j.ctx}
j.parameters = []any{j.ctx}
} else if _, ok := tsk.parameters[0].(context.Context); !ok {
tsk.parameters = append([]any{j.ctx}, tsk.parameters...)
j.parameters = append([]any{j.ctx}, j.parameters...)
}
}
}
if err := s.verifyParameterType(taskFunc, tsk); err != nil {
return nil, err
}
if err := definition.setup(&j, s.location, s.exec.clock.Now()); err != nil {
return nil, err
}
newJobCtx, newJobCancel := context.WithCancel(context.Background())
select {
case <-s.shutdownCtx.Done():
case s.newJobCh <- newJobIn{
ctx: newJobCtx,
cancel: newJobCancel,
job: j,
}:
}
select {
case <-newJobCtx.Done():
case <-s.shutdownCtx.Done():
}
out := s.jobFromInternalJob(j)
return &out, nil
}
func (s *scheduler) RemoveByTags(tags ...string) {
select {
case <-s.shutdownCtx.Done():
case s.removeJobsByTagsCh <- tags:
}
}
func (s *scheduler) RemoveJob(id uuid.UUID) error {
j := requestJobCtx(s.shutdownCtx, id, s.jobOutRequestCh)
if j == nil || j.id == uuid.Nil {
return ErrJobNotFound
}
select {
case <-s.shutdownCtx.Done():
case s.removeJobCh <- id:
}
return nil
}
func (s *scheduler) Start() {
select {
case <-s.shutdownCtx.Done():
case s.startCh <- struct{}{}:
<-s.startedCh
}
}
func (s *scheduler) StopJobs() error {
select {
case <-s.shutdownCtx.Done():
return nil
case s.stopCh <- struct{}{}:
}
t := time.NewTimer(s.exec.stopTimeout + 2*time.Second)
select {
case err := <-s.stopErrCh:
t.Stop()
return err
case <-t.C:
return ErrStopSchedulerTimedOut
}
}
func (s *scheduler) Shutdown() error {
s.shutdownCancel()
t := time.NewTimer(s.exec.stopTimeout + 2*time.Second)
select {
case err := <-s.stopErrCh:
t.Stop()
return err
case <-t.C:
return ErrStopSchedulerTimedOut
}
}
func (s *scheduler) Update(id uuid.UUID, jobDefinition JobDefinition, task Task, options ...JobOption) (Job, error) {
return s.addOrUpdateJob(id, jobDefinition, task, options)
}
func (s *scheduler) JobsWaitingInQueue() int {
if s.exec.limitMode != nil && s.exec.limitMode.mode == LimitModeWait {
return len(s.exec.limitMode.in)
}
return 0
}
// -----------------------------------------------
// -----------------------------------------------
// ------------- Scheduler Options ---------------
// -----------------------------------------------
// -----------------------------------------------
// SchedulerOption defines the function for setting
// options on the Scheduler.
type SchedulerOption func(*scheduler) error
// WithClock sets the clock used by the Scheduler
// to the clock provided. See https://github.com/jonboulle/clockwork
func WithClock(clock clockwork.Clock) SchedulerOption {
return func(s *scheduler) error {
if clock == nil {
return ErrWithClockNil
}
s.exec.clock = clock
return nil
}
}
// WithDistributedElector sets the elector to be used by multiple
// Scheduler instances to determine who should be the leader.
// Only the leader runs jobs, while non-leaders wait and continue
// to check if a new leader has been elected.
func WithDistributedElector(elector Elector) SchedulerOption {
return func(s *scheduler) error {
if elector == nil {
return ErrWithDistributedElectorNil
}
s.exec.elector = elector
return nil
}
}
// WithDistributedLocker sets the locker to be used by multiple
// Scheduler instances to ensure that only one instance of each
// job is run.
// To disable this global locker for specific jobs, see
// WithDisabledDistributedJobLocker.
func WithDistributedLocker(locker Locker) SchedulerOption {
return func(s *scheduler) error {
if locker == nil {
return ErrWithDistributedLockerNil
}
s.exec.locker = locker
return nil
}
}
// WithGlobalJobOptions sets JobOption's that will be applied to
// all jobs added to the scheduler. JobOption's set on the job
// itself will override if the same JobOption is set globally.
func WithGlobalJobOptions(jobOptions ...JobOption) SchedulerOption {
return func(s *scheduler) error {
s.globalJobOptions = jobOptions
return nil
}
}
// LimitMode defines the modes used for handling jobs that reach
// the limit provided in WithLimitConcurrentJobs
type LimitMode int
const (
// LimitModeReschedule causes jobs reaching the limit set in
// WithLimitConcurrentJobs or WithSingletonMode to be skipped
// and rescheduled for the next run time rather than being
// queued up to wait.
LimitModeReschedule = 1
// LimitModeWait causes jobs reaching the limit set in
// WithLimitConcurrentJobs or WithSingletonMode to wait
// in a queue until a slot becomes available to run.
//
// Note: this mode can produce unpredictable results as
// job execution order isn't guaranteed. For example, a job that
// executes frequently may pile up in the wait queue and be executed
// many times back to back when the queue opens.
//
// Warning: do not use this mode if your jobs will continue to stack
// up beyond the ability of the limit workers to keep up. An example of
// what NOT to do:
//
// s, _ := gocron.NewScheduler(gocron.WithLimitConcurrentJobs)
// s.NewJob(
// gocron.DurationJob(
// time.Second,
// Task{
// Function: func() {
// time.Sleep(10 * time.Second)
// },
// },
// ),
// )
LimitModeWait = 2
)
// WithLimitConcurrentJobs sets the limit and mode to be used by the
// Scheduler for limiting the number of jobs that may be running at
// a given time.
//
// Note: the limit mode selected for WithLimitConcurrentJobs takes initial
// precedence in the event you are also running a limit mode at the job level
// using WithSingletonMode.
//
// Warning: a single time consuming job can dominate your limit in the event
// you are running both the scheduler limit WithLimitConcurrentJobs(1, LimitModeWait)
// and a job limit WithSingletonMode(LimitModeReschedule).
func WithLimitConcurrentJobs(limit uint, mode LimitMode) SchedulerOption {
return func(s *scheduler) error {
if limit == 0 {
return ErrWithLimitConcurrentJobsZero
}
s.exec.limitMode = &limitModeConfig{
mode: mode,
limit: limit,
in: make(chan jobIn, 1000),
singletonJobs: make(map[uuid.UUID]struct{}),
}
if mode == LimitModeReschedule {
s.exec.limitMode.rescheduleLimiter = make(chan struct{}, limit)
}
return nil
}
}
// WithLocation sets the location (i.e. timezone) that the scheduler
// should operate within. In many systems time.Local is UTC.
// Default: time.Local
func WithLocation(location *time.Location) SchedulerOption {
return func(s *scheduler) error {
if location == nil {
return ErrWithLocationNil
}
s.location = location
return nil
}
}
// WithLogger sets the logger to be used by the Scheduler.
func WithLogger(logger Logger) SchedulerOption {
return func(s *scheduler) error {
if logger == nil {
return ErrWithLoggerNil
}
s.logger = logger
s.exec.logger = logger
return nil
}
}
// WithStopTimeout sets the amount of time the Scheduler should
// wait gracefully for jobs to complete before returning when
// StopJobs() or Shutdown() are called.
// Default: 10 * time.Second
func WithStopTimeout(timeout time.Duration) SchedulerOption {
return func(s *scheduler) error {
if timeout <= 0 {
return ErrWithStopTimeoutZeroOrNegative
}
s.exec.stopTimeout = timeout
return nil
}
}
// WithMonitor sets the metrics provider to be used by the Scheduler.
func WithMonitor(monitor Monitor) SchedulerOption {
return func(s *scheduler) error {
if monitor == nil {
return ErrWithMonitorNil
}
s.exec.monitor = monitor
return nil
}
}
// WithMonitorStatus sets the metrics provider to be used by the Scheduler.
func WithMonitorStatus(monitor MonitorStatus) SchedulerOption {
return func(s *scheduler) error {
if monitor == nil {
return ErrWithMonitorNil
}
s.exec.monitorStatus = monitor
return nil
}
}

118
vendor/github.com/go-co-op/gocron/v2/util.go generated vendored Normal file
View File

@@ -0,0 +1,118 @@
package gocron
import (
"context"
"reflect"
"sync"
"time"
"github.com/google/uuid"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
func callJobFuncWithParams(jobFunc any, params ...any) error {
if jobFunc == nil {
return nil
}
f := reflect.ValueOf(jobFunc)
if f.IsZero() {
return nil
}
if len(params) != f.Type().NumIn() {
return nil
}
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
returnValues := f.Call(in)
for _, val := range returnValues {
i := val.Interface()
if err, ok := i.(error); ok {
return err
}
}
return nil
}
func requestJob(id uuid.UUID, ch chan jobOutRequest) *internalJob {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
return requestJobCtx(ctx, id, ch)
}
func requestJobCtx(ctx context.Context, id uuid.UUID, ch chan jobOutRequest) *internalJob {
resp := make(chan internalJob, 1)
select {
case ch <- jobOutRequest{
id: id,
outChan: resp,
}:
case <-ctx.Done():
return nil
}
var j internalJob
select {
case <-ctx.Done():
return nil
case jobReceived := <-resp:
j = jobReceived
}
return &j
}
func removeSliceDuplicatesInt(in []int) []int {
m := make(map[int]struct{})
for _, i := range in {
m[i] = struct{}{}
}
return maps.Keys(m)
}
func convertAtTimesToDateTime(atTimes AtTimes, location *time.Location) ([]time.Time, error) {
if atTimes == nil {
return nil, errAtTimesNil
}
var atTimesDate []time.Time
for _, a := range atTimes() {
if a == nil {
return nil, errAtTimeNil
}
at := a()
if at.hours > 23 {
return nil, errAtTimeHours
} else if at.minutes > 59 || at.seconds > 59 {
return nil, errAtTimeMinSec
}
atTimesDate = append(atTimesDate, at.time(location))
}
slices.SortStableFunc(atTimesDate, ascendingTime)
return atTimesDate, nil
}
func ascendingTime(a, b time.Time) int {
return a.Compare(b)
}
type waitGroupWithMutex struct {
wg sync.WaitGroup
mu sync.Mutex
}
func (w *waitGroupWithMutex) Add(delta int) {
w.mu.Lock()
defer w.mu.Unlock()
w.wg.Add(delta)
}
func (w *waitGroupWithMutex) Done() {
w.wg.Done()
}
func (w *waitGroupWithMutex) Wait() {
w.mu.Lock()
defer w.mu.Unlock()
w.wg.Wait()
}

262
vendor/github.com/go-jet/jet/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,262 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 Goran Bjelanovic
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
------------------------------------------------------------------------------------
This product builds on various third-party components under other open source licenses.
This section summarizes those components and their licenses.
https://github.com/dropbox/godropbox
---------------------------------------
Copyright (c) 2014 Dropbox, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
https://github.com/serenize/snaker
---------------------------------------
Copyright (c) 2015 Serenize UG (haftungsbeschränkt)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

14
vendor/github.com/go-jet/jet/v2/NOTICE generated vendored Normal file
View File

@@ -0,0 +1,14 @@
Jet
Copyright 2019 Goran Bjelanovic
This product contains a modified portion of 'godropbox' which can be obtained at:
https://github.com/dropbox/godropbox/tree/master/database/sqlbuilder (BSD-3)
This product contains a modified portion of 'snaker' which can be obtained at:
https://github.com/serenize/snaker (MIT)
This product contains `FormatTimestamp` function from 'pq' which can be obtained at:
https://github.com/lib/pq (MIT)

View File

@@ -0,0 +1,42 @@
package pq
// Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany
import (
"strconv"
"time"
)
// FormatTimestamp formats t into Postgres' text format for timestamps. From: github.com/lib/pq
func FormatTimestamp(t time.Time) []byte {
// Need to send dates before 0001 A.D. with " BC" suffix, instead of the
// minus sign preferred by Go.
// Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
bc := false
if t.Year() <= 0 {
// flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11"
t = t.AddDate((-t.Year())*2+1, 0, 0)
bc = true
}
b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00"))
_, offset := t.Zone()
offset = offset % 60
if offset != 0 {
// RFC3339Nano already printed the minus sign
if offset < 0 {
offset = -offset
}
b = append(b, ':')
if offset < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(offset), 10)
}
if bc {
b = append(b, " BC"...)
}
return b
}

View File

@@ -0,0 +1,119 @@
package snaker
// Package snaker provides methods to convert CamelCase names to snake_case and back.
// It considers the list of allowed initialsms used by github.com/golang/lint/golint (e.g. ID or HTTP)
import (
"strings"
"unicode"
)
// SnakeToCamel returns a string converted from snake case to uppercase
func SnakeToCamel(s string, firstLetterUppercase ...bool) string {
upperCase := true
if len(firstLetterUppercase) > 0 {
upperCase = firstLetterUppercase[0]
}
return snakeToCamel(s, upperCase)
}
func snakeToCamel(s string, upperCase bool) string {
if len(s) == 0 {
return s
}
var result string
words := strings.Split(s, "_")
for i, word := range words {
if exception := snakeToCamelExceptions[word]; len(exception) > 0 {
result += exception
continue
}
if upperCase || i > 0 {
if upper := strings.ToUpper(word); commonInitialisms[upper] {
result += upper
continue
}
}
if upperCase || i > 0 {
result += camelizeWord(word, len(words) > 1)
} else {
result += word
}
}
return result
}
func camelizeWord(word string, force bool) string {
runes := []rune(word)
for i, r := range runes {
if i == 0 {
runes[i] = unicode.ToUpper(r)
} else {
if !force && unicode.IsLower(r) { // already camelCase
return string(runes)
}
runes[i] = unicode.ToLower(r)
}
}
return string(runes)
}
// commonInitialisms, taken from
// https://github.com/golang/lint/blob/206c0f020eba0f7fbcfbc467a5eb808037df2ed6/lint.go#L731
var commonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"DNS": true,
"EOF": true,
"ETA": true,
"GPU": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ID": true,
"IP": true,
"JSON": true,
"LHS": true,
"OS": true,
"QPS": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"UUID": true,
"URI": true,
"URL": true,
"UTF8": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
"OAuth": true,
}
// add exceptions here for things that are not automatically convertable
var snakeToCamelExceptions = map[string]string{
"oauth": "OAuth",
}

32
vendor/github.com/go-jet/jet/v2/internal/jet/alias.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package jet
type alias struct {
expression Expression
alias string
}
func newAlias(expression Expression, aliasName string) Projection {
return &alias{
expression: expression,
alias: aliasName,
}
}
func (a *alias) fromImpl(subQuery SelectTable) Projection {
// if alias is in the form "table.column", we break it into two parts so that ProjectionList.As(newAlias) can
// overwrite tableName with a new alias. This method is called only for exporting aliased custom columns.
// Generated columns have default aliasing.
tableName, columnName := extractTableAndColumnName(a.alias)
column := NewColumnImpl(columnName, tableName, nil)
column.subQuery = subQuery
return &column
}
func (a *alias) serializeForProjection(statement StatementType, out *SQLBuilder) {
a.expression.serialize(statement, out)
out.WriteString("AS")
out.WriteAlias(a.alias)
}

View File

@@ -0,0 +1,115 @@
package jet
// BoolExpression interface
type BoolExpression interface {
Expression
// Check if this expression is equal to rhs
EQ(rhs BoolExpression) BoolExpression
// Check if this expression is not equal to rhs
NOT_EQ(rhs BoolExpression) BoolExpression
// Check if this expression is distinct to rhs
IS_DISTINCT_FROM(rhs BoolExpression) BoolExpression
// Check if this expression is not distinct to rhs
IS_NOT_DISTINCT_FROM(rhs BoolExpression) BoolExpression
// Check if this expression is true
IS_TRUE() BoolExpression
// Check if this expression is not true
IS_NOT_TRUE() BoolExpression
// Check if this expression is false
IS_FALSE() BoolExpression
// Check if this expression is not false
IS_NOT_FALSE() BoolExpression
// Check if this expression is unknown
IS_UNKNOWN() BoolExpression
// Check if this expression is not unknown
IS_NOT_UNKNOWN() BoolExpression
// expression AND operator rhs
AND(rhs BoolExpression) BoolExpression
// expression OR operator rhs
OR(rhs BoolExpression) BoolExpression
}
type boolInterfaceImpl struct {
parent BoolExpression
}
func (b *boolInterfaceImpl) EQ(expression BoolExpression) BoolExpression {
return Eq(b.parent, expression)
}
func (b *boolInterfaceImpl) NOT_EQ(expression BoolExpression) BoolExpression {
return NotEq(b.parent, expression)
}
func (b *boolInterfaceImpl) IS_DISTINCT_FROM(rhs BoolExpression) BoolExpression {
return IsDistinctFrom(b.parent, rhs)
}
func (b *boolInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs BoolExpression) BoolExpression {
return IsNotDistinctFrom(b.parent, rhs)
}
func (b *boolInterfaceImpl) AND(expression BoolExpression) BoolExpression {
return newBinaryBoolOperatorExpression(b.parent, expression, "AND")
}
func (b *boolInterfaceImpl) OR(expression BoolExpression) BoolExpression {
return newBinaryBoolOperatorExpression(b.parent, expression, "OR")
}
func (b *boolInterfaceImpl) IS_TRUE() BoolExpression {
return newPostfixBoolOperatorExpression(b.parent, "IS TRUE")
}
func (b *boolInterfaceImpl) IS_NOT_TRUE() BoolExpression {
return newPostfixBoolOperatorExpression(b.parent, "IS NOT TRUE")
}
func (b *boolInterfaceImpl) IS_FALSE() BoolExpression {
return newPostfixBoolOperatorExpression(b.parent, "IS FALSE")
}
func (b *boolInterfaceImpl) IS_NOT_FALSE() BoolExpression {
return newPostfixBoolOperatorExpression(b.parent, "IS NOT FALSE")
}
func (b *boolInterfaceImpl) IS_UNKNOWN() BoolExpression {
return newPostfixBoolOperatorExpression(b.parent, "IS UNKNOWN")
}
func (b *boolInterfaceImpl) IS_NOT_UNKNOWN() BoolExpression {
return newPostfixBoolOperatorExpression(b.parent, "IS NOT UNKNOWN")
}
func newBinaryBoolOperatorExpression(lhs, rhs Expression, operator string, additionalParams ...Expression) BoolExpression {
return BoolExp(NewBinaryOperatorExpression(lhs, rhs, operator, additionalParams...))
}
func newPrefixBoolOperatorExpression(expression Expression, operator string) BoolExpression {
return BoolExp(newPrefixOperatorExpression(expression, operator))
}
func newPostfixBoolOperatorExpression(expression Expression, operator string) BoolExpression {
return BoolExp(newPostfixOperatorExpression(expression, operator))
}
type boolExpressionWrapper struct {
boolInterfaceImpl
Expression
}
func newBoolExpressionWrap(expression Expression) BoolExpression {
boolExpressionWrap := boolExpressionWrapper{Expression: expression}
boolExpressionWrap.boolInterfaceImpl.parent = &boolExpressionWrap
return &boolExpressionWrap
}
// BoolExp is bool expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as bool expression.
// Does not add sql cast to generated sql builder output.
func BoolExp(expression Expression) BoolExpression {
return newBoolExpressionWrap(expression)
}

53
vendor/github.com/go-jet/jet/v2/internal/jet/cast.go generated vendored Normal file
View File

@@ -0,0 +1,53 @@
package jet
// Cast interface
type Cast interface {
AS(castType string) Expression
}
type castImpl struct {
expression Expression
}
// NewCastImpl creates new generic cast
func NewCastImpl(expression Expression) Cast {
castImpl := castImpl{
expression: expression,
}
return &castImpl
}
func (b *castImpl) AS(castType string) Expression {
castExp := &castExpression{
expression: b.expression,
cast: string(castType),
}
castExp.ExpressionInterfaceImpl.Parent = castExp
return castExp
}
type castExpression struct {
ExpressionInterfaceImpl
expression Expression
cast string
}
func (b *castExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
expression := b.expression
castType := b.cast
if castOverride := out.Dialect.OperatorSerializeOverride("CAST"); castOverride != nil {
castOverride(expression, String(castType))(statement, out, FallTrough(options)...)
return
}
out.WriteString("CAST(")
expression.serialize(statement, out, FallTrough(options)...)
out.WriteString("AS")
out.WriteString(castType + ")")
}

672
vendor/github.com/go-jet/jet/v2/internal/jet/clause.go generated vendored Normal file
View File

@@ -0,0 +1,672 @@
package jet
import "github.com/go-jet/jet/v2/internal/utils/is"
// Clause interface
type Clause interface {
Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption)
}
// ClauseWithProjections interface
type ClauseWithProjections interface {
Clause
Projections() ProjectionList
}
// OptimizerHint provides a way to optimize query execution per-statement basis
type OptimizerHint string
type optimizerHints []OptimizerHint
func (o optimizerHints) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(o) == 0 {
return
}
out.WriteString("/*+")
for i, hint := range o {
if i > 0 {
out.WriteByte(' ')
}
out.WriteString(string(hint))
}
out.WriteString("*/")
}
// ClauseSelect struct
type ClauseSelect struct {
Distinct bool
DistinctOnColumns []ColumnExpression
ProjectionList []Projection
// MySQL only
OptimizerHints optimizerHints
}
// Projections returns list of projections for select clause
func (s *ClauseSelect) Projections() ProjectionList {
return s.ProjectionList
}
// Serialize serializes clause into SQLBuilder
func (s *ClauseSelect) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
out.NewLine()
out.WriteString("SELECT")
s.OptimizerHints.Serialize(statementType, out, options...)
if s.Distinct {
out.WriteString("DISTINCT")
}
if len(s.DistinctOnColumns) > 0 {
out.WriteString("ON (")
SerializeColumnExpressions(s.DistinctOnColumns, statementType, out)
out.WriteByte(')')
}
if len(s.ProjectionList) == 0 {
panic("jet: SELECT clause has to have at least one projection")
}
out.WriteProjections(statementType, s.ProjectionList)
}
// ClauseFrom struct
type ClauseFrom struct {
Name string
Tables []Serializer
}
// Serialize serializes clause into SQLBuilder
func (f *ClauseFrom) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(f.Tables) == 0 { // SELECT statement does not have to have FROM clause
return
}
out.NewLine()
if f.Name != "" {
out.WriteString(f.Name)
} else {
out.WriteString("FROM")
}
out.IncreaseIdent()
for i, table := range f.Tables {
if i > 0 {
out.WriteString(",")
out.NewLine()
}
table.serialize(statementType, out, FallTrough(options)...)
}
out.DecreaseIdent()
}
// ClauseWhere struct
type ClauseWhere struct {
Condition BoolExpression
Mandatory bool
}
// Serialize serializes clause into SQLBuilder
func (c *ClauseWhere) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if c.Condition == nil {
if c.Mandatory {
panic("jet: WHERE clause not set")
}
return
}
if !contains(options, SkipNewLine) {
out.NewLine()
}
out.WriteString("WHERE")
out.IncreaseIdent(6)
c.Condition.serialize(statementType, out, NoWrap.WithFallTrough(options)...)
out.DecreaseIdent(6)
}
// ClauseGroupBy struct
type ClauseGroupBy struct {
List []GroupByClause
}
// Serialize serializes clause into SQLBuilder
func (c *ClauseGroupBy) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(c.List) == 0 {
return
}
out.NewLine()
out.WriteString("GROUP BY")
out.IncreaseIdent()
for i, c := range c.List {
if i > 0 {
out.WriteString(", ")
}
if c == nil {
panic("jet: nil clause in GROUP BY list")
}
c.serializeForGroupBy(statementType, out)
}
out.DecreaseIdent()
}
// ClauseHaving struct
type ClauseHaving struct {
Condition BoolExpression
}
// Serialize serializes clause into SQLBuilder
func (c *ClauseHaving) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if c.Condition == nil {
return
}
out.NewLine()
out.WriteString("HAVING")
out.IncreaseIdent()
c.Condition.serialize(statementType, out, NoWrap.WithFallTrough(options)...)
out.DecreaseIdent()
}
// ClauseOrderBy struct
type ClauseOrderBy struct {
List []OrderByClause
SkipNewLine bool
}
// Serialize serializes clause into SQLBuilder
func (o *ClauseOrderBy) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if o.List == nil {
return
}
if !o.SkipNewLine {
out.NewLine()
}
out.WriteString("ORDER BY")
out.IncreaseIdent()
for i, value := range o.List {
if i > 0 {
out.WriteString(", ")
}
value.serializeForOrderBy(statementType, out)
}
out.DecreaseIdent()
}
// ClauseLimit struct
type ClauseLimit struct {
Count int64
}
// Serialize serializes clause into SQLBuilder
func (l *ClauseLimit) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if l.Count >= 0 {
out.NewLine()
out.WriteString("LIMIT")
out.insertParametrizedArgument(l.Count)
}
}
// ClauseOffset struct
type ClauseOffset struct {
Count IntegerExpression
}
// Serialize serializes clause into SQLBuilder
func (o *ClauseOffset) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if is.Nil(o.Count) {
return
}
out.NewLine()
out.WriteString("OFFSET")
o.Count.serialize(statementType, out, options...)
}
// ClauseFetch struct
type ClauseFetch struct {
Count IntegerExpression
WithTies bool
}
// Serialize serializes ClauseFetch into sql builder output
func (o *ClauseFetch) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if is.Nil(o.Count) {
return
}
out.NewLine()
out.WriteString("FETCH FIRST")
o.Count.serialize(statementType, out, options...)
if o.WithTies {
out.WriteString("ROWS WITH TIES")
} else {
out.WriteString("ROWS ONLY")
}
}
// ClauseFor struct
type ClauseFor struct {
Lock RowLock
}
// Serialize serializes clause into SQLBuilder
func (f *ClauseFor) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if f.Lock == nil {
return
}
out.NewLine()
out.WriteString("FOR")
f.Lock.serialize(statementType, out, FallTrough(options)...)
}
// ClauseSetStmtOperator struct
type ClauseSetStmtOperator struct {
Operator string
All bool
Selects []SerializerStatement
OrderBy ClauseOrderBy
Limit ClauseLimit
Offset ClauseOffset
SkipSelectWrap bool
}
// Projections returns set of projections for ClauseSetStmtOperator
func (s *ClauseSetStmtOperator) Projections() ProjectionList {
if len(s.Selects) > 0 {
return s.Selects[0].projections()
}
return nil
}
// Serialize serializes clause into SQLBuilder
func (s *ClauseSetStmtOperator) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(s.Selects) < 2 {
panic("jet: UNION Statement must contain at least two SELECT statements")
}
for i, selectStmt := range s.Selects {
out.NewLine()
if i > 0 {
if s.SkipSelectWrap {
out.NewLine()
}
out.WriteString(s.Operator)
if s.All {
out.WriteString("ALL")
}
out.NewLine()
}
if selectStmt == nil {
panic("jet: select statement of '" + s.Operator + "' is nil")
}
if s.SkipSelectWrap {
options = append(FallTrough(options), NoWrap)
}
selectStmt.serialize(statementType, out, options...)
}
s.OrderBy.Serialize(statementType, out)
s.Limit.Serialize(statementType, out)
s.Offset.Serialize(statementType, out)
}
// ClauseUpdate struct
type ClauseUpdate struct {
Table SerializerTable
// MySQL only
OptimizerHints optimizerHints
}
// Serialize serializes clause into SQLBuilder
func (u *ClauseUpdate) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
out.NewLine()
out.WriteString("UPDATE")
u.OptimizerHints.Serialize(statementType, out, options...)
if is.Nil(u.Table) {
panic("jet: table to update is nil")
}
u.Table.serialize(statementType, out, FallTrough(options)...)
}
// SetClause struct
type SetClause struct {
Columns []Column
Values []Serializer
}
// Serialize serializes clause into SQLBuilder
func (s *SetClause) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(s.Values) == 0 {
return
}
out.NewLine()
out.WriteString("SET")
if len(s.Columns) != len(s.Values) {
panic("jet: mismatch in numbers of columns and values for SET clause")
}
out.IncreaseIdent(4)
for i, column := range s.Columns {
if i > 0 {
out.WriteString(",")
out.NewLine()
}
if column == nil {
panic("jet: nil column in columns list for SET clause")
}
out.WriteIdentifier(column.Name())
out.WriteString(" = ")
s.Values[i].serialize(UpdateStatementType, out, FallTrough(options)...)
}
out.DecreaseIdent(4)
}
// ClauseInsert struct
type ClauseInsert struct {
Table SerializerTable
Columns []Column
// MySQL only
OptimizerHints optimizerHints
}
// GetColumns gets list of columns for insert
func (i *ClauseInsert) GetColumns() []Column {
if len(i.Columns) > 0 {
return i.Columns
}
return i.Table.columns()
}
// Serialize serializes clause into SQLBuilder
func (i *ClauseInsert) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if is.Nil(i.Table) {
panic("jet: table is nil for INSERT clause")
}
out.NewLine()
out.WriteString("INSERT")
i.OptimizerHints.Serialize(statementType, out, options...)
out.WriteString("INTO")
i.Table.serialize(statementType, out)
if len(i.Columns) > 0 {
out.WriteString("(")
SerializeColumnNames(i.Columns, out)
out.WriteString(")")
}
}
// ClauseValuesQuery struct
type ClauseValuesQuery struct {
ClauseValues
ClauseQuery
}
// Serialize serializes clause into SQLBuilder
func (v *ClauseValuesQuery) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(v.Rows) > 0 && v.Query != nil {
panic("jet: VALUES or QUERY has to be specified for INSERT statement")
}
v.ClauseValues.Serialize(statementType, out, FallTrough(options)...)
v.ClauseQuery.Serialize(statementType, out, FallTrough(options)...)
}
// ClauseValues struct
type ClauseValues struct {
Rows [][]Serializer
As string
}
// Serialize serializes clause into SQLBuilder
func (v *ClauseValues) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(v.Rows) == 0 {
return
}
out.NewLine()
out.WriteString("VALUES")
for rowIndex, row := range v.Rows {
if rowIndex > 0 {
out.WriteString(",")
out.NewLine()
} else {
out.IncreaseIdent(7)
}
out.WriteString("(")
SerializeClauseList(statementType, row, out)
out.WriteByte(')')
}
if len(v.As) > 0 {
out.WriteString("AS")
out.WriteIdentifier(v.As)
}
out.DecreaseIdent(7)
}
// ClauseQuery struct
type ClauseQuery struct {
Query SerializerStatement
SkipSelectWrap bool
}
// Serialize serializes clause into SQLBuilder
func (v *ClauseQuery) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if v.Query == nil {
return
}
if v.SkipSelectWrap {
options = append(FallTrough(options), NoWrap)
}
v.Query.serialize(statementType, out, options...)
}
// ClauseDelete struct
type ClauseDelete struct {
Table SerializerTable
// MySQL only
OptimizerHints optimizerHints
}
// Serialize serializes clause into SQLBuilder
func (d *ClauseDelete) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
out.NewLine()
out.WriteString("DELETE")
d.OptimizerHints.Serialize(statementType, out, options...)
out.WriteString("FROM")
d.Table.serialize(statementType, out, FallTrough(options)...)
}
// ClauseStatementBegin struct
type ClauseStatementBegin struct {
Name string
Tables []SerializerTable
}
// Serialize serializes clause into SQLBuilder
func (d *ClauseStatementBegin) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
out.NewLine()
out.WriteString(d.Name)
for i, table := range d.Tables {
if i > 0 {
out.WriteString(", ")
}
table.serialize(statementType, out, FallTrough(options)...)
}
}
// ClauseOptional struct
type ClauseOptional struct {
Name string
Show bool
InNewLine bool
}
// Serialize serializes clause into SQLBuilder
func (d *ClauseOptional) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if !d.Show {
return
}
if d.InNewLine {
out.NewLine()
}
out.WriteString(d.Name)
}
// ClauseIn struct
type ClauseIn struct {
LockMode string
}
// Serialize serializes clause into SQLBuilder
func (i *ClauseIn) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if i.LockMode == "" {
return
}
out.WriteString("IN")
out.WriteString(string(i.LockMode))
out.WriteString("MODE")
}
// WindowDefinition struct
type WindowDefinition struct {
Name string
Window Window
}
// ClauseWindow struct
type ClauseWindow struct {
Definitions []WindowDefinition
}
// Serialize serializes clause into SQLBuilder
func (i *ClauseWindow) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(i.Definitions) == 0 {
return
}
out.NewLine()
out.WriteString("WINDOW")
for i, def := range i.Definitions {
if i > 0 {
out.WriteString(", ")
}
out.WriteString(def.Name)
out.WriteString("AS")
if def.Window == nil {
out.WriteString("()")
continue
}
def.Window.serialize(statementType, out, FallTrough(options)...)
}
}
// SetPair clause
type SetPair struct {
Column ColumnSerializer
Value Serializer
}
// SetClauseNew clause
type SetClauseNew []ColumnAssigment
// Serialize for SetClauseNew
func (s SetClauseNew) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(s) == 0 {
return
}
out.NewLine()
out.WriteString("SET")
out.IncreaseIdent(4)
for i, assigment := range s {
if i > 0 {
out.WriteString(",")
out.NewLine()
}
assigment.serialize(statementType, out, FallTrough(options)...)
}
out.DecreaseIdent(4)
}
// KeywordClause type
type KeywordClause struct {
Keyword
}
// Serialize for KeywordClause
func (k KeywordClause) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
k.serialize(statementType, out, FallTrough(options)...)
}
// ClauseReturning type
type ClauseReturning struct {
ProjectionList []Projection
}
// Serialize for ClauseReturning
func (r *ClauseReturning) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(r.ProjectionList) == 0 {
return
}
out.NewLine()
out.WriteString("RETURNING")
out.IncreaseIdent()
out.WriteProjections(statementType, r.ProjectionList)
out.DecreaseIdent()
}
// Projections for ClauseReturning
func (r ClauseReturning) Projections() ProjectionList {
return r.ProjectionList
}

117
vendor/github.com/go-jet/jet/v2/internal/jet/column.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
// Modeling of columns
package jet
// Column is common column interface for all types of columns.
type Column interface {
Name() string
TableName() string
setTableName(table string)
setSubQuery(subQuery SelectTable)
defaultAlias() string
}
// ColumnSerializer is interface for all serializable columns
type ColumnSerializer interface {
Serializer
Column
}
// ColumnExpression interface
type ColumnExpression interface {
Column
Expression
}
// ColumnExpressionImpl is base type for sql columns.
type ColumnExpressionImpl struct {
ExpressionInterfaceImpl
name string
tableName string
subQuery SelectTable
}
// NewColumnImpl creates new ColumnExpressionImpl
func NewColumnImpl(name string, tableName string, parent ColumnExpression) ColumnExpressionImpl {
bc := ColumnExpressionImpl{
name: name,
tableName: tableName,
}
if parent != nil {
bc.ExpressionInterfaceImpl.Parent = parent
} else {
bc.ExpressionInterfaceImpl.Parent = &bc
}
return bc
}
// Name returns name of the column
func (c *ColumnExpressionImpl) Name() string {
return c.name
}
// TableName returns column table name
func (c *ColumnExpressionImpl) TableName() string {
return c.tableName
}
func (c *ColumnExpressionImpl) setTableName(table string) {
c.tableName = table
}
func (c *ColumnExpressionImpl) setSubQuery(subQuery SelectTable) {
c.subQuery = subQuery
}
func (c *ColumnExpressionImpl) defaultAlias() string {
if c.tableName != "" {
return c.tableName + "." + c.name
}
return c.name
}
func (c *ColumnExpressionImpl) fromImpl(subQuery SelectTable) Projection {
newColumn := NewColumnImpl(c.name, c.tableName, nil)
newColumn.setSubQuery(subQuery)
return &newColumn
}
func (c *ColumnExpressionImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
if statement == SetStatementType {
// set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause
out.WriteAlias(c.defaultAlias()) //always quote
return
}
c.serialize(statement, out)
}
func (c ColumnExpressionImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
c.serialize(statement, out)
out.WriteString("AS")
out.WriteAlias(c.defaultAlias())
}
func (c ColumnExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if c.subQuery != nil {
out.WriteIdentifier(c.subQuery.Alias())
out.WriteByte('.')
out.WriteIdentifier(c.defaultAlias())
} else {
if c.tableName != "" && !contains(options, ShortName) {
out.WriteIdentifier(c.tableName)
out.WriteByte('.')
}
out.WriteIdentifier(c.name)
}
}

View File

@@ -0,0 +1,27 @@
package jet
// ColumnAssigment is interface wrapper around column assigment
type ColumnAssigment interface {
Serializer
isColumnAssigment()
}
type columnAssigmentImpl struct {
column ColumnSerializer
expression Expression
}
func NewColumnAssignment(serializer ColumnSerializer, expression Expression) ColumnAssigment {
return &columnAssigmentImpl{
column: serializer,
expression: expression,
}
}
func (a columnAssigmentImpl) isColumnAssigment() {}
func (a columnAssigmentImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
a.column.serialize(statement, out, ShortName.WithFallTrough(options)...)
out.WriteString("=")
a.expression.serialize(statement, out, FallTrough(options)...)
}

View File

@@ -0,0 +1,100 @@
package jet
// ColumnList is a helper type to support list of columns as single projection
type ColumnList []ColumnExpression
// SET creates column assigment for each column in column list. expression should be created by ROW function
//
// Link.UPDATE().
// SET(Link.MutableColumns.SET(ROW(String("github.com"), Bool(false))).
// WHERE(Link.ID.EQ(Int(0)))
func (cl ColumnList) SET(expression Expression) ColumnAssigment {
return columnAssigmentImpl{
column: cl,
expression: expression,
}
}
// Except will create new column list in which columns contained in list of excluded column names are removed
//
// Address.AllColumns.Except(Address.PostalCode, Address.Phone)
func (cl ColumnList) Except(excludedColumns ...Column) ColumnList {
excludedColumnList := UnwidColumnList(excludedColumns)
excludedColumnNames := map[string]bool{}
for _, excludedColumn := range excludedColumnList {
excludedColumnNames[excludedColumn.Name()] = true
}
var ret ColumnList
for _, column := range cl {
if excludedColumnNames[column.Name()] {
continue
}
ret = append(ret, column)
}
return ret
}
// As will create new projection list where each column is wrapped with a new table alias.
// tableAlias should be in the form 'name' or 'name.*', or it can also be an empty string.
// For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will
// have a column wrapped in alias 'Musician.Name'. If tableAlias is empty string, it removes existing table alias ('Artist.Name' becomes 'Name').
func (cl ColumnList) As(tableAlias string) ProjectionList {
ret := make(ProjectionList, 0, len(cl))
for _, c := range cl {
ret = append(ret, c.AS(joinAlias(tableAlias, c.Name())))
}
return ret
}
func (cl ColumnList) fromImpl(subQuery SelectTable) Projection {
newProjectionList := ProjectionList{}
for _, column := range cl {
newProjectionList = append(newProjectionList, column.fromImpl(subQuery))
}
return newProjectionList
}
func (cl ColumnList) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("(")
for i, column := range cl {
if i > 0 {
out.WriteString(", ")
}
column.serialize(statement, out, FallTrough(options)...)
}
out.WriteString(")")
}
func (cl ColumnList) serializeForProjection(statement StatementType, out *SQLBuilder) {
projections := ColumnListToProjectionList(cl)
SerializeProjectionList(statement, projections, out)
}
// dummy column interface implementation
// Name is placeholder for ColumnList to implement Column interface
func (cl ColumnList) Name() string { return "" }
// TableName is placeholder for ColumnList to implement Column interface
func (cl ColumnList) TableName() string { return "" }
func (cl ColumnList) setTableName(name string) {}
func (cl ColumnList) setSubQuery(subQuery SelectTable) {}
func (cl ColumnList) defaultAlias() string { return "" }
// SetTableName is utility function to set table name from outside of jet package to avoid making public setTableName
func SetTableName(columnExpression ColumnExpression, tableName string) {
columnExpression.setTableName(tableName)
}
// SetSubQuery is utility function to set table name from outside of jet package to avoid making public setSubQuery
func SetSubQuery(columnExpression ColumnExpression, subQuery SelectTable) {
columnExpression.setSubQuery(subQuery)
}

View File

@@ -0,0 +1,401 @@
package jet
// ColumnBool is interface for SQL boolean columns.
type ColumnBool interface {
BoolExpression
Column
From(subQuery SelectTable) ColumnBool
SET(boolExp BoolExpression) ColumnAssigment
}
type boolColumnImpl struct {
boolInterfaceImpl
ColumnExpressionImpl
}
func (i *boolColumnImpl) From(subQuery SelectTable) ColumnBool {
newBoolColumn := BoolColumn(i.name)
newBoolColumn.setTableName(i.tableName)
newBoolColumn.setSubQuery(subQuery)
return newBoolColumn
}
func (i *boolColumnImpl) SET(boolExp BoolExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: boolExp,
}
}
// BoolColumn creates named bool column.
func BoolColumn(name string) ColumnBool {
boolColumn := &boolColumnImpl{}
boolColumn.ColumnExpressionImpl = NewColumnImpl(name, "", boolColumn)
boolColumn.boolInterfaceImpl.parent = boolColumn
return boolColumn
}
//------------------------------------------------------//
// ColumnFloat is interface for SQL real, numeric, decimal or double precision column.
type ColumnFloat interface {
FloatExpression
Column
From(subQuery SelectTable) ColumnFloat
SET(floatExp FloatExpression) ColumnAssigment
}
type floatColumnImpl struct {
floatInterfaceImpl
ColumnExpressionImpl
}
func (i *floatColumnImpl) From(subQuery SelectTable) ColumnFloat {
newFloatColumn := FloatColumn(i.name)
newFloatColumn.setTableName(i.tableName)
newFloatColumn.setSubQuery(subQuery)
return newFloatColumn
}
func (i *floatColumnImpl) SET(floatExp FloatExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: floatExp,
}
}
// FloatColumn creates named float column.
func FloatColumn(name string) ColumnFloat {
floatColumn := &floatColumnImpl{}
floatColumn.floatInterfaceImpl.parent = floatColumn
floatColumn.ColumnExpressionImpl = NewColumnImpl(name, "", floatColumn)
return floatColumn
}
//------------------------------------------------------//
// ColumnInteger is interface for SQL smallint, integer, bigint columns.
type ColumnInteger interface {
IntegerExpression
Column
From(subQuery SelectTable) ColumnInteger
SET(intExp IntegerExpression) ColumnAssigment
}
type integerColumnImpl struct {
integerInterfaceImpl
ColumnExpressionImpl
}
func (i *integerColumnImpl) From(subQuery SelectTable) ColumnInteger {
newIntColumn := IntegerColumn(i.name)
newIntColumn.setTableName(i.tableName)
newIntColumn.setSubQuery(subQuery)
return newIntColumn
}
func (i *integerColumnImpl) SET(intExp IntegerExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: intExp,
}
}
// IntegerColumn creates named integer column.
func IntegerColumn(name string) ColumnInteger {
integerColumn := &integerColumnImpl{}
integerColumn.integerInterfaceImpl.parent = integerColumn
integerColumn.ColumnExpressionImpl = NewColumnImpl(name, "", integerColumn)
return integerColumn
}
//------------------------------------------------------//
// ColumnString is interface for SQL text, character, character varying
// bytea, uuid columns and enums types.
type ColumnString interface {
StringExpression
Column
From(subQuery SelectTable) ColumnString
SET(stringExp StringExpression) ColumnAssigment
}
type stringColumnImpl struct {
stringInterfaceImpl
ColumnExpressionImpl
}
func (i *stringColumnImpl) From(subQuery SelectTable) ColumnString {
newStrColumn := StringColumn(i.name)
newStrColumn.setTableName(i.tableName)
newStrColumn.setSubQuery(subQuery)
return newStrColumn
}
func (i *stringColumnImpl) SET(stringExp StringExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: stringExp,
}
}
// StringColumn creates named string column.
func StringColumn(name string) ColumnString {
stringColumn := &stringColumnImpl{}
stringColumn.stringInterfaceImpl.parent = stringColumn
stringColumn.ColumnExpressionImpl = NewColumnImpl(name, "", stringColumn)
return stringColumn
}
//------------------------------------------------------//
// ColumnTime is interface for SQL time column.
type ColumnTime interface {
TimeExpression
Column
From(subQuery SelectTable) ColumnTime
SET(timeExp TimeExpression) ColumnAssigment
}
type timeColumnImpl struct {
timeInterfaceImpl
ColumnExpressionImpl
}
func (i *timeColumnImpl) From(subQuery SelectTable) ColumnTime {
newTimeColumn := TimeColumn(i.name)
newTimeColumn.setTableName(i.tableName)
newTimeColumn.setSubQuery(subQuery)
return newTimeColumn
}
func (i *timeColumnImpl) SET(timeExp TimeExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: timeExp,
}
}
// TimeColumn creates named time column
func TimeColumn(name string) ColumnTime {
timeColumn := &timeColumnImpl{}
timeColumn.timeInterfaceImpl.parent = timeColumn
timeColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timeColumn)
return timeColumn
}
//------------------------------------------------------//
// ColumnTimez is interface of SQL time with time zone columns.
type ColumnTimez interface {
TimezExpression
Column
From(subQuery SelectTable) ColumnTimez
SET(timeExp TimezExpression) ColumnAssigment
}
type timezColumnImpl struct {
timezInterfaceImpl
ColumnExpressionImpl
}
func (i *timezColumnImpl) From(subQuery SelectTable) ColumnTimez {
newTimezColumn := TimezColumn(i.name)
newTimezColumn.setTableName(i.tableName)
newTimezColumn.setSubQuery(subQuery)
return newTimezColumn
}
func (i *timezColumnImpl) SET(timezExp TimezExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: timezExp,
}
}
// TimezColumn creates named time with time zone column.
func TimezColumn(name string) ColumnTimez {
timezColumn := &timezColumnImpl{}
timezColumn.timezInterfaceImpl.parent = timezColumn
timezColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timezColumn)
return timezColumn
}
//------------------------------------------------------//
// ColumnTimestamp is interface of SQL timestamp columns.
type ColumnTimestamp interface {
TimestampExpression
Column
From(subQuery SelectTable) ColumnTimestamp
SET(timestampExp TimestampExpression) ColumnAssigment
}
type timestampColumnImpl struct {
timestampInterfaceImpl
ColumnExpressionImpl
}
func (i *timestampColumnImpl) From(subQuery SelectTable) ColumnTimestamp {
newTimestampColumn := TimestampColumn(i.name)
newTimestampColumn.setTableName(i.tableName)
newTimestampColumn.setSubQuery(subQuery)
return newTimestampColumn
}
func (i *timestampColumnImpl) SET(timestampExp TimestampExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: timestampExp,
}
}
// TimestampColumn creates named timestamp column
func TimestampColumn(name string) ColumnTimestamp {
timestampColumn := &timestampColumnImpl{}
timestampColumn.timestampInterfaceImpl.parent = timestampColumn
timestampColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timestampColumn)
return timestampColumn
}
//------------------------------------------------------//
// ColumnTimestampz is interface of SQL timestamp with timezone columns.
type ColumnTimestampz interface {
TimestampzExpression
Column
From(subQuery SelectTable) ColumnTimestampz
SET(timestampzExp TimestampzExpression) ColumnAssigment
}
type timestampzColumnImpl struct {
timestampzInterfaceImpl
ColumnExpressionImpl
}
func (i *timestampzColumnImpl) From(subQuery SelectTable) ColumnTimestampz {
newTimestampzColumn := TimestampzColumn(i.name)
newTimestampzColumn.setTableName(i.tableName)
newTimestampzColumn.setSubQuery(subQuery)
return newTimestampzColumn
}
func (i *timestampzColumnImpl) SET(timestampzExp TimestampzExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: timestampzExp,
}
}
// TimestampzColumn creates named timestamp with time zone column.
func TimestampzColumn(name string) ColumnTimestampz {
timestampzColumn := &timestampzColumnImpl{}
timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn
timestampzColumn.ColumnExpressionImpl = NewColumnImpl(name, "", timestampzColumn)
return timestampzColumn
}
//------------------------------------------------------//
// ColumnDate is interface of SQL date columns.
type ColumnDate interface {
DateExpression
Column
From(subQuery SelectTable) ColumnDate
SET(dateExp DateExpression) ColumnAssigment
}
type dateColumnImpl struct {
dateInterfaceImpl
ColumnExpressionImpl
}
func (i *dateColumnImpl) From(subQuery SelectTable) ColumnDate {
newDateColumn := DateColumn(i.name)
newDateColumn.setTableName(i.tableName)
newDateColumn.setSubQuery(subQuery)
return newDateColumn
}
func (i *dateColumnImpl) SET(dateExp DateExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: dateExp,
}
}
// DateColumn creates named date column.
func DateColumn(name string) ColumnDate {
dateColumn := &dateColumnImpl{}
dateColumn.dateInterfaceImpl.parent = dateColumn
dateColumn.ColumnExpressionImpl = NewColumnImpl(name, "", dateColumn)
return dateColumn
}
//------------------------------------------------------//
// ColumnRange is interface for range columns which can be int range, string range
// timestamp range or date range.
type ColumnRange[T Expression] interface {
Range[T]
Column
From(subQuery SelectTable) ColumnRange[T]
SET(rangeExp Range[T]) ColumnAssigment
}
type rangeColumnImpl[T Expression] struct {
rangeInterfaceImpl[T]
ColumnExpressionImpl
}
func (i *rangeColumnImpl[T]) From(subQuery SelectTable) ColumnRange[T] {
newRangeColumn := RangeColumn[T](i.name)
newRangeColumn.setTableName(i.tableName)
newRangeColumn.setSubQuery(subQuery)
return newRangeColumn
}
func (i *rangeColumnImpl[T]) SET(rangeExp Range[T]) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: rangeExp,
}
}
// RangeColumn creates named range column.
func RangeColumn[T Expression](name string) ColumnRange[T] {
rangeColumn := &rangeColumnImpl[T]{}
rangeColumn.rangeInterfaceImpl.parent = rangeColumn
rangeColumn.ColumnExpressionImpl = NewColumnImpl(name, "", rangeColumn)
return rangeColumn
}

View File

@@ -0,0 +1,93 @@
package jet
// DateExpression is interface for date types
type DateExpression interface {
Expression
EQ(rhs DateExpression) BoolExpression
NOT_EQ(rhs DateExpression) BoolExpression
IS_DISTINCT_FROM(rhs DateExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs DateExpression) BoolExpression
LT(rhs DateExpression) BoolExpression
LT_EQ(rhs DateExpression) BoolExpression
GT(rhs DateExpression) BoolExpression
GT_EQ(rhs DateExpression) BoolExpression
BETWEEN(min, max DateExpression) BoolExpression
NOT_BETWEEN(min, max DateExpression) BoolExpression
ADD(rhs Interval) TimestampExpression
SUB(rhs Interval) TimestampExpression
}
type dateInterfaceImpl struct {
parent DateExpression
}
func (d *dateInterfaceImpl) EQ(rhs DateExpression) BoolExpression {
return Eq(d.parent, rhs)
}
func (d *dateInterfaceImpl) NOT_EQ(rhs DateExpression) BoolExpression {
return NotEq(d.parent, rhs)
}
func (d *dateInterfaceImpl) IS_DISTINCT_FROM(rhs DateExpression) BoolExpression {
return IsDistinctFrom(d.parent, rhs)
}
func (d *dateInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs DateExpression) BoolExpression {
return IsNotDistinctFrom(d.parent, rhs)
}
func (d *dateInterfaceImpl) LT(rhs DateExpression) BoolExpression {
return Lt(d.parent, rhs)
}
func (d *dateInterfaceImpl) LT_EQ(rhs DateExpression) BoolExpression {
return LtEq(d.parent, rhs)
}
func (d *dateInterfaceImpl) GT(rhs DateExpression) BoolExpression {
return Gt(d.parent, rhs)
}
func (d *dateInterfaceImpl) GT_EQ(rhs DateExpression) BoolExpression {
return GtEq(d.parent, rhs)
}
func (d *dateInterfaceImpl) BETWEEN(min, max DateExpression) BoolExpression {
return NewBetweenOperatorExpression(d.parent, min, max, false)
}
func (d *dateInterfaceImpl) NOT_BETWEEN(min, max DateExpression) BoolExpression {
return NewBetweenOperatorExpression(d.parent, min, max, true)
}
func (d *dateInterfaceImpl) ADD(rhs Interval) TimestampExpression {
return TimestampExp(Add(d.parent, rhs))
}
func (d *dateInterfaceImpl) SUB(rhs Interval) TimestampExpression {
return TimestampExp(Sub(d.parent, rhs))
}
//---------------------------------------------------//
type dateExpressionWrapper struct {
dateInterfaceImpl
Expression
}
func newDateExpressionWrap(expression Expression) DateExpression {
dateExpressionWrap := dateExpressionWrapper{Expression: expression}
dateExpressionWrap.dateInterfaceImpl.parent = &dateExpressionWrap
return &dateExpressionWrap
}
// DateExp is date expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as date expression.
// Does not add sql cast to generated sql builder output.
func DateExp(expression Expression) DateExpression {
return newDateExpressionWrap(expression)
}

125
vendor/github.com/go-jet/jet/v2/internal/jet/dialect.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
package jet
import "strings"
// Dialect interface
type Dialect interface {
Name() string
PackageName() string
OperatorSerializeOverride(operator string) SerializeOverride
FunctionSerializeOverride(function string) SerializeOverride
AliasQuoteChar() byte
IdentifierQuoteChar() byte
ArgumentPlaceholder() QueryPlaceholderFunc
IsReservedWord(name string) bool
SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
ValuesDefaultColumnName(index int) string
}
// SerializerFunc func
type SerializerFunc func(statement StatementType, out *SQLBuilder, options ...SerializeOption)
// SerializeOverride func
type SerializeOverride func(expressions ...Serializer) SerializerFunc
// QueryPlaceholderFunc func
type QueryPlaceholderFunc func(ord int) string
// DialectParams struct
type DialectParams struct {
Name string
PackageName string
OperatorSerializeOverrides map[string]SerializeOverride
FunctionSerializeOverrides map[string]SerializeOverride
AliasQuoteChar byte
IdentifierQuoteChar byte
ArgumentPlaceholder QueryPlaceholderFunc
ReservedWords []string
SerializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
ValuesDefaultColumnName func(index int) string
}
// NewDialect creates new dialect with params
func NewDialect(params DialectParams) Dialect {
return &dialectImpl{
name: params.Name,
packageName: params.PackageName,
operatorSerializeOverrides: params.OperatorSerializeOverrides,
functionSerializeOverrides: params.FunctionSerializeOverrides,
aliasQuoteChar: params.AliasQuoteChar,
identifierQuoteChar: params.IdentifierQuoteChar,
argumentPlaceholder: params.ArgumentPlaceholder,
reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords),
serializeOrderBy: params.SerializeOrderBy,
valuesDefaultColumnName: params.ValuesDefaultColumnName,
}
}
type dialectImpl struct {
name string
packageName string
operatorSerializeOverrides map[string]SerializeOverride
functionSerializeOverrides map[string]SerializeOverride
aliasQuoteChar byte
identifierQuoteChar byte
argumentPlaceholder QueryPlaceholderFunc
reservedWords map[string]bool
serializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
valuesDefaultColumnName func(index int) string
}
func (d *dialectImpl) Name() string {
return d.name
}
func (d *dialectImpl) PackageName() string {
return d.packageName
}
func (d *dialectImpl) OperatorSerializeOverride(operator string) SerializeOverride {
if d.operatorSerializeOverrides == nil {
return nil
}
return d.operatorSerializeOverrides[operator]
}
func (d *dialectImpl) FunctionSerializeOverride(function string) SerializeOverride {
if d.functionSerializeOverrides == nil {
return nil
}
return d.functionSerializeOverrides[function]
}
func (d *dialectImpl) AliasQuoteChar() byte {
return d.aliasQuoteChar
}
func (d *dialectImpl) IdentifierQuoteChar() byte {
return d.identifierQuoteChar
}
func (d *dialectImpl) ArgumentPlaceholder() QueryPlaceholderFunc {
return d.argumentPlaceholder
}
func (d *dialectImpl) IsReservedWord(name string) bool {
_, isReservedWord := d.reservedWords[strings.ToLower(name)]
return isReservedWord
}
func (d *dialectImpl) SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc {
return d.serializeOrderBy
}
func (d *dialectImpl) ValuesDefaultColumnName(index int) string {
return d.valuesDefaultColumnName(index)
}
func arrayOfStringsToMapOfStrings(arr []string) map[string]bool {
ret := map[string]bool{}
for _, elem := range arr {
ret[strings.ToLower(elem)] = true
}
return ret
}

View File

@@ -0,0 +1,22 @@
package jet
type enumValue struct {
ExpressionInterfaceImpl
stringInterfaceImpl
name string
}
// NewEnumValue creates new named enum value
func NewEnumValue(name string) StringExpression {
enumValue := &enumValue{name: name}
enumValue.ExpressionInterfaceImpl.Parent = enumValue
enumValue.stringInterfaceImpl.parent = enumValue
return enumValue
}
func (e enumValue) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.insertConstantArgument(e.name)
}

View File

@@ -0,0 +1,321 @@
package jet
import "fmt"
// Expression is common interface for all expressions.
// Can be Bool, Int, Float, String, Date, Time, Timez, Timestamp or Timestampz expressions.
type Expression interface {
Serializer
Projection
GroupByClause
OrderByClause
// IS_NULL tests expression whether it is a NULL value.
IS_NULL() BoolExpression
// IS_NOT_NULL tests expression whether it is a non-NULL value.
IS_NOT_NULL() BoolExpression
// IN checks if this expressions matches any in expressions list
IN(expressions ...Expression) BoolExpression
// NOT_IN checks if this expressions is different of all expressions in expressions list
NOT_IN(expressions ...Expression) BoolExpression
// AS the temporary alias name to assign to the expression
AS(alias string) Projection
// ASC expression will be used to sort query result in ascending order
ASC() OrderByClause
// DESC expression will be used to sort query result in descending order
DESC() OrderByClause
}
// ExpressionInterfaceImpl implements Expression interface methods
type ExpressionInterfaceImpl struct {
Parent Expression
}
func (e *ExpressionInterfaceImpl) fromImpl(subQuery SelectTable) Projection {
panic(fmt.Sprintf("jet: can't export unaliased expression subQuery: %s, expression: %s",
subQuery.Alias(), serializeToDefaultDebugString(e.Parent)))
}
// IS_NULL tests expression whether it is a NULL value.
func (e *ExpressionInterfaceImpl) IS_NULL() BoolExpression {
return newPostfixBoolOperatorExpression(e.Parent, "IS NULL")
}
// IS_NOT_NULL tests expression whether it is a non-NULL value.
func (e *ExpressionInterfaceImpl) IS_NOT_NULL() BoolExpression {
return newPostfixBoolOperatorExpression(e.Parent, "IS NOT NULL")
}
// IN checks if this expressions matches any in expressions list
func (e *ExpressionInterfaceImpl) IN(expressions ...Expression) BoolExpression {
return newBinaryBoolOperatorExpression(e.Parent, wrap(expressions...), "IN")
}
// NOT_IN checks if this expressions is different of all expressions in expressions list
func (e *ExpressionInterfaceImpl) NOT_IN(expressions ...Expression) BoolExpression {
return newBinaryBoolOperatorExpression(e.Parent, wrap(expressions...), "NOT IN")
}
// AS the temporary alias name to assign to the expression
func (e *ExpressionInterfaceImpl) AS(alias string) Projection {
return newAlias(e.Parent, alias)
}
// ASC expression will be used to sort a query result in ascending order
func (e *ExpressionInterfaceImpl) ASC() OrderByClause {
return newOrderByAscending(e.Parent, true)
}
// DESC expression will be used to sort a query result in descending order
func (e *ExpressionInterfaceImpl) DESC() OrderByClause {
return newOrderByAscending(e.Parent, false)
}
// NULLS_FIRST specifies sort where null values appear before all non-null values
func (e *ExpressionInterfaceImpl) NULLS_FIRST() OrderByClause {
return newOrderByNullsFirst(e.Parent, true)
}
// NULLS_LAST specifies sort where null values appear after all non-null values
func (e *ExpressionInterfaceImpl) NULLS_LAST() OrderByClause {
return newOrderByNullsFirst(e.Parent, false)
}
func (e *ExpressionInterfaceImpl) serializeForGroupBy(statement StatementType, out *SQLBuilder) {
e.Parent.serialize(statement, out, NoWrap)
}
func (e *ExpressionInterfaceImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
e.Parent.serialize(statement, out, NoWrap)
}
func (e *ExpressionInterfaceImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
e.Parent.serialize(statement, out, NoWrap)
}
// Representation of binary operations (e.g. comparisons, arithmetic)
type binaryOperatorExpression struct {
ExpressionInterfaceImpl
lhs, rhs Serializer
additionalParam Serializer
operator string
}
// NewBinaryOperatorExpression creates new binaryOperatorExpression
func NewBinaryOperatorExpression(lhs, rhs Serializer, operator string, additionalParam ...Expression) Expression {
binaryExpression := &binaryOperatorExpression{
lhs: lhs,
rhs: rhs,
operator: operator,
}
if len(additionalParam) > 0 {
binaryExpression.additionalParam = additionalParam[0]
}
binaryExpression.ExpressionInterfaceImpl.Parent = binaryExpression
return complexExpr(binaryExpression)
}
func (c *binaryOperatorExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if serializeOverride := out.Dialect.OperatorSerializeOverride(c.operator); serializeOverride != nil {
serializeOverrideFunc := serializeOverride(c.lhs, c.rhs, c.additionalParam)
serializeOverrideFunc(statement, out, FallTrough(options)...)
} else {
c.lhs.serialize(statement, out, FallTrough(options)...)
out.WriteString(c.operator)
c.rhs.serialize(statement, out, FallTrough(options)...)
}
}
type expressionListOperator struct {
ExpressionInterfaceImpl
operator string
expressions []Expression
}
func newExpressionListOperator(operator string, expressions ...Expression) *expressionListOperator {
ret := &expressionListOperator{
operator: operator,
expressions: expressions,
}
ret.ExpressionInterfaceImpl.Parent = ret
return ret
}
func newBoolExpressionListOperator(operator string, expressions ...BoolExpression) BoolExpression {
return BoolExp(newExpressionListOperator(operator, BoolExpressionListToExpressionList(expressions)...))
}
func (elo *expressionListOperator) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if len(elo.expressions) == 0 {
panic("jet: syntax error, expression list empty")
}
shouldWrap := len(elo.expressions) > 1
if shouldWrap {
out.WriteByte('(')
out.IncreaseIdent(tabSize)
out.NewLine()
}
for i, expression := range elo.expressions {
if i == 1 {
out.IncreaseIdent(tabSize)
}
if i > 0 {
out.NewLine()
out.WriteString(elo.operator)
}
out.IncreaseIdent(len(elo.operator) + 1)
expression.serialize(statement, out, FallTrough(options)...)
out.DecreaseIdent(len(elo.operator) + 1)
}
if len(elo.expressions) > 1 {
out.DecreaseIdent(tabSize)
}
if shouldWrap {
out.DecreaseIdent(tabSize)
out.NewLine()
out.WriteByte(')')
}
}
// A prefix operator Expression
type prefixExpression struct {
ExpressionInterfaceImpl
expression Expression
operator string
}
func newPrefixOperatorExpression(expression Expression, operator string) Expression {
prefixExpression := &prefixExpression{
expression: expression,
operator: operator,
}
prefixExpression.ExpressionInterfaceImpl.Parent = prefixExpression
return complexExpr(prefixExpression)
}
func (p *prefixExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString(p.operator)
p.expression.serialize(statement, out, FallTrough(options)...)
}
// A postfix operator Expression
type postfixOpExpression struct {
ExpressionInterfaceImpl
expression Expression
operator string
}
func newPostfixOperatorExpression(expression Expression, operator string) *postfixOpExpression {
postfixOpExpression := &postfixOpExpression{
expression: expression,
operator: operator,
}
postfixOpExpression.ExpressionInterfaceImpl.Parent = postfixOpExpression
return postfixOpExpression
}
func (p *postfixOpExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
p.expression.serialize(statement, out, FallTrough(options)...)
out.WriteString(p.operator)
}
type betweenOperatorExpression struct {
ExpressionInterfaceImpl
expression Expression
notBetween bool
min Expression
max Expression
}
// NewBetweenOperatorExpression creates new BETWEEN operator expression
func NewBetweenOperatorExpression(expression, min, max Expression, notBetween bool) BoolExpression {
newBetweenOperator := &betweenOperatorExpression{
expression: expression,
notBetween: notBetween,
min: min,
max: max,
}
newBetweenOperator.ExpressionInterfaceImpl.Parent = newBetweenOperator
return BoolExp(complexExpr(newBetweenOperator))
}
func (p *betweenOperatorExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
p.expression.serialize(statement, out, FallTrough(options)...)
if p.notBetween {
out.WriteString("NOT")
}
out.WriteString("BETWEEN")
p.min.serialize(statement, out, FallTrough(options)...)
out.WriteString("AND")
p.max.serialize(statement, out, FallTrough(options)...)
}
type customExpression struct {
ExpressionInterfaceImpl
parts []Serializer
}
func CustomExpression(parts ...Serializer) Expression {
ret := customExpression{
parts: parts,
}
ret.ExpressionInterfaceImpl.Parent = &ret
return &ret
}
func (c *customExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
for _, expression := range c.parts {
expression.serialize(statement, out, options...)
}
}
type complexExpression struct {
ExpressionInterfaceImpl
expressions Expression
}
func complexExpr(expression Expression) Expression {
complexExpression := &complexExpression{expressions: expression}
complexExpression.ExpressionInterfaceImpl.Parent = complexExpression
return complexExpression
}
func (s *complexExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if !contains(options, NoWrap) {
out.WriteString("(")
}
s.expressions.serialize(statement, out, options...) // FallTrough here because complexExpression is just a wrapper
if !contains(options, NoWrap) {
out.WriteString(")")
}
}
func wrap(expressions ...Expression) Expression {
return NewFunc("", expressions, nil)
}

View File

@@ -0,0 +1,115 @@
package jet
// FloatExpression is interface for SQL float columns
type FloatExpression interface {
Expression
numericExpression
EQ(rhs FloatExpression) BoolExpression
NOT_EQ(rhs FloatExpression) BoolExpression
IS_DISTINCT_FROM(rhs FloatExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs FloatExpression) BoolExpression
LT(rhs FloatExpression) BoolExpression
LT_EQ(rhs FloatExpression) BoolExpression
GT(rhs FloatExpression) BoolExpression
GT_EQ(rhs FloatExpression) BoolExpression
BETWEEN(min, max FloatExpression) BoolExpression
NOT_BETWEEN(min, max FloatExpression) BoolExpression
ADD(rhs NumericExpression) FloatExpression
SUB(rhs NumericExpression) FloatExpression
MUL(rhs NumericExpression) FloatExpression
DIV(rhs NumericExpression) FloatExpression
MOD(rhs NumericExpression) FloatExpression
POW(rhs NumericExpression) FloatExpression
}
type floatInterfaceImpl struct {
numericExpressionImpl
parent FloatExpression
}
func (n *floatInterfaceImpl) EQ(rhs FloatExpression) BoolExpression {
return Eq(n.parent, rhs)
}
func (n *floatInterfaceImpl) NOT_EQ(rhs FloatExpression) BoolExpression {
return NotEq(n.parent, rhs)
}
func (n *floatInterfaceImpl) IS_DISTINCT_FROM(rhs FloatExpression) BoolExpression {
return IsDistinctFrom(n.parent, rhs)
}
func (n *floatInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs FloatExpression) BoolExpression {
return IsNotDistinctFrom(n.parent, rhs)
}
func (n *floatInterfaceImpl) GT(rhs FloatExpression) BoolExpression {
return Gt(n.parent, rhs)
}
func (n *floatInterfaceImpl) GT_EQ(rhs FloatExpression) BoolExpression {
return GtEq(n.parent, rhs)
}
func (n *floatInterfaceImpl) LT(rhs FloatExpression) BoolExpression {
return Lt(n.parent, rhs)
}
func (n *floatInterfaceImpl) LT_EQ(rhs FloatExpression) BoolExpression {
return LtEq(n.parent, rhs)
}
func (n *floatInterfaceImpl) BETWEEN(min, max FloatExpression) BoolExpression {
return NewBetweenOperatorExpression(n.parent, min, max, false)
}
func (n *floatInterfaceImpl) NOT_BETWEEN(min, max FloatExpression) BoolExpression {
return NewBetweenOperatorExpression(n.parent, min, max, true)
}
func (n *floatInterfaceImpl) ADD(rhs NumericExpression) FloatExpression {
return FloatExp(Add(n.parent, rhs))
}
func (n *floatInterfaceImpl) SUB(rhs NumericExpression) FloatExpression {
return FloatExp(Sub(n.parent, rhs))
}
func (n *floatInterfaceImpl) MUL(rhs NumericExpression) FloatExpression {
return FloatExp(Mul(n.parent, rhs))
}
func (n *floatInterfaceImpl) DIV(rhs NumericExpression) FloatExpression {
return FloatExp(Div(n.parent, rhs))
}
func (n *floatInterfaceImpl) MOD(rhs NumericExpression) FloatExpression {
return FloatExp(Mod(n.parent, rhs))
}
func (n *floatInterfaceImpl) POW(rhs NumericExpression) FloatExpression {
return POW(n.parent, rhs)
}
//---------------------------------------------------//
type floatExpressionWrapper struct {
floatInterfaceImpl
Expression
}
func newFloatExpressionWrap(expression Expression) FloatExpression {
floatExpressionWrap := floatExpressionWrapper{Expression: expression}
floatExpressionWrap.floatInterfaceImpl.parent = &floatExpressionWrap
return &floatExpressionWrap
}
// FloatExp is date expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as float expression.
// Does not add sql cast to generated sql builder output.
func FloatExp(expression Expression) FloatExpression {
return newFloatExpressionWrap(expression)
}

View File

@@ -0,0 +1,926 @@
package jet
// AND function adds AND operator between expressions. This function can be used, instead of method AND,
// to have a better inlining of a complex condition in the Go code and in the generated SQL.
func AND(expressions ...BoolExpression) BoolExpression {
return newBoolExpressionListOperator("AND", expressions...)
}
// OR function adds OR operator between expressions. This function can be used, instead of method OR,
// to have a better inlining of a complex condition in the Go code and in the generated SQL.
func OR(expressions ...BoolExpression) BoolExpression {
return newBoolExpressionListOperator("OR", expressions...)
}
// ------------------ Mathematical functions ---------------//
// ABSf calculates absolute value from float expression
func ABSf(floatExpression FloatExpression) FloatExpression {
return NewFloatFunc("ABS", floatExpression)
}
// ABSi calculates absolute value from int expression
func ABSi(integerExpression IntegerExpression) IntegerExpression {
return newIntegerFunc("ABS", integerExpression)
}
// POW calculates power of base with exponent
func POW(base, exponent NumericExpression) FloatExpression {
return NewFloatFunc("POW", base, exponent)
}
// POWER calculates power of base with exponent
func POWER(base, exponent NumericExpression) FloatExpression {
return NewFloatFunc("POWER", base, exponent)
}
// SQRT calculates square root of numeric expression
func SQRT(numericExpression NumericExpression) FloatExpression {
return NewFloatFunc("SQRT", numericExpression)
}
// CBRT calculates cube root of numeric expression
func CBRT(numericExpression NumericExpression) FloatExpression {
return NewFloatFunc("CBRT", numericExpression)
}
// CEIL calculates ceil of float expression
func CEIL(floatExpression FloatExpression) FloatExpression {
return NewFloatFunc("CEIL", floatExpression)
}
// FLOOR calculates floor of float expression
func FLOOR(floatExpression FloatExpression) FloatExpression {
return NewFloatFunc("FLOOR", floatExpression)
}
// ROUND calculates round of a float expressions with optional precision
func ROUND(floatExpression FloatExpression, precision ...IntegerExpression) FloatExpression {
if len(precision) > 0 {
return NewFloatFunc("ROUND", floatExpression, precision[0])
}
return NewFloatFunc("ROUND", floatExpression)
}
// SIGN returns sign of float expression
func SIGN(floatExpression FloatExpression) FloatExpression {
return NewFloatFunc("SIGN", floatExpression)
}
// TRUNC calculates trunc of float expression with optional precision
func TRUNC(floatExpression FloatExpression, precision ...IntegerExpression) FloatExpression {
if len(precision) > 0 {
return NewFloatFunc("TRUNC", floatExpression, precision[0])
}
return NewFloatFunc("TRUNC", floatExpression)
}
// LN calculates natural algorithm of float expression
func LN(floatExpression FloatExpression) FloatExpression {
return NewFloatFunc("LN", floatExpression)
}
// LOG calculates logarithm of float expression
func LOG(floatExpression FloatExpression) FloatExpression {
return NewFloatFunc("LOG", floatExpression)
}
// ----------------- Aggregate functions -------------------//
// AVG is aggregate function used to calculate avg value from numeric expression
func AVG(numericExpression Expression) floatWindowExpression {
return NewFloatWindowFunc("AVG", numericExpression)
}
// BIT_AND is aggregate function used to calculates the bitwise AND of all non-null input values, or null if none.
func BIT_AND(integerExpression IntegerExpression) integerWindowExpression {
return newIntegerWindowFunc("BIT_AND", integerExpression)
}
// BIT_OR is aggregate function used to calculates the bitwise OR of all non-null input values, or null if none.
func BIT_OR(integerExpression IntegerExpression) integerWindowExpression {
return newIntegerWindowFunc("BIT_OR", integerExpression)
}
// BOOL_AND is aggregate function. Returns true if all input values are true, otherwise false
func BOOL_AND(boolExpression BoolExpression) boolWindowExpression {
return newBoolWindowFunc("BOOL_AND", boolExpression)
}
// BOOL_OR is aggregate function. Returns true if at least one input value is true, otherwise false
func BOOL_OR(boolExpression BoolExpression) boolWindowExpression {
return newBoolWindowFunc("BOOL_OR", boolExpression)
}
// COUNT is aggregate function. Returns number of input rows for which the value of expression is not null.
func COUNT(expression Expression) integerWindowExpression {
return newIntegerWindowFunc("COUNT", expression)
}
// EVERY is aggregate function. Returns true if all input values are true, otherwise false
func EVERY(boolExpression BoolExpression) boolWindowExpression {
return newBoolWindowFunc("EVERY", boolExpression)
}
// MAX is aggregate function. Returns minimum value of expression across all input values.
func MAX(expression Expression) Expression {
return newWindowFunc("MAX", expression)
}
// MAXf is aggregate function. Returns maximum value of float expression across all input values
func MAXf(floatExpression FloatExpression) floatWindowExpression {
return NewFloatWindowFunc("MAX", floatExpression)
}
// MAXi is aggregate function. Returns maximum value of int expression across all input values
func MAXi(integerExpression IntegerExpression) integerWindowExpression {
return newIntegerWindowFunc("MAX", integerExpression)
}
// MIN is aggregate function. Returns minimum value of expression across all input values.
func MIN(expression Expression) Expression {
return newWindowFunc("MIN", expression)
}
// MINf is aggregate function. Returns minimum value of float expression across all input values
func MINf(floatExpression FloatExpression) floatWindowExpression {
return NewFloatWindowFunc("MIN", floatExpression)
}
// MINi is aggregate function. Returns minimum value of int expression across all input values
func MINi(integerExpression IntegerExpression) integerWindowExpression {
return newIntegerWindowFunc("MIN", integerExpression)
}
// SUM is aggregate function. Returns sum of all expressions
func SUM(expression Expression) Expression {
return newWindowFunc("SUM", expression)
}
// SUMf is aggregate function. Returns sum of expression across all float expressions
func SUMf(floatExpression FloatExpression) floatWindowExpression {
return NewFloatWindowFunc("SUM", floatExpression)
}
// SUMi is aggregate function. Returns sum of expression across all integer expression.
func SUMi(integerExpression IntegerExpression) integerWindowExpression {
return newIntegerWindowFunc("SUM", integerExpression)
}
// ----------------- Window functions -------------------//
// ROW_NUMBER returns number of the current row within its partition, counting from 1
func ROW_NUMBER() integerWindowExpression {
return newIntegerWindowFunc("ROW_NUMBER")
}
// RANK of the current row with gaps; same as row_number of its first peer
func RANK() integerWindowExpression {
return newIntegerWindowFunc("RANK")
}
// DENSE_RANK returns rank of the current row without gaps; this function counts peer groups
func DENSE_RANK() integerWindowExpression {
return newIntegerWindowFunc("DENSE_RANK")
}
// PERCENT_RANK calculates relative rank of the current row: (rank - 1) / (total partition rows - 1)
func PERCENT_RANK() floatWindowExpression {
return NewFloatWindowFunc("PERCENT_RANK")
}
// CUME_DIST calculates cumulative distribution: (number of partition rows preceding or peer with current row) / total partition rows
func CUME_DIST() floatWindowExpression {
return NewFloatWindowFunc("CUME_DIST")
}
// NTILE returns integer ranging from 1 to the argument value, dividing the partition as equally as possible
func NTILE(numOfBuckets int64) integerWindowExpression {
return newIntegerWindowFunc("NTILE", FixedLiteral(numOfBuckets))
}
// LAG returns value evaluated at the row that is offset rows before the current row within the partition;
// if there is no such row, instead return default (which must be of the same type as value).
// Both offset and default are evaluated with respect to the current row.
// If omitted, offset defaults to 1 and default to null
func LAG(expr Expression, offsetAndDefault ...interface{}) windowExpression {
return leadLagImpl("LAG", expr, offsetAndDefault...)
}
// LEAD returns value evaluated at the row that is offset rows after the current row within the partition;
// if there is no such row, instead return default (which must be of the same type as value).
// Both offset and default are evaluated with respect to the current row.
// If omitted, offset defaults to 1 and default to null
func LEAD(expr Expression, offsetAndDefault ...interface{}) windowExpression {
return leadLagImpl("LEAD", expr, offsetAndDefault...)
}
// FIRST_VALUE returns value evaluated at the row that is the first row of the window frame
func FIRST_VALUE(value Expression) windowExpression {
return newWindowFunc("FIRST_VALUE", value)
}
// LAST_VALUE returns value evaluated at the row that is the last row of the window frame
func LAST_VALUE(value Expression) windowExpression {
return newWindowFunc("LAST_VALUE", value)
}
// NTH_VALUE returns value evaluated at the row that is the nth row of the window frame (counting from 1); null if no such row
func NTH_VALUE(value Expression, nth int64) windowExpression {
return newWindowFunc("NTH_VALUE", value, FixedLiteral(nth))
}
func leadLagImpl(name string, expr Expression, offsetAndDefault ...interface{}) windowExpression {
params := []Expression{expr}
if len(offsetAndDefault) >= 2 {
offset, ok := offsetAndDefault[0].(int)
if !ok {
panic("jet: LAG offset should be an integer")
}
var defaultValue Expression
defaultValue, ok = offsetAndDefault[1].(Expression)
if !ok {
defaultValue = literal(offsetAndDefault[1])
}
params = append(params, FixedLiteral(offset), defaultValue)
}
return newWindowFunc(name, params...)
}
//------------ String functions ------------------//
// BIT_LENGTH returns number of bits in string expression
func BIT_LENGTH(stringExpression StringExpression) IntegerExpression {
return newIntegerFunc("BIT_LENGTH", stringExpression)
}
// CHAR_LENGTH returns number of characters in string expression
func CHAR_LENGTH(stringExpression StringExpression) IntegerExpression {
return newIntegerFunc("CHAR_LENGTH", stringExpression)
}
// OCTET_LENGTH returns number of bytes in string expression
func OCTET_LENGTH(stringExpression StringExpression) IntegerExpression {
return newIntegerFunc("OCTET_LENGTH", stringExpression)
}
// LOWER returns string expression in lower case
func LOWER(stringExpression StringExpression) StringExpression {
return NewStringFunc("LOWER", stringExpression)
}
// UPPER returns string expression in upper case
func UPPER(stringExpression StringExpression) StringExpression {
return NewStringFunc("UPPER", stringExpression)
}
// BTRIM removes the longest string consisting only of characters
// in characters (a space by default) from the start and end of string
func BTRIM(stringExpression StringExpression, trimChars ...StringExpression) StringExpression {
if len(trimChars) > 0 {
return NewStringFunc("BTRIM", stringExpression, trimChars[0])
}
return NewStringFunc("BTRIM", stringExpression)
}
// LTRIM removes the longest string containing only characters
// from characters (a space by default) from the start of string
func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression {
if len(trimChars) > 0 {
return NewStringFunc("LTRIM", str, trimChars[0])
}
return NewStringFunc("LTRIM", str)
}
// RTRIM removes the longest string containing only characters
// from characters (a space by default) from the end of string
func RTRIM(str StringExpression, trimChars ...StringExpression) StringExpression {
if len(trimChars) > 0 {
return NewStringFunc("RTRIM", str, trimChars[0])
}
return NewStringFunc("RTRIM", str)
}
// CHR returns character with the given code.
func CHR(integerExpression IntegerExpression) StringExpression {
return NewStringFunc("CHR", integerExpression)
}
// CONCAT adds two or more expressions together
func CONCAT(expressions ...Expression) StringExpression {
return NewStringFunc("CONCAT", expressions...)
}
// CONCAT_WS adds two or more expressions together with a separator.
func CONCAT_WS(separator Expression, expressions ...Expression) StringExpression {
return NewStringFunc("CONCAT_WS", append([]Expression{separator}, expressions...)...)
}
// CONVERT converts string to dest_encoding. The original encoding is
// specified by src_encoding. The string must be valid in this encoding.
func CONVERT(str StringExpression, srcEncoding StringExpression, destEncoding StringExpression) StringExpression {
return NewStringFunc("CONVERT", str, srcEncoding, destEncoding)
}
// CONVERT_FROM converts string to the database encoding. The original
// encoding is specified by src_encoding. The string must be valid in this encoding.
func CONVERT_FROM(str StringExpression, srcEncoding StringExpression) StringExpression {
return NewStringFunc("CONVERT_FROM", str, srcEncoding)
}
// CONVERT_TO converts string to dest_encoding.
func CONVERT_TO(str StringExpression, toEncoding StringExpression) StringExpression {
return NewStringFunc("CONVERT_TO", str, toEncoding)
}
// ENCODE encodes binary data into a textual representation.
// Supported formats are: base64, hex, escape. escape converts zero bytes and
// high-bit-set bytes to octal sequences (\nnn) and doubles backslashes.
func ENCODE(data StringExpression, format StringExpression) StringExpression {
return NewStringFunc("ENCODE", data, format)
}
// DECODE decodes binary data from textual representation in string.
// Options for format are same as in encode.
func DECODE(data StringExpression, format StringExpression) StringExpression {
return NewStringFunc("DECODE", data, format)
}
// FORMAT formats a number to a format like "#,###,###.##", rounded to a specified number of decimal places, then it returns the result as a string.
func FORMAT(formatStr StringExpression, formatArgs ...Expression) StringExpression {
args := []Expression{formatStr}
args = append(args, formatArgs...)
return NewStringFunc("FORMAT", args...)
}
// INITCAP converts the first letter of each word to upper case
// and the rest to lower case. Words are sequences of alphanumeric
// characters separated by non-alphanumeric characters.
func INITCAP(str StringExpression) StringExpression {
return NewStringFunc("INITCAP", str)
}
// LEFT returns first n characters in the string.
// When n is negative, return all but last |n| characters.
func LEFT(str StringExpression, n IntegerExpression) StringExpression {
return NewStringFunc("LEFT", str, n)
}
// RIGHT returns last n characters in the string.
// When n is negative, return all but first |n| characters.
func RIGHT(str StringExpression, n IntegerExpression) StringExpression {
return NewStringFunc("RIGHT", str, n)
}
// LENGTH returns number of characters in string with a given encoding
func LENGTH(str StringExpression, encoding ...StringExpression) StringExpression {
if len(encoding) > 0 {
return NewStringFunc("LENGTH", str, encoding[0])
}
return NewStringFunc("LENGTH", str)
}
// LPAD fills up the string to length length by prepending the characters
// fill (a space by default). If the string is already longer than length
// then it is truncated (on the right).
func LPAD(str StringExpression, length IntegerExpression, text ...StringExpression) StringExpression {
if len(text) > 0 {
return NewStringFunc("LPAD", str, length, text[0])
}
return NewStringFunc("LPAD", str, length)
}
// RPAD fills up the string to length length by appending the characters
// fill (a space by default). If the string is already longer than length then it is truncated.
func RPAD(str StringExpression, length IntegerExpression, text ...StringExpression) StringExpression {
if len(text) > 0 {
return NewStringFunc("RPAD", str, length, text[0])
}
return NewStringFunc("RPAD", str, length)
}
// MD5 calculates the MD5 hash of string, returning the result in hexadecimal
func MD5(stringExpression StringExpression) StringExpression {
return NewStringFunc("MD5", stringExpression)
}
// REPEAT repeats string the specified number of times
func REPEAT(str StringExpression, n IntegerExpression) StringExpression {
return NewStringFunc("REPEAT", str, n)
}
// REPLACE replaces all occurrences in string of substring from with substring to
func REPLACE(text, from, to StringExpression) StringExpression {
return NewStringFunc("REPLACE", text, from, to)
}
// REVERSE returns reversed string.
func REVERSE(stringExpression StringExpression) StringExpression {
return NewStringFunc("REVERSE", stringExpression)
}
// STRPOS returns location of specified substring (same as position(substring in string),
// but note the reversed argument order)
func STRPOS(str, substring StringExpression) IntegerExpression {
return newIntegerFunc("STRPOS", str, substring)
}
// SUBSTR extracts substring
func SUBSTR(str StringExpression, from IntegerExpression, count ...IntegerExpression) StringExpression {
if len(count) > 0 {
return NewStringFunc("SUBSTR", str, from, count[0])
}
return NewStringFunc("SUBSTR", str, from)
}
// TO_ASCII convert string to ASCII from another encoding
func TO_ASCII(str StringExpression, encoding ...StringExpression) StringExpression {
if len(encoding) > 0 {
return NewStringFunc("TO_ASCII", str, encoding[0])
}
return NewStringFunc("TO_ASCII", str)
}
// TO_HEX converts number to its equivalent hexadecimal representation
func TO_HEX(number IntegerExpression) StringExpression {
return NewStringFunc("TO_HEX", number)
}
// REGEXP_LIKE Returns 1 if the string expr matches the regular expression specified by the pattern pat, 0 otherwise.
func REGEXP_LIKE(stringExp StringExpression, pattern StringExpression, matchType ...string) BoolExpression {
if len(matchType) > 0 {
return newBoolFunc("REGEXP_LIKE", stringExp, pattern, FixedLiteral(matchType[0]))
}
return newBoolFunc("REGEXP_LIKE", stringExp, pattern)
}
//----------Range Type Functions ----------------------//
// LOWER_BOUND returns range expressions lower bound. Returns null if range is empty or the requested bound is infinite.
func LOWER_BOUND[T Expression](rangeExpression Range[T]) T {
return rangeTypeCaster[T](rangeExpression, NewFunc("LOWER", []Expression{rangeExpression}, nil))
}
// UPPER_BOUND returns range expressions upper bound. Returns null if range is empty or the requested bound is infinite.
func UPPER_BOUND[T Expression](rangeExpression Range[T]) T {
return rangeTypeCaster[T](rangeExpression, NewFunc("UPPER", []Expression{rangeExpression}, nil))
}
func rangeTypeCaster[T Expression](rangeExpression Range[T], exp Expression) T {
var i Expression
switch rangeExpression.(type) {
case Range[Int4Expression], Range[Int8Expression]:
i = IntExp(exp)
case Range[NumericExpression]:
i = FloatExp(exp)
case Range[DateExpression]:
i = DateExp(exp)
case Range[TimestampExpression]:
i = TimestampExp(exp)
case Range[TimestampzExpression]:
i = TimestampzExp(exp)
}
return i.(T)
}
// IS_EMPTY returns true if range is empty
func IS_EMPTY[T Expression](rangeExpression Range[T]) BoolExpression {
return newBoolFunc("ISEMPTY", rangeExpression)
}
// LOWER_INC returns true if lower bound is inclusive. Returns false for empty range.
func LOWER_INC[T Expression](rangeExpression Range[T]) BoolExpression {
return newBoolFunc("LOWER_INC", rangeExpression)
}
// UPPER_INC returns true if upper bound is inclusive. Returns false for empty range.
func UPPER_INC[T Expression](rangeExpression Range[T]) BoolExpression {
return newBoolFunc("UPPER_INC", rangeExpression)
}
// LOWER_INF returns true if upper bound is infinite. Returns false for empty range.
func LOWER_INF[T Expression](rangeExpression Range[T]) BoolExpression {
return newBoolFunc("LOWER_INF", rangeExpression)
}
// UPPER_INF returns true if lower bound is infinite. Returns false for empty range.
func UPPER_INF[T Expression](rangeExpression Range[T]) BoolExpression {
return newBoolFunc("UPPER_INF", rangeExpression)
}
//----------Data Type Formatting Functions ----------------------//
// TO_CHAR converts expression to string with format
func TO_CHAR(expression Expression, format StringExpression) StringExpression {
return NewStringFunc("TO_CHAR", expression, format)
}
// TO_DATE converts string to date using format
func TO_DATE(dateStr, format StringExpression) DateExpression {
return NewDateFunc("TO_DATE", dateStr, format)
}
// TO_NUMBER converts string to numeric using format
func TO_NUMBER(floatStr, format StringExpression) FloatExpression {
return NewFloatFunc("TO_NUMBER", floatStr, format)
}
// TO_TIMESTAMP converts string to time stamp with time zone using format
func TO_TIMESTAMP(timestampzStr, format StringExpression) TimestampzExpression {
return newTimestampzFunc("TO_TIMESTAMP", timestampzStr, format)
}
//----------------- Date/Time Functions and Operators ---------------//
// EXTRACT extracts time component from time expression
func EXTRACT(field string, from Expression) Expression {
return CustomExpression(Token("EXTRACT("), Token(field), Token("FROM"), from, Token(")"))
}
// CURRENT_DATE returns current date
func CURRENT_DATE() DateExpression {
dateFunc := NewDateFunc("CURRENT_DATE")
dateFunc.noBrackets = true
return dateFunc
}
// CURRENT_TIME returns current time with time zone
func CURRENT_TIME(precision ...int) TimezExpression {
var timezFunc *timezFunc
if len(precision) > 0 {
timezFunc = newTimezFunc("CURRENT_TIME", FixedLiteral(precision[0]))
} else {
timezFunc = newTimezFunc("CURRENT_TIME")
}
timezFunc.noBrackets = true
return timezFunc
}
// CURRENT_TIMESTAMP returns current timestamp with time zone
func CURRENT_TIMESTAMP(precision ...int) TimestampzExpression {
var timestampzFunc *timestampzFunc
if len(precision) > 0 {
timestampzFunc = newTimestampzFunc("CURRENT_TIMESTAMP", FixedLiteral(precision[0]))
} else {
timestampzFunc = newTimestampzFunc("CURRENT_TIMESTAMP")
}
timestampzFunc.noBrackets = true
return timestampzFunc
}
// LOCALTIME returns local time of day using optional precision
func LOCALTIME(precision ...int) TimeExpression {
var timeFunc *timeFunc
if len(precision) > 0 {
timeFunc = NewTimeFunc("LOCALTIME", FixedLiteral(precision[0]))
} else {
timeFunc = NewTimeFunc("LOCALTIME")
}
timeFunc.noBrackets = true
return timeFunc
}
// LOCALTIMESTAMP returns current date and time using optional precision
func LOCALTIMESTAMP(precision ...int) TimestampExpression {
var timestampFunc *timestampFunc
if len(precision) > 0 {
timestampFunc = NewTimestampFunc("LOCALTIMESTAMP", FixedLiteral(precision[0]))
} else {
timestampFunc = NewTimestampFunc("LOCALTIMESTAMP")
}
timestampFunc.noBrackets = true
return timestampFunc
}
// NOW returns current date and time
func NOW() TimestampzExpression {
return newTimestampzFunc("NOW")
}
// --------------- Conditional Expressions Functions -------------//
// COALESCE function returns the first of its arguments that is not null.
func COALESCE(value Expression, values ...Expression) Expression {
var allValues = []Expression{value}
allValues = append(allValues, values...)
return NewFunc("COALESCE", allValues, nil)
}
// NULLIF function returns a null value if value1 equals value2; otherwise it returns value1.
func NULLIF(value1, value2 Expression) Expression {
return NewFunc("NULLIF", []Expression{value1, value2}, nil)
}
// GREATEST selects the largest value from a list of expressions
func GREATEST(value Expression, values ...Expression) Expression {
var allValues = []Expression{value}
allValues = append(allValues, values...)
return NewFunc("GREATEST", allValues, nil)
}
// LEAST selects the smallest value from a list of expressions
func LEAST(value Expression, values ...Expression) Expression {
var allValues = []Expression{value}
allValues = append(allValues, values...)
return NewFunc("LEAST", allValues, nil)
}
//--------------------------------------------------------------------//
type funcExpressionImpl struct {
ExpressionInterfaceImpl
name string
parameters parametersSerializer
noBrackets bool
}
// NewFunc creates new function with name and expressions parameters
func NewFunc(name string, expressions []Expression, parent Expression) *funcExpressionImpl {
funcExp := &funcExpressionImpl{
name: name,
parameters: parametersSerializer(expressions),
}
if parent != nil {
funcExp.ExpressionInterfaceImpl.Parent = parent
} else {
funcExp.ExpressionInterfaceImpl.Parent = funcExp
}
return funcExp
}
func (f *funcExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if serializeOverride := out.Dialect.FunctionSerializeOverride(f.name); serializeOverride != nil {
serializeOverrideFunc := serializeOverride(ExpressionListToSerializerList(f.parameters)...)
serializeOverrideFunc(statement, out, FallTrough(options)...)
return
}
addBrackets := !f.noBrackets || len(f.parameters) > 0
if addBrackets {
out.WriteString(f.name + "(")
} else {
out.WriteString(f.name)
}
f.parameters.serialize(statement, out, options...)
if addBrackets {
out.WriteString(")")
}
}
type parametersSerializer []Expression
func (p parametersSerializer) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
for i, expression := range p {
if i > 0 {
out.WriteString(", ")
}
if _, isStatement := expression.(Statement); isStatement {
expression.serialize(statement, out, options...)
} else {
expression.serialize(statement, out, append(options, NoWrap, Ident)...)
}
}
}
// NewFloatWindowFunc creates new float function with name and expressions
func newWindowFunc(name string, expressions ...Expression) windowExpression {
newFun := NewFunc(name, expressions, nil)
windowExpr := newWindowExpression(newFun)
newFun.ExpressionInterfaceImpl.Parent = windowExpr
return windowExpr
}
type boolFunc struct {
funcExpressionImpl
boolInterfaceImpl
}
func newBoolFunc(name string, expressions ...Expression) BoolExpression {
boolFunc := &boolFunc{}
boolFunc.funcExpressionImpl = *NewFunc(name, expressions, boolFunc)
boolFunc.boolInterfaceImpl.parent = boolFunc
boolFunc.ExpressionInterfaceImpl.Parent = boolFunc
return boolFunc
}
// NewFloatWindowFunc creates new float function with name and expressions
func newBoolWindowFunc(name string, expressions ...Expression) boolWindowExpression {
boolFunc := &boolFunc{}
boolFunc.funcExpressionImpl = *NewFunc(name, expressions, boolFunc)
intWindowFunc := newBoolWindowExpression(boolFunc)
boolFunc.boolInterfaceImpl.parent = intWindowFunc
boolFunc.ExpressionInterfaceImpl.Parent = intWindowFunc
return intWindowFunc
}
type floatFunc struct {
funcExpressionImpl
floatInterfaceImpl
}
// NewFloatFunc creates new float function with name and expressions
func NewFloatFunc(name string, expressions ...Expression) FloatExpression {
floatFunc := &floatFunc{}
floatFunc.funcExpressionImpl = *NewFunc(name, expressions, floatFunc)
floatFunc.floatInterfaceImpl.parent = floatFunc
return floatFunc
}
// NewFloatWindowFunc creates new float function with name and expressions
func NewFloatWindowFunc(name string, expressions ...Expression) floatWindowExpression {
floatFunc := &floatFunc{}
floatFunc.funcExpressionImpl = *NewFunc(name, expressions, floatFunc)
floatWindowFunc := newFloatWindowExpression(floatFunc)
floatFunc.floatInterfaceImpl.parent = floatWindowFunc
floatFunc.ExpressionInterfaceImpl.Parent = floatWindowFunc
return floatWindowFunc
}
type integerFunc struct {
funcExpressionImpl
integerInterfaceImpl
}
func newIntegerFunc(name string, expressions ...Expression) IntegerExpression {
intFunc := &integerFunc{}
intFunc.funcExpressionImpl = *NewFunc(name, expressions, intFunc)
intFunc.integerInterfaceImpl.parent = intFunc
return intFunc
}
// NewFloatWindowFunc creates new float function with name and expressions
func newIntegerWindowFunc(name string, expressions ...Expression) integerWindowExpression {
integerFunc := &integerFunc{}
integerFunc.funcExpressionImpl = *NewFunc(name, expressions, integerFunc)
intWindowFunc := newIntegerWindowExpression(integerFunc)
integerFunc.integerInterfaceImpl.parent = intWindowFunc
integerFunc.ExpressionInterfaceImpl.Parent = intWindowFunc
return intWindowFunc
}
type stringFunc struct {
funcExpressionImpl
stringInterfaceImpl
}
// NewStringFunc creates new string function with name and expression parameters
func NewStringFunc(name string, expressions ...Expression) StringExpression {
stringFunc := &stringFunc{}
stringFunc.funcExpressionImpl = *NewFunc(name, expressions, stringFunc)
stringFunc.stringInterfaceImpl.parent = stringFunc
return stringFunc
}
type dateFunc struct {
funcExpressionImpl
dateInterfaceImpl
}
// NewDateFunc creates new date function with name and expression parameters
func NewDateFunc(name string, expressions ...Expression) *dateFunc {
dateFunc := &dateFunc{}
dateFunc.funcExpressionImpl = *NewFunc(name, expressions, dateFunc)
dateFunc.dateInterfaceImpl.parent = dateFunc
return dateFunc
}
type timeFunc struct {
funcExpressionImpl
timeInterfaceImpl
}
// NewTimeFunc creates new time function with name and expression parameters
func NewTimeFunc(name string, expressions ...Expression) *timeFunc {
timeFun := &timeFunc{}
timeFun.funcExpressionImpl = *NewFunc(name, expressions, timeFun)
timeFun.timeInterfaceImpl.parent = timeFun
return timeFun
}
type timezFunc struct {
funcExpressionImpl
timezInterfaceImpl
}
func newTimezFunc(name string, expressions ...Expression) *timezFunc {
timezFun := &timezFunc{}
timezFun.funcExpressionImpl = *NewFunc(name, expressions, timezFun)
timezFun.timezInterfaceImpl.parent = timezFun
return timezFun
}
type timestampFunc struct {
funcExpressionImpl
timestampInterfaceImpl
}
// NewTimestampFunc creates new timestamp function with name and expressions
func NewTimestampFunc(name string, expressions ...Expression) *timestampFunc {
timestampFunc := &timestampFunc{}
timestampFunc.funcExpressionImpl = *NewFunc(name, expressions, timestampFunc)
timestampFunc.timestampInterfaceImpl.parent = timestampFunc
return timestampFunc
}
type timestampzFunc struct {
funcExpressionImpl
timestampzInterfaceImpl
}
func newTimestampzFunc(name string, expressions ...Expression) *timestampzFunc {
timestampzFunc := &timestampzFunc{}
timestampzFunc.funcExpressionImpl = *NewFunc(name, expressions, timestampzFunc)
timestampzFunc.timestampzInterfaceImpl.parent = timestampzFunc
return timestampzFunc
}
// Func can be used to call custom or unsupported database functions.
func Func(name string, expressions ...Expression) Expression {
return NewFunc(name, expressions, nil)
}
func NumRange(lowNum, highNum NumericExpression, bounds ...StringExpression) Range[NumericExpression] {
return NumRangeExp(NewFunc("numrange", rangeFuncParamCombiner(lowNum, highNum, bounds...), nil))
}
func Int4Range(lowNum, highNum IntegerExpression, bounds ...StringExpression) Range[Int4Expression] {
return Int4RangeExp(NewFunc("int4range", rangeFuncParamCombiner(lowNum, highNum, bounds...), nil))
}
func Int8Range(lowNum, highNum Int8Expression, bounds ...StringExpression) Range[Int8Expression] {
return Int8RangeExp(NewFunc("int8range", rangeFuncParamCombiner(lowNum, highNum, bounds...), nil))
}
func TsRange(lowTs, highTs TimestampExpression, bounds ...StringExpression) Range[TimestampExpression] {
return TsRangeExp(NewFunc("tsrange", rangeFuncParamCombiner(lowTs, highTs, bounds...), nil))
}
func TstzRange(lowTs, highTs TimestampzExpression, bounds ...StringExpression) Range[TimestampzExpression] {
return TstzRangeExp(NewFunc("tstzrange", rangeFuncParamCombiner(lowTs, highTs, bounds...), nil))
}
func DateRange(lowTs, highTs DateExpression, bounds ...StringExpression) Range[DateExpression] {
return DateRangeExp(NewFunc("daterange", rangeFuncParamCombiner(lowTs, highTs, bounds...), nil))
}
func rangeFuncParamCombiner(low, high Expression, bounds ...StringExpression) []Expression {
exp := []Expression{low, high}
if len(bounds) != 0 {
exp = append(exp, bounds[0])
}
return exp
}

View File

@@ -0,0 +1,41 @@
package jet
// GroupByClause interface
type GroupByClause interface {
serializeForGroupBy(statement StatementType, out *SQLBuilder)
}
// GROUPING_SETS operator allows grouping of the rows in a table by multiple sets of columns in a single query.
// This can be useful when we want to analyze data by different combinations of columns, without having to write separate
// queries for each combination.
func GROUPING_SETS(expressions ...Expression) GroupByClause {
return Func("GROUPING SETS", expressions...)
}
// ROLLUP operator is used with the GROUP BY clause to generate all prefixes of a group of columns including the empty list.
// It creates extra rows in the result set that represent the subtotal values for each combination of columns.
func ROLLUP(expressions ...Expression) GroupByClause {
return Func("ROLLUP", expressions...)
}
// CUBE operator is used with the GROUP BY clause to generate subtotals for all possible combinations of a group of columns.
// It creates extra rows in the result set that represent the subtotal values for each combination of columns.
func CUBE(expressions ...Expression) GroupByClause {
return Func("CUBE", expressions...)
}
// GROUPING function is used to identify which columns are included in a grouping set or a subtotal row. It takes as input
// the name of a column and returns 1 if the column is not included in the current grouping set, and 0 otherwise.
// It can be also used with multiple parameters to check if a set of columns is included in the current grouping set. The result
// of the GROUPING function would then be an integer bit mask having 1s for the arguments which have GROUPING(argument) as 1.
func GROUPING(expressions ...Expression) IntegerExpression {
return IntExp(Func("GROUPING", expressions...))
}
// WITH_ROLLUP operator is used with the GROUP BY clause to generate all prefixes of a group of columns including the empty list.
// It creates extra rows in the result set that represent the subtotal values for each combination of columns.
func WITH_ROLLUP(expressions ...Expression) GroupByClause {
return CustomExpression(
parametersSerializer(expressions), Token("WITH ROLLUP"),
)
}

View File

@@ -0,0 +1,156 @@
package jet
// IntegerExpression interface
type IntegerExpression interface {
Expression
numericExpression
EQ(rhs IntegerExpression) BoolExpression
NOT_EQ(rhs IntegerExpression) BoolExpression
IS_DISTINCT_FROM(rhs IntegerExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs IntegerExpression) BoolExpression
LT(rhs IntegerExpression) BoolExpression
LT_EQ(rhs IntegerExpression) BoolExpression
GT(rhs IntegerExpression) BoolExpression
GT_EQ(rhs IntegerExpression) BoolExpression
BETWEEN(min, max IntegerExpression) BoolExpression
NOT_BETWEEN(min, max IntegerExpression) BoolExpression
ADD(rhs IntegerExpression) IntegerExpression
SUB(rhs IntegerExpression) IntegerExpression
MUL(rhs IntegerExpression) IntegerExpression
DIV(rhs IntegerExpression) IntegerExpression
MOD(rhs IntegerExpression) IntegerExpression
POW(rhs IntegerExpression) IntegerExpression
BIT_AND(rhs IntegerExpression) IntegerExpression
BIT_OR(rhs IntegerExpression) IntegerExpression
BIT_XOR(rhs IntegerExpression) IntegerExpression
BIT_SHIFT_LEFT(shift IntegerExpression) IntegerExpression
BIT_SHIFT_RIGHT(shift IntegerExpression) IntegerExpression
}
// additional integer expression subtypes, used in range expressions.
type (
Int4Expression IntegerExpression
Int8Expression IntegerExpression
)
type integerInterfaceImpl struct {
numericExpressionImpl
parent IntegerExpression
}
func (i *integerInterfaceImpl) EQ(rhs IntegerExpression) BoolExpression {
return Eq(i.parent, rhs)
}
func (i *integerInterfaceImpl) NOT_EQ(rhs IntegerExpression) BoolExpression {
return NotEq(i.parent, rhs)
}
func (i *integerInterfaceImpl) IS_DISTINCT_FROM(rhs IntegerExpression) BoolExpression {
return IsDistinctFrom(i.parent, rhs)
}
func (i *integerInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs IntegerExpression) BoolExpression {
return IsNotDistinctFrom(i.parent, rhs)
}
func (i *integerInterfaceImpl) GT(rhs IntegerExpression) BoolExpression {
return Gt(i.parent, rhs)
}
func (i *integerInterfaceImpl) GT_EQ(rhs IntegerExpression) BoolExpression {
return GtEq(i.parent, rhs)
}
func (i *integerInterfaceImpl) LT(rhs IntegerExpression) BoolExpression {
return Lt(i.parent, rhs)
}
func (i *integerInterfaceImpl) LT_EQ(rhs IntegerExpression) BoolExpression {
return LtEq(i.parent, rhs)
}
func (i *integerInterfaceImpl) BETWEEN(min, max IntegerExpression) BoolExpression {
return NewBetweenOperatorExpression(i.parent, min, max, false)
}
func (i *integerInterfaceImpl) NOT_BETWEEN(min, max IntegerExpression) BoolExpression {
return NewBetweenOperatorExpression(i.parent, min, max, true)
}
func (i *integerInterfaceImpl) ADD(rhs IntegerExpression) IntegerExpression {
return IntExp(Add(i.parent, rhs))
}
func (i *integerInterfaceImpl) SUB(rhs IntegerExpression) IntegerExpression {
return IntExp(Sub(i.parent, rhs))
}
func (i *integerInterfaceImpl) MUL(rhs IntegerExpression) IntegerExpression {
return IntExp(Mul(i.parent, rhs))
}
func (i *integerInterfaceImpl) DIV(rhs IntegerExpression) IntegerExpression {
return IntExp(Div(i.parent, rhs))
}
func (i *integerInterfaceImpl) MOD(rhs IntegerExpression) IntegerExpression {
return IntExp(Mod(i.parent, rhs))
}
func (i *integerInterfaceImpl) POW(rhs IntegerExpression) IntegerExpression {
return IntExp(POW(i.parent, rhs))
}
func (i *integerInterfaceImpl) BIT_AND(rhs IntegerExpression) IntegerExpression {
return newBinaryIntegerOperatorExpression(i.parent, rhs, "&")
}
func (i *integerInterfaceImpl) BIT_OR(rhs IntegerExpression) IntegerExpression {
return newBinaryIntegerOperatorExpression(i.parent, rhs, "|")
}
func (i *integerInterfaceImpl) BIT_XOR(rhs IntegerExpression) IntegerExpression {
return newBinaryIntegerOperatorExpression(i.parent, rhs, "#")
}
func (i *integerInterfaceImpl) BIT_SHIFT_LEFT(intExpression IntegerExpression) IntegerExpression {
return newBinaryIntegerOperatorExpression(i.parent, intExpression, "<<")
}
func (i *integerInterfaceImpl) BIT_SHIFT_RIGHT(intExpression IntegerExpression) IntegerExpression {
return newBinaryIntegerOperatorExpression(i.parent, intExpression, ">>")
}
func newBinaryIntegerOperatorExpression(lhs, rhs IntegerExpression, operator string) IntegerExpression {
return IntExp(NewBinaryOperatorExpression(lhs, rhs, operator))
}
func newPrefixIntegerOperatorExpression(expression IntegerExpression, operator string) IntegerExpression {
return IntExp(newPrefixOperatorExpression(expression, operator))
}
type integerExpressionWrapper struct {
integerInterfaceImpl
Expression
}
func newIntExpressionWrap(expression Expression) IntegerExpression {
intExpressionWrap := integerExpressionWrapper{Expression: expression}
intExpressionWrap.integerInterfaceImpl.parent = &intExpressionWrap
return &intExpressionWrap
}
// IntExp is int expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as int expression.
// Does not add sql cast to generated sql builder output.
func IntExp(expression Expression) IntegerExpression {
return newIntExpressionWrap(expression)
}

View File

@@ -0,0 +1,37 @@
package jet
// Interval is internal common representation of sql interval
type Interval interface {
Serializer
IsInterval
}
// IsInterval interface
type IsInterval interface {
isInterval()
}
// IsIntervalImpl is implementation of IsInterval interface
type IsIntervalImpl struct{}
func (i *IsIntervalImpl) isInterval() {}
// NewInterval creates new interval from serializer
func NewInterval(s Serializer) *IntervalImpl {
newInterval := &IntervalImpl{
Value: s,
}
return newInterval
}
// IntervalImpl is implementation of Interval type
type IntervalImpl struct {
Value Serializer
IsIntervalImpl
}
func (i IntervalImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("INTERVAL")
i.Value.serialize(statement, out, FallTrough(options)...)
}

View File

@@ -0,0 +1,13 @@
package jet
const (
// DEFAULT is jet equivalent of SQL DEFAULT
DEFAULT Keyword = "DEFAULT"
)
// Keyword type
type Keyword string
func (k Keyword) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString(string(k))
}

View File

@@ -0,0 +1,480 @@
package jet
import (
"fmt"
"time"
)
// LiteralExpression is representation of an escaped literal
type LiteralExpression interface {
Expression
Value() interface{}
SetConstant(constant bool)
}
type literalExpressionImpl struct {
ExpressionInterfaceImpl
value interface{}
constant bool
}
func literal(value interface{}, optionalConstant ...bool) *literalExpressionImpl {
exp := literalExpressionImpl{value: value}
if len(optionalConstant) > 0 {
exp.constant = optionalConstant[0]
}
exp.ExpressionInterfaceImpl.Parent = &exp
return &exp
}
// Literal is injected directly to SQL query, and does not appear in parametrized argument list.
func Literal(value interface{}) *literalExpressionImpl {
exp := literal(value)
return exp
}
// FixedLiteral is injected directly to SQL query, and does not appear in parametrized argument list.
func FixedLiteral(value interface{}) *literalExpressionImpl {
exp := literal(value)
exp.constant = true
return exp
}
func (l *literalExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if l.constant {
out.insertConstantArgument(l.value)
} else {
out.insertParametrizedArgument(l.value)
}
}
func (l *literalExpressionImpl) Value() interface{} {
return l.value
}
func (l *literalExpressionImpl) SetConstant(constant bool) {
l.constant = constant
}
type integerLiteralExpression struct {
literalExpressionImpl
integerInterfaceImpl
}
func intLiteral(value interface{}) IntegerExpression {
numLiteral := &integerLiteralExpression{}
numLiteral.literalExpressionImpl = *literal(value)
numLiteral.literalExpressionImpl.Parent = numLiteral
numLiteral.integerInterfaceImpl.parent = numLiteral
return numLiteral
}
// Int creates a new 64 bit signed integer literal
func Int(value int64) IntegerExpression {
return intLiteral(value)
}
// Int8 creates a new 8 bit signed integer literal
func Int8(value int8) IntegerExpression {
return intLiteral(value)
}
// Int16 creates a new 16 bit signed integer literal
func Int16(value int16) IntegerExpression {
return intLiteral(value)
}
// Int32 creates a new 32 bit signed integer literal
func Int32(value int32) IntegerExpression {
return intLiteral(value)
}
// Uint8 creates a new 8 bit unsigned integer literal
func Uint8(value uint8) IntegerExpression {
return intLiteral(value)
}
// Uint16 creates a new 16 bit unsigned integer literal
func Uint16(value uint16) IntegerExpression {
return intLiteral(value)
}
// Uint32 creates a new 32 bit unsigned integer literal
func Uint32(value uint32) IntegerExpression {
return intLiteral(value)
}
// Uint64 creates a new 64 bit unsigned integer literal
func Uint64(value uint64) IntegerExpression {
return intLiteral(value)
}
// ---------------------------------------------------//
type boolLiteralExpression struct {
boolInterfaceImpl
literalExpressionImpl
}
// Bool creates new bool literal expression
func Bool(value bool) BoolExpression {
boolLiteralExpression := boolLiteralExpression{}
boolLiteralExpression.literalExpressionImpl = *literal(value)
boolLiteralExpression.boolInterfaceImpl.parent = &boolLiteralExpression
return &boolLiteralExpression
}
// ---------------------------------------------------//
type floatLiteral struct {
floatInterfaceImpl
literalExpressionImpl
}
// Float creates new float literal from float64 value
func Float(value float64) FloatExpression {
floatLiteral := floatLiteral{}
floatLiteral.literalExpressionImpl = *literal(value)
floatLiteral.floatInterfaceImpl.parent = &floatLiteral
return &floatLiteral
}
// Decimal creates new float literal from string value
func Decimal(value string) FloatExpression {
floatLiteral := floatLiteral{}
floatLiteral.literalExpressionImpl = *literal(value)
floatLiteral.floatInterfaceImpl.parent = &floatLiteral
return &floatLiteral
}
// ---------------------------------------------------//
type stringLiteral struct {
stringInterfaceImpl
literalExpressionImpl
}
// String creates new string literal expression
func String(value string) StringExpression {
stringLiteral := stringLiteral{}
stringLiteral.literalExpressionImpl = *literal(value)
stringLiteral.stringInterfaceImpl.parent = &stringLiteral
return &stringLiteral
}
//---------------------------------------------------//
type timeLiteral struct {
timeInterfaceImpl
literalExpressionImpl
}
// Time creates new time literal expression
func Time(hour, minute, second int, nanoseconds ...time.Duration) TimeExpression {
timeLiteral := &timeLiteral{}
timeStr := fmt.Sprintf("%02d:%02d:%02d", hour, minute, second)
timeStr += formatNanoseconds(nanoseconds...)
timeLiteral.literalExpressionImpl = *literal(timeStr)
timeLiteral.timeInterfaceImpl.parent = timeLiteral
return timeLiteral
}
// TimeT creates new time literal expression from time.Time object
func TimeT(t time.Time) TimeExpression {
timeLiteral := &timeLiteral{}
timeLiteral.literalExpressionImpl = *literal(t)
timeLiteral.timeInterfaceImpl.parent = timeLiteral
return timeLiteral
}
//---------------------------------------------------//
type timezLiteral struct {
timezInterfaceImpl
literalExpressionImpl
}
// Timez creates new time with time zone literal expression
func Timez(hour, minute, second int, nanoseconds time.Duration, timezone string) TimezExpression {
timezLiteral := timezLiteral{}
timeStr := fmt.Sprintf("%02d:%02d:%02d", hour, minute, second)
timeStr += formatNanoseconds(nanoseconds)
timeStr += " " + timezone
timezLiteral.literalExpressionImpl = *literal(timeStr)
return TimezExp(literal(timeStr))
}
// TimezT creates new time with time zone literal expression from time.Time object
func TimezT(t time.Time) TimezExpression {
timeLiteral := &timezLiteral{}
timeLiteral.literalExpressionImpl = *literal(t)
timeLiteral.timezInterfaceImpl.parent = timeLiteral
return timeLiteral
}
//---------------------------------------------------//
type timestampLiteral struct {
timestampInterfaceImpl
literalExpressionImpl
}
// Timestamp creates new timestamp literal expression
func Timestamp(year int, month time.Month, day, hour, minute, second int, nanoseconds ...time.Duration) TimestampExpression {
timestamp := &timestampLiteral{}
timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
timeStr += formatNanoseconds(nanoseconds...)
timestamp.literalExpressionImpl = *literal(timeStr)
timestamp.timestampInterfaceImpl.parent = timestamp
return timestamp
}
// TimestampT creates new timestamp literal expression from time.Time object
func TimestampT(t time.Time) TimestampExpression {
timestamp := &timestampLiteral{}
timestamp.literalExpressionImpl = *literal(t)
timestamp.timestampInterfaceImpl.parent = timestamp
return timestamp
}
//---------------------------------------------------//
type timestampzLiteral struct {
timestampzInterfaceImpl
literalExpressionImpl
}
// Timestampz creates new timestamp with time zone literal expression
func Timestampz(year int, month time.Month, day, hour, minute, second int, nanoseconds time.Duration, timezone string) TimestampzExpression {
timestamp := &timestampzLiteral{}
timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
timeStr += formatNanoseconds(nanoseconds)
timeStr += " " + timezone
timestamp.literalExpressionImpl = *literal(timeStr)
timestamp.timestampzInterfaceImpl.parent = timestamp
return timestamp
}
// TimestampzT creates new timestamp literal expression from time.Time object
func TimestampzT(t time.Time) TimestampzExpression {
timestamp := &timestampzLiteral{}
timestamp.literalExpressionImpl = *literal(t)
timestamp.timestampzInterfaceImpl.parent = timestamp
return timestamp
}
//---------------------------------------------------//
type dateLiteral struct {
dateInterfaceImpl
literalExpressionImpl
}
// Date creates new date literal expression
func Date(year int, month time.Month, day int) DateExpression {
dateLiteral := &dateLiteral{}
timeStr := fmt.Sprintf("%04d-%02d-%02d", year, month, day)
dateLiteral.literalExpressionImpl = *literal(timeStr)
dateLiteral.dateInterfaceImpl.parent = dateLiteral
return dateLiteral
}
// DateT creates new date literal expression from time.Time object
func DateT(t time.Time) DateExpression {
dateLiteral := &dateLiteral{}
dateLiteral.literalExpressionImpl = *literal(t)
dateLiteral.dateInterfaceImpl.parent = dateLiteral
return dateLiteral
}
func formatNanoseconds(nanoseconds ...time.Duration) string {
if len(nanoseconds) > 0 && nanoseconds[0] != 0 {
duration := fmt.Sprintf("%09d", nanoseconds[0])
i := len(duration) - 1
for ; i >= 3; i-- {
if duration[i] != '0' {
break
}
}
return "." + duration[0:i+1]
}
return ""
}
//--------------------------------------------------//
var (
// NULL is jet equivalent of SQL NULL
NULL = newNullLiteral()
// STAR is jet equivalent of SQL *
STAR = newStarLiteral()
// PLUS_INFINITY is jet equivalent for sql infinity
PLUS_INFINITY = String("infinity")
// MINUS_INFINITY is jet equivalent for sql -infinity
MINUS_INFINITY = String("-infinity")
)
type nullLiteral struct {
ExpressionInterfaceImpl
}
func newNullLiteral() Expression {
nullExpression := &nullLiteral{}
nullExpression.ExpressionInterfaceImpl.Parent = nullExpression
return nullExpression
}
func (n *nullLiteral) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("NULL")
}
// --------------------------------------------------//
type starLiteral struct {
ExpressionInterfaceImpl
}
func newStarLiteral() Expression {
starExpression := &starLiteral{}
starExpression.ExpressionInterfaceImpl.Parent = starExpression
return starExpression
}
func (n *starLiteral) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("*")
}
//---------------------------------------------------//
type rawExpression struct {
ExpressionInterfaceImpl
Raw string
NamedArgument map[string]interface{}
noWrap bool
}
func (n *rawExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if !n.noWrap && !contains(options, NoWrap) {
out.WriteByte('(')
}
out.insertRawQuery(n.Raw, n.NamedArgument)
if !n.noWrap && !contains(options, NoWrap) {
out.WriteByte(')')
}
}
// Raw can be used for any unsupported functions, operators or expressions.
// For example: Raw("current_database()")
func Raw(raw string, namedArgs ...map[string]interface{}) Expression {
var namedArguments map[string]interface{}
if len(namedArgs) > 0 {
namedArguments = namedArgs[0]
}
rawExp := &rawExpression{
Raw: raw,
NamedArgument: namedArguments,
}
rawExp.ExpressionInterfaceImpl.Parent = rawExp
return rawExp
}
// RawWithParent is a Raw constructor used for construction dialect specific expression
func RawWithParent(raw string, parent ...Expression) Expression {
rawExp := &rawExpression{
Raw: raw,
noWrap: true,
}
rawExp.ExpressionInterfaceImpl.Parent = OptionalOrDefaultExpression(rawExp, parent...)
return rawExp
}
// RawBool helper that for raw string boolean expressions
func RawBool(raw string, namedArgs ...map[string]interface{}) BoolExpression {
return BoolExp(Raw(raw, namedArgs...))
}
// RawInt helper that for integer expressions
func RawInt(raw string, namedArgs ...map[string]interface{}) IntegerExpression {
return IntExp(Raw(raw, namedArgs...))
}
// RawFloat helper that for float expressions
func RawFloat(raw string, namedArgs ...map[string]interface{}) FloatExpression {
return FloatExp(Raw(raw, namedArgs...))
}
// RawString helper that for string expressions
func RawString(raw string, namedArgs ...map[string]interface{}) StringExpression {
return StringExp(Raw(raw, namedArgs...))
}
// RawTime helper that for time expressions
func RawTime(raw string, namedArgs ...map[string]interface{}) TimeExpression {
return TimeExp(Raw(raw, namedArgs...))
}
// RawTimez helper that for time with time zone expressions
func RawTimez(raw string, namedArgs ...map[string]interface{}) TimezExpression {
return TimezExp(Raw(raw, namedArgs...))
}
// RawTimestamp helper that for timestamp expressions
func RawTimestamp(raw string, namedArgs ...map[string]interface{}) TimestampExpression {
return TimestampExp(Raw(raw, namedArgs...))
}
// RawTimestampz helper that for timestamp with time zone expressions
func RawTimestampz(raw string, namedArgs ...map[string]interface{}) TimestampzExpression {
return TimestampzExp(Raw(raw, namedArgs...))
}
// RawDate helper that for date expressions
func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression {
return DateExp(Raw(raw, namedArgs...))
}
// RawRange helper that for range expressions
func RawRange[T Expression](raw string, namedArgs ...map[string]interface{}) Range[T] {
return RangeExp[T](Raw(raw, namedArgs...))
}
// UUID is a helper function to create string literal expression from uuid object
// value can be any uuid type with a String method
func UUID(value fmt.Stringer) StringExpression {
return String(value.String())
}

81
vendor/github.com/go-jet/jet/v2/internal/jet/logger.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package jet
import (
"context"
"runtime"
"strings"
"time"
)
// PrintableStatement is a statement which sql query can be logged
type PrintableStatement interface {
Sql() (query string, args []interface{})
DebugSql() (query string)
}
// LoggerFunc is a function user can implement to support automatic statement logging.
type LoggerFunc func(ctx context.Context, statement PrintableStatement)
var logger LoggerFunc
// SetLoggerFunc sets automatic statement logging
func SetLoggerFunc(loggerFunc LoggerFunc) {
logger = loggerFunc
}
func callLogger(ctx context.Context, statement Statement) {
if logger != nil {
logger(ctx, statement)
}
}
// QueryInfo contains information about executed query
type QueryInfo struct {
Statement PrintableStatement
// Depending on how the statement is executed, RowsProcessed is:
// - Number of rows returned for Query() and QueryContext() methods
// - RowsAffected() for Exec() and ExecContext() methods
// - Always 0 for Rows() method.
RowsProcessed int64
Duration time.Duration
Err error
}
// QueryLoggerFunc is a function user can implement to retrieve more information about statement executed.
type QueryLoggerFunc func(ctx context.Context, info QueryInfo)
var queryLoggerFunc QueryLoggerFunc
// SetQueryLogger sets automatic query logging function.
func SetQueryLogger(loggerFunc QueryLoggerFunc) {
queryLoggerFunc = loggerFunc
}
func callQueryLoggerFunc(ctx context.Context, info QueryInfo) {
if queryLoggerFunc != nil {
queryLoggerFunc(ctx, info)
}
}
// Caller returns information about statement caller
func (q QueryInfo) Caller() (file string, line int, function string) {
skip := 4
// depending on execution type (Query, QueryContext, Exec, ...) looped once or twice
for {
var pc uintptr
var ok bool
pc, file, line, ok = runtime.Caller(skip)
if !ok {
return
}
funcDetails := runtime.FuncForPC(pc)
if !strings.Contains(funcDetails.Name(), "github.com/go-jet/jet/v2/internal") {
function = funcDetails.Name()
return
}
skip++
}
}

View File

@@ -0,0 +1,15 @@
package jet
// NumericExpression is common interface for all integer and float expressions
type NumericExpression interface {
Expression
numericExpression
}
type numericExpression interface {
isNumericExpression()
}
type numericExpressionImpl struct{}
func (n *numericExpressionImpl) isNumericExpression() {}

View File

@@ -0,0 +1,194 @@
package jet
// Operators
const (
StringConcatOperator = "||"
StringRegexpLikeOperator = "REGEXP"
StringNotRegexpLikeOperator = "NOT REGEXP"
)
//----------- Logical operators ---------------//
// NOT returns negation of bool expression result
func NOT(exp BoolExpression) BoolExpression {
return newPrefixBoolOperatorExpression(exp, "NOT")
}
// BIT_NOT inverts every bit in integer expression result
func BIT_NOT(expr IntegerExpression) IntegerExpression {
if literalExp, ok := expr.(LiteralExpression); ok {
literalExp.SetConstant(true)
}
return newPrefixIntegerOperatorExpression(expr, "~")
}
//----------- Comparison operators ---------------//
// EXISTS checks for existence of the rows in subQuery
func EXISTS(subQuery Expression) BoolExpression {
return newPrefixBoolOperatorExpression(subQuery, "EXISTS")
}
// Eq returns a representation of "a=b"
func Eq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "=")
}
// NotEq returns a representation of "a!=b"
func NotEq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "!=")
}
// IsDistinctFrom returns a representation of "a IS DISTINCT FROM b"
func IsDistinctFrom(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "IS DISTINCT FROM")
}
// IsNotDistinctFrom returns a representation of "a IS NOT DISTINCT FROM b"
func IsNotDistinctFrom(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "IS NOT DISTINCT FROM")
}
// Lt returns a representation of "a<b"
func Lt(lhs Expression, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "<")
}
// LtEq returns a representation of "a<=b"
func LtEq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "<=")
}
// Gt returns a representation of "a>b"
func Gt(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, ">")
}
// GtEq returns a representation of "a>=b"
func GtEq(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, ">=")
}
// Contains returns a representation of "a @> b"
func Contains(lhs Expression, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "@>")
}
// Overlap returns a representation of "a && b"
func Overlap(lhs, rhs Expression) BoolExpression {
return newBinaryBoolOperatorExpression(lhs, rhs, "&&")
}
// Add notEq returns a representation of "a + b"
func Add(lhs, rhs Serializer) Expression {
return NewBinaryOperatorExpression(lhs, rhs, "+")
}
// Sub notEq returns a representation of "a - b"
func Sub(lhs, rhs Serializer) Expression {
return NewBinaryOperatorExpression(lhs, rhs, "-")
}
// Mul returns a representation of "a * b"
func Mul(lhs, rhs Serializer) Expression {
return NewBinaryOperatorExpression(lhs, rhs, "*")
}
// Div returns a representation of "a / b"
func Div(lhs, rhs Serializer) Expression {
return NewBinaryOperatorExpression(lhs, rhs, "/")
}
// Mod returns a representation of "a % b"
func Mod(lhs, rhs Serializer) Expression {
return NewBinaryOperatorExpression(lhs, rhs, "%")
}
// --------------- CASE operator -------------------//
// CaseOperator is interface for SQL case operator
type CaseOperator interface {
Expression
WHEN(condition Expression) CaseOperator
THEN(then Expression) CaseOperator
ELSE(els Expression) CaseOperator
}
type caseOperatorImpl struct {
ExpressionInterfaceImpl
expression Expression
when []Expression
then []Expression
els Expression
}
// CASE create CASE operator with optional list of expressions
func CASE(expression ...Expression) CaseOperator {
caseExp := &caseOperatorImpl{}
if len(expression) > 0 {
caseExp.expression = expression[0]
}
caseExp.ExpressionInterfaceImpl.Parent = caseExp
return caseExp
}
func (c *caseOperatorImpl) WHEN(when Expression) CaseOperator {
c.when = append(c.when, when)
return c
}
func (c *caseOperatorImpl) THEN(then Expression) CaseOperator {
c.then = append(c.then, then)
return c
}
func (c *caseOperatorImpl) ELSE(els Expression) CaseOperator {
c.els = els
return c
}
func (c *caseOperatorImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("(CASE")
if c.expression != nil {
c.expression.serialize(statement, out, FallTrough(options)...)
}
if len(c.when) == 0 || len(c.then) == 0 {
panic("jet: invalid case Statement. There should be at least one WHEN/THEN pair. ")
}
if len(c.when) != len(c.then) {
panic("jet: WHEN and THEN expression count mismatch. ")
}
for i, when := range c.when {
out.WriteString("WHEN")
when.serialize(statement, out, NoWrap)
out.WriteString("THEN")
c.then[i].serialize(statement, out, NoWrap)
}
if c.els != nil {
out.WriteString("ELSE")
c.els.serialize(statement, out, NoWrap)
}
out.WriteString("END)")
}
// DISTINCT operator can be used to return distinct values of expr
func DISTINCT(expr Expression) Expression {
return newPrefixOperatorExpression(expr, "DISTINCT")
}
func BinaryOperator(lhs Expression, rhs Expression, operator string) Expression {
return NewBinaryOperatorExpression(lhs, rhs, operator)
}

View File

@@ -0,0 +1,79 @@
package jet
// OrderByClause interface
type OrderByClause interface {
// NULLS_FIRST specifies sort where null values appear before all non-null values.
// For some dialects(mysql,mariadb), which do not support NULL_FIRST, NULL_FIRST is simulated
// with additional IS_NOT_NULL expression.
// For instance,
// Rental.ReturnDate.DESC().NULLS_FIRST()
// would translate to,
// rental.return_date IS NOT NULL, rental.return_date DESC
NULLS_FIRST() OrderByClause
// NULLS_LAST specifies sort where null values appear after all non-null values.
// For some dialects(mysql,mariadb), which do not support NULLS_LAST, NULLS_LAST is simulated
// with additional IS_NULL expression.
// For instance,
// Rental.ReturnDate.ASC().NULLS_LAST()
// would translate to,
// rental.return_date IS NULL, rental.return_date ASC
NULLS_LAST() OrderByClause
serializeForOrderBy(statement StatementType, out *SQLBuilder)
}
type orderByClauseImpl struct {
expression Expression
ascending *bool
nullsFirst *bool
}
func (ord *orderByClauseImpl) NULLS_FIRST() OrderByClause {
nullsFirst := true
ord.nullsFirst = &nullsFirst
return ord
}
func (ord *orderByClauseImpl) NULLS_LAST() OrderByClause {
nullsFirst := false
ord.nullsFirst = &nullsFirst
return ord
}
func (ord *orderByClauseImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
customSerializer := out.Dialect.SerializeOrderBy()
if customSerializer != nil {
customSerializer(ord.expression, ord.ascending, ord.nullsFirst)(statement, out)
return
}
if ord.expression == nil {
panic("jet: nil expression in ORDER BY clause")
}
ord.expression.serializeForOrderBy(statement, out)
if ord.ascending != nil {
if *ord.ascending {
out.WriteString("ASC")
} else {
out.WriteString("DESC")
}
}
if ord.nullsFirst != nil {
if *ord.nullsFirst {
out.WriteString("NULLS FIRST")
} else {
out.WriteString("NULLS LAST")
}
}
}
func newOrderByAscending(expression Expression, ascending bool) OrderByClause {
return &orderByClauseImpl{expression: expression, ascending: &ascending}
}
func newOrderByNullsFirst(expression Expression, nullsFirst bool) OrderByClause {
return &orderByClauseImpl{expression: expression, nullsFirst: &nullsFirst}
}

View File

@@ -0,0 +1,65 @@
package jet
// MODE computes the most frequent value of the aggregated argument
func MODE() *OrderSetAggregateFunc {
return newOrderSetAggregateFunction("MODE", nil)
}
// PERCENTILE_CONT computes a value corresponding to the specified fraction within the ordered set of
// aggregated argument values. This will interpolate between adjacent input items if needed.
func PERCENTILE_CONT(fraction FloatExpression) *OrderSetAggregateFunc {
return newOrderSetAggregateFunction("PERCENTILE_CONT", fraction)
}
// PERCENTILE_DISC computes the first value within the ordered set of aggregated argument values whose position
// in the ordering equals or exceeds the specified fraction. The aggregated argument must be of a sortable type.
func PERCENTILE_DISC(fraction FloatExpression) *OrderSetAggregateFunc {
return newOrderSetAggregateFunction("PERCENTILE_DISC", fraction)
}
// OrderSetAggregateFunc implementation of order set aggregate function
type OrderSetAggregateFunc struct {
name string
fraction FloatExpression
orderBy Window
}
func newOrderSetAggregateFunction(name string, fraction FloatExpression) *OrderSetAggregateFunc {
return &OrderSetAggregateFunc{
name: name,
fraction: fraction,
}
}
// WITHIN_GROUP_ORDER_BY specifies ordered set of aggregated argument values
func (p *OrderSetAggregateFunc) WITHIN_GROUP_ORDER_BY(orderBy OrderByClause) Expression {
p.orderBy = ORDER_BY(orderBy)
return newOrderSetAggregateFuncExpression(*p)
}
func newOrderSetAggregateFuncExpression(aggFunc OrderSetAggregateFunc) *orderSetAggregateFuncExpression {
ret := &orderSetAggregateFuncExpression{
OrderSetAggregateFunc: aggFunc,
}
ret.ExpressionInterfaceImpl.Parent = ret
return ret
}
type orderSetAggregateFuncExpression struct {
ExpressionInterfaceImpl
OrderSetAggregateFunc
}
func (p *orderSetAggregateFuncExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString(p.name)
if p.fraction != nil {
wrap(p.fraction).serialize(statement, out, FallTrough(options)...)
} else {
wrap().serialize(statement, out, FallTrough(options)...)
}
out.WriteString("WITHIN GROUP")
p.orderBy.serialize(statement, out)
}

View File

@@ -0,0 +1,81 @@
package jet
// Projection is interface for all projection types. Types that can be part of, for instance SELECT clause.
type Projection interface {
serializeForProjection(statement StatementType, out *SQLBuilder)
fromImpl(subQuery SelectTable) Projection
}
// SerializeForProjection is helper function for serializing projection outside of jet package
func SerializeForProjection(projection Projection, statementType StatementType, out *SQLBuilder) {
projection.serializeForProjection(statementType, out)
}
// ProjectionList is a redefined type, so that ProjectionList can be used as a Projection.
type ProjectionList []Projection
func (pl ProjectionList) fromImpl(subQuery SelectTable) Projection {
newProjectionList := ProjectionList{}
for _, projection := range pl {
newProjectionList = append(newProjectionList, projection.fromImpl(subQuery))
}
return newProjectionList
}
func (pl ProjectionList) serializeForProjection(statement StatementType, out *SQLBuilder) {
SerializeProjectionList(statement, pl, out)
}
// As will create new projection list where each column is wrapped with a new table alias.
// tableAlias should be in the form 'name' or 'name.*', or it can be an empty string, which will remove existing table alias.
// For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will
// have a column wrapped in alias 'Musician.Name'. If tableAlias is empty string, it removes existing table alias ('Artist.Name' becomes 'Name').
func (pl ProjectionList) As(tableAlias string) ProjectionList {
newProjectionList := ProjectionList{}
for _, projection := range pl {
switch p := projection.(type) {
case ProjectionList:
newProjectionList = append(newProjectionList, p.As(tableAlias))
case ColumnList:
newProjectionList = append(newProjectionList, p.As(tableAlias))
case ColumnExpression:
newProjectionList = append(newProjectionList, newAlias(p, joinAlias(tableAlias, p.Name())))
case *alias:
newAlias := *p
_, columnName := extractTableAndColumnName(newAlias.alias)
newAlias.alias = joinAlias(tableAlias, columnName)
newProjectionList = append(newProjectionList, &newAlias)
}
}
return newProjectionList
}
// Except will create new projection list in which columns contained in excluded column names are removed
func (pl ProjectionList) Except(toExclude ...Column) ProjectionList {
excludedColumnList := UnwidColumnList(toExclude)
excludedColumnNames := map[string]bool{}
for _, excludedColumn := range excludedColumnList {
excludedColumnNames[excludedColumn.Name()] = true
}
var ret ProjectionList
for _, projection := range pl {
switch p := projection.(type) {
case ProjectionList:
ret = append(ret, p.Except(toExclude...))
case ColumnExpression:
if excludedColumnNames[p.Name()] {
continue
}
ret = append(ret, p)
}
}
return ret
}

View File

@@ -0,0 +1,141 @@
package jet
// Range Expression is interface for date range types
type Range[T Expression] interface {
Expression
EQ(rhs Range[T]) BoolExpression
NOT_EQ(rhs Range[T]) BoolExpression
LT(rhs Range[T]) BoolExpression
LT_EQ(rhs Range[T]) BoolExpression
GT(rhs Range[T]) BoolExpression
GT_EQ(rhs Range[T]) BoolExpression
CONTAINS(rhs T) BoolExpression
CONTAINS_RANGE(rhs Range[T]) BoolExpression
OVERLAP(rhs Range[T]) BoolExpression
UNION(rhs Range[T]) Range[T]
INTERSECTION(rhs Range[T]) Range[T]
DIFFERENCE(rhs Range[T]) Range[T]
UPPER_BOUND() T
LOWER_BOUND() T
IS_EMPTY() BoolExpression
LOWER_INC() BoolExpression
UPPER_INC() BoolExpression
LOWER_INF() BoolExpression
UPPER_INF() BoolExpression
}
type rangeInterfaceImpl[T Expression] struct {
parent Range[T]
}
func (r *rangeInterfaceImpl[T]) EQ(rhs Range[T]) BoolExpression {
return Eq(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) NOT_EQ(rhs Range[T]) BoolExpression {
return NotEq(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) LT(rhs Range[T]) BoolExpression {
return Lt(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) LT_EQ(rhs Range[T]) BoolExpression {
return LtEq(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) GT(rhs Range[T]) BoolExpression {
return Gt(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) GT_EQ(rhs Range[T]) BoolExpression {
return GtEq(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) CONTAINS(rhs T) BoolExpression {
return Contains(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) CONTAINS_RANGE(rhs Range[T]) BoolExpression {
return Contains(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) OVERLAP(rhs Range[T]) BoolExpression {
return Overlap(r.parent, rhs)
}
func (r *rangeInterfaceImpl[T]) UNION(rhs Range[T]) Range[T] {
return RangeExp[T](Add(r.parent, rhs))
}
func (r *rangeInterfaceImpl[T]) INTERSECTION(rhs Range[T]) Range[T] {
return RangeExp[T](Mul(r.parent, rhs))
}
func (r *rangeInterfaceImpl[T]) DIFFERENCE(rhs Range[T]) Range[T] {
return RangeExp[T](Sub(r.parent, rhs))
}
func (r *rangeInterfaceImpl[T]) UPPER_BOUND() T {
return UPPER_BOUND(r.parent)
}
func (r *rangeInterfaceImpl[T]) LOWER_BOUND() T {
return LOWER_BOUND(r.parent)
}
func (r *rangeInterfaceImpl[T]) IS_EMPTY() BoolExpression {
return IS_EMPTY(r.parent)
}
func (r *rangeInterfaceImpl[T]) LOWER_INC() BoolExpression {
return LOWER_INC(r.parent)
}
func (r *rangeInterfaceImpl[T]) UPPER_INC() BoolExpression {
return UPPER_INC(r.parent)
}
func (r *rangeInterfaceImpl[T]) LOWER_INF() BoolExpression {
return LOWER_INF(r.parent)
}
func (r *rangeInterfaceImpl[T]) UPPER_INF() BoolExpression {
return UPPER_INF(r.parent)
}
//---------------------------------------------------//
type rangeExpressionWrapper[T Expression] struct {
rangeInterfaceImpl[T]
Expression
}
func newRangeExpressionWrap[T Expression](expression Expression) Range[T] {
rangeExpressionWrap := rangeExpressionWrapper[T]{Expression: expression}
rangeExpressionWrap.rangeInterfaceImpl.parent = &rangeExpressionWrap
return &rangeExpressionWrap
}
// RangeExp is range expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as range expression.
// Does not add sql cast to generated sql builder output.
func RangeExp[T Expression](expression Expression) Range[T] {
return newRangeExpressionWrap[T](expression)
}
// different range expression wrappers
var (
Int4RangeExp = RangeExp[Int4Expression]
Int8RangeExp = RangeExp[Int8Expression]
NumRangeExp = RangeExp[NumericExpression]
DateRangeExp = RangeExp[DateExpression]
TsRangeExp = RangeExp[TimestampExpression]
TstzRangeExp = RangeExp[TimestampzExpression]
)

View File

@@ -0,0 +1,47 @@
package jet
type rawStatementImpl struct {
serializerStatementInterfaceImpl
RawQuery string
NamedArguments map[string]interface{}
}
// RawStatement creates new sql statements from raw query and optional map of named arguments
func RawStatement(dialect Dialect, rawQuery string, namedArgument ...map[string]interface{}) SerializerStatement {
newRawStatement := rawStatementImpl{
serializerStatementInterfaceImpl: serializerStatementInterfaceImpl{
dialect: dialect,
statementType: "",
parent: nil,
},
RawQuery: rawQuery,
}
if len(namedArgument) > 0 {
newRawStatement.NamedArguments = namedArgument[0]
}
newRawStatement.parent = &newRawStatement
return &newRawStatement
}
func (s *rawStatementImpl) projections() ProjectionList {
return nil
}
func (s *rawStatementImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if !contains(options, NoWrap) {
out.WriteString("(")
out.IncreaseIdent()
}
out.insertRawQuery(s.RawQuery, s.NamedArguments)
if !contains(options, NoWrap) {
out.DecreaseIdent()
out.NewLine()
out.WriteString(")")
}
}

View File

@@ -0,0 +1,102 @@
package jet
// RowExpression interface
type RowExpression interface {
Expression
HasProjections
EQ(rhs RowExpression) BoolExpression
NOT_EQ(rhs RowExpression) BoolExpression
IS_DISTINCT_FROM(rhs RowExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs RowExpression) BoolExpression
LT(rhs RowExpression) BoolExpression
LT_EQ(rhs RowExpression) BoolExpression
GT(rhs RowExpression) BoolExpression
GT_EQ(rhs RowExpression) BoolExpression
}
type rowInterfaceImpl struct {
parent Expression
dialect Dialect
elemCount int
}
func (n *rowInterfaceImpl) EQ(rhs RowExpression) BoolExpression {
return Eq(n.parent, rhs)
}
func (n *rowInterfaceImpl) NOT_EQ(rhs RowExpression) BoolExpression {
return NotEq(n.parent, rhs)
}
func (n *rowInterfaceImpl) IS_DISTINCT_FROM(rhs RowExpression) BoolExpression {
return IsDistinctFrom(n.parent, rhs)
}
func (n *rowInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs RowExpression) BoolExpression {
return IsNotDistinctFrom(n.parent, rhs)
}
func (n *rowInterfaceImpl) GT(rhs RowExpression) BoolExpression {
return Gt(n.parent, rhs)
}
func (n *rowInterfaceImpl) GT_EQ(rhs RowExpression) BoolExpression {
return GtEq(n.parent, rhs)
}
func (n *rowInterfaceImpl) LT(rhs RowExpression) BoolExpression {
return Lt(n.parent, rhs)
}
func (n *rowInterfaceImpl) LT_EQ(rhs RowExpression) BoolExpression {
return LtEq(n.parent, rhs)
}
func (n *rowInterfaceImpl) projections() ProjectionList {
var ret ProjectionList
for i := 0; i < n.elemCount; i++ {
rowColumn := NewColumnImpl(n.dialect.ValuesDefaultColumnName(i), "", nil)
ret = append(ret, &rowColumn)
}
return ret
}
// ---------------------------------------------------//
type rowExpressionWrapper struct {
rowInterfaceImpl
Expression
}
func newRowExpression(name string, dialect Dialect, expressions ...Expression) RowExpression {
ret := &rowExpressionWrapper{}
ret.rowInterfaceImpl.parent = ret
ret.Expression = NewFunc(name, expressions, ret)
ret.dialect = dialect
ret.elemCount = len(expressions)
return ret
}
// ROW function is used to create a tuple value that consists of a set of expressions or column values.
func ROW(dialect Dialect, expressions ...Expression) RowExpression {
return newRowExpression("ROW", dialect, expressions...)
}
// WRAP creates row expressions without ROW keyword `( expression1, expression2, ... )`.
func WRAP(dialect Dialect, expressions ...Expression) RowExpression {
return newRowExpression("", dialect, expressions...)
}
// RowExp serves as a wrapper for an arbitrary expression, treating it as a row expression.
// This enables the Go compiler to interpret any expression as a row expression
// Note: This does not modify the generated SQL builder output by adding a SQL CAST operation.
func RowExp(expression Expression) RowExpression {
rowExpressionWrap := rowExpressionWrapper{Expression: expression}
rowExpressionWrap.rowInterfaceImpl.parent = &rowExpressionWrap
return &rowExpressionWrap
}

View File

@@ -0,0 +1,71 @@
package jet
// RowLock is interface for SELECT statement row lock types
type RowLock interface {
Serializer
OF(...Table) RowLock
NOWAIT() RowLock
SKIP_LOCKED() RowLock
}
type selectLockImpl struct {
lockStrength string
of []Table
noWait, skipLocked bool
}
// NewRowLock creates new RowLock
func NewRowLock(name string) func() RowLock {
return func() RowLock {
return newSelectLock(name)
}
}
func newSelectLock(lockStrength string) *selectLockImpl {
return &selectLockImpl{lockStrength: lockStrength}
}
func (s *selectLockImpl) OF(tables ...Table) RowLock {
s.of = tables
return s
}
func (s *selectLockImpl) NOWAIT() RowLock {
s.noWait = true
return s
}
func (s *selectLockImpl) SKIP_LOCKED() RowLock {
s.skipLocked = true
return s
}
func (s *selectLockImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString(s.lockStrength)
if len(s.of) > 0 {
out.WriteString("OF")
for i, of := range s.of {
if i > 0 {
out.WriteString(", ")
}
table := of.Alias()
if table == "" {
table = of.TableName()
}
out.WriteIdentifier(table)
}
}
if s.noWait {
out.WriteString("NOWAIT")
}
if s.skipLocked {
out.WriteString("SKIP LOCKED")
}
}

View File

@@ -0,0 +1,78 @@
package jet
// SelectTable is interface for SELECT sub-queries
type SelectTable interface {
SerializerHasProjections
Alias() string
AllColumns() ProjectionList
}
type selectTableImpl struct {
Statement SerializerHasProjections
alias string
columnAliases []ColumnExpression
}
// NewSelectTable func
func NewSelectTable(selectStmt SerializerHasProjections, alias string, columnAliases []ColumnExpression) selectTableImpl {
selectTable := selectTableImpl{
Statement: selectStmt,
alias: alias,
columnAliases: columnAliases,
}
for _, column := range selectTable.columnAliases {
column.setSubQuery(selectTable)
}
return selectTable
}
func (s selectTableImpl) projections() ProjectionList {
return s.Statement.projections()
}
func (s selectTableImpl) Alias() string {
return s.alias
}
func (s selectTableImpl) AllColumns() ProjectionList {
if len(s.columnAliases) > 0 {
return ColumnListToProjectionList(s.columnAliases)
}
projectionList := s.projections().fromImpl(s)
return projectionList.(ProjectionList)
}
func (s selectTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
s.Statement.serialize(statement, out)
out.WriteString("AS")
out.WriteIdentifier(s.alias)
if len(s.columnAliases) > 0 {
out.WriteByte('(')
SerializeColumnExpressionNames(s.columnAliases, out)
out.WriteByte(')')
}
}
// --------------------------------------
type lateralImpl struct {
selectTableImpl
}
// NewLateral creates new lateral expression from select statement with alias
func NewLateral(selectStmt SerializerStatement, alias string) SelectTable {
return lateralImpl{selectTableImpl: NewSelectTable(selectStmt, alias, nil)}
}
func (s lateralImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("LATERAL")
s.Statement.serialize(statement, out)
out.WriteString("AS")
out.WriteIdentifier(s.alias)
}

View File

@@ -0,0 +1,109 @@
package jet
// SerializeOption type
type SerializeOption int
// Serialize options
const (
NoWrap SerializeOption = iota
SkipNewLine
Ident
fallTroughOptions // fall trough options
ShortName
)
// WithFallTrough extends existing serialize options with additional
func (s SerializeOption) WithFallTrough(options []SerializeOption) []SerializeOption {
return append(FallTrough(options), s)
}
// StatementType is type of the SQL statement
type StatementType string
// Statement types
const (
SelectStatementType StatementType = "SELECT"
InsertStatementType StatementType = "INSERT"
UpdateStatementType StatementType = "UPDATE"
DeleteStatementType StatementType = "DELETE"
SetStatementType StatementType = "SET"
LockStatementType StatementType = "LOCK"
UnLockStatementType StatementType = "UNLOCK"
WithStatementType StatementType = "WITH"
)
// Serializer interface
type Serializer interface {
serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption)
}
// Serialize func
func Serialize(exp Serializer, statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
exp.serialize(statementType, out, options...)
}
func SerializeForOrderBy(exp Expression, statementType StatementType, out *SQLBuilder) {
exp.serializeForOrderBy(statementType, out)
}
func contains(options []SerializeOption, option SerializeOption) bool {
for _, opt := range options {
if opt == option {
return true
}
}
return false
}
// FallTrough filters fall-trough options from the list
func FallTrough(options []SerializeOption) []SerializeOption {
var ret []SerializeOption
for _, option := range options {
if option > fallTroughOptions {
ret = append(ret, option)
}
}
return ret
}
// ListSerializer serializes list of serializers with separator
type ListSerializer struct {
Serializers []Serializer
Separator string
}
func (s ListSerializer) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
for i, ser := range s.Serializers {
if i > 0 {
out.WriteString(s.Separator)
}
ser.serialize(statement, out, FallTrough(options)...)
}
}
// NewSerializerClauseImpl is constructor for Seralizer with list of clauses
func NewSerializerClauseImpl(clauses ...Clause) Serializer {
return &serializerImpl{Clauses: clauses}
}
type serializerImpl struct {
Clauses []Clause
}
func (s serializerImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
for _, clause := range s.Clauses {
clause.Serialize(statement, out, FallTrough(options)...)
}
}
// Token can be used to construct complex custom expressions
type Token string
func (t Token) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString(string(t))
}

View File

@@ -0,0 +1,303 @@
package jet
import (
"bytes"
"database/sql/driver"
"fmt"
"github.com/go-jet/jet/v2/internal/3rdparty/pq"
"github.com/go-jet/jet/v2/internal/utils/is"
"github.com/google/uuid"
"reflect"
"sort"
"strconv"
"strings"
"time"
"unicode"
)
// SQLBuilder generates output SQL
type SQLBuilder struct {
Dialect Dialect
Buff bytes.Buffer
Args []interface{}
lastChar byte
ident int
Debug bool
}
const tabSize = 4
const defaultIdent = 5
// IncreaseIdent adds ident or defaultIdent number of spaces to each new line
func (s *SQLBuilder) IncreaseIdent(ident ...int) {
if len(ident) > 0 {
s.ident += ident[0]
} else {
s.ident += defaultIdent
}
}
// DecreaseIdent removes ident or defaultIdent number of spaces for each new line
func (s *SQLBuilder) DecreaseIdent(ident ...int) {
toDecrease := defaultIdent
if len(ident) > 0 {
toDecrease = ident[0]
}
if s.ident < toDecrease {
s.ident = 0
}
s.ident -= toDecrease
}
// WriteProjections func
func (s *SQLBuilder) WriteProjections(statement StatementType, projections []Projection) {
s.IncreaseIdent()
SerializeProjectionList(statement, projections, s)
s.DecreaseIdent()
}
// NewLine adds new line to output SQL
func (s *SQLBuilder) NewLine() {
s.write([]byte{'\n'})
s.write(bytes.Repeat([]byte{' '}, s.ident))
}
func (s *SQLBuilder) write(data []byte) {
if len(data) == 0 {
return
}
if !isPreSeparator(s.lastChar) && !isPostSeparator(data[0]) && s.Buff.Len() > 0 {
s.Buff.WriteByte(' ')
}
s.Buff.Write(data)
s.lastChar = data[len(data)-1]
}
func isPreSeparator(b byte) bool {
return b == ' ' || b == '.' || b == ',' || b == '(' || b == '\n' || b == ':'
}
func isPostSeparator(b byte) bool {
return b == ' ' || b == '.' || b == ',' || b == ')' || b == '\n' || b == ':'
}
// WriteAlias is used to add alias to output SQL
func (s *SQLBuilder) WriteAlias(str string) {
aliasQuoteChar := string(s.Dialect.AliasQuoteChar())
s.WriteString(aliasQuoteChar + str + aliasQuoteChar)
}
// WriteString writes sting to output SQL
func (s *SQLBuilder) WriteString(str string) {
s.write([]byte(str))
}
// WriteIdentifier adds identifier to output SQL
func (s *SQLBuilder) WriteIdentifier(name string, alwaysQuote ...bool) {
if s.shouldQuote(name, alwaysQuote...) {
identQuoteChar := string(s.Dialect.IdentifierQuoteChar())
s.WriteString(identQuoteChar + name + identQuoteChar)
} else {
s.WriteString(name)
}
}
func (s *SQLBuilder) shouldQuote(name string, alwaysQuote ...bool) bool {
return s.Dialect.IsReservedWord(name) || shouldQuoteIdentifier(name) || len(alwaysQuote) > 0
}
// WriteByte writes byte to output SQL
func (s *SQLBuilder) WriteByte(b byte) {
s.write([]byte{b})
}
func (s *SQLBuilder) finalize() (string, []interface{}) {
return s.Buff.String() + ";\n", s.Args
}
func (s *SQLBuilder) insertConstantArgument(arg interface{}) {
s.WriteString(argToString(arg))
}
func (s *SQLBuilder) insertParametrizedArgument(arg interface{}) {
if s.Debug {
s.insertConstantArgument(arg)
return
}
s.Args = append(s.Args, arg)
argPlaceholder := s.Dialect.ArgumentPlaceholder()(len(s.Args))
s.WriteString(argPlaceholder)
}
func (s *SQLBuilder) insertRawQuery(raw string, namedArg map[string]interface{}) {
type namedArgumentPosition struct {
Name string
Value interface{}
Position int
}
var namedArgumentPositions []namedArgumentPosition
for namedArg, value := range namedArg {
rawCopy := raw
rawIndex := 0
exists := false
// one named argument can occur multiple times inside raw string
for {
index := strings.Index(rawCopy, namedArg)
if index == -1 {
break
}
exists = true
namedArgumentPositions = append(namedArgumentPositions, namedArgumentPosition{
Name: namedArg,
Value: value,
Position: rawIndex + index,
})
rawCopy = rawCopy[index+len(namedArg):]
rawIndex += index + len(namedArg)
}
if !exists {
panic("jet: named argument '" + namedArg + "' does not appear in raw query")
}
}
sort.Slice(namedArgumentPositions, func(i, j int) bool {
return namedArgumentPositions[i].Position < namedArgumentPositions[j].Position
})
for _, namedArgumentPos := range namedArgumentPositions {
// if named argument does not exists in raw string do not add argument to the list of arguments
// It can happen if the same argument occurs multiple times in postgres query.
if !strings.Contains(raw, namedArgumentPos.Name) {
continue
}
s.Args = append(s.Args, namedArgumentPos.Value)
currentArgNum := len(s.Args)
placeholder := s.Dialect.ArgumentPlaceholder()(currentArgNum)
// if placeholder is not unique identifier ($1, $2, etc..), we will replace just one occurrence of the argument
toReplace := -1 // all occurrences
if placeholder == "?" {
toReplace = 1 // just one occurrence
}
if s.Debug {
placeholder = argToString(namedArgumentPos.Value)
}
raw = strings.Replace(raw, namedArgumentPos.Name, placeholder, toReplace)
}
s.WriteString(raw)
}
func argToString(value interface{}) string {
if is.Nil(value) {
return "NULL"
}
switch bindVal := value.(type) {
case bool:
if bindVal {
return "TRUE"
}
return "FALSE"
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return integerTypesToString(bindVal)
case float32:
return strconv.FormatFloat(float64(bindVal), 'f', -1, 64)
case float64:
return strconv.FormatFloat(float64(bindVal), 'f', -1, 64)
case string:
return stringQuote(bindVal)
case []byte:
return stringQuote(string(bindVal))
case uuid.UUID:
return stringQuote(bindVal.String())
case time.Time:
return stringQuote(string(pq.FormatTimestamp(bindVal)))
default:
if strBindValue, ok := bindVal.(fmt.Stringer); ok {
return stringQuote(strBindValue.String())
}
if valuer, ok := bindVal.(driver.Valuer); ok {
val, err := valuer.Value()
if err != nil {
// If valuer for some reason returns an error, we return error string representation.
// This is fine because argToString is called only from DebugSQL, and DebugSQL shouldn't be used in production.
return err.Error()
}
return argToString(val)
}
panic(fmt.Sprintf("jet: %s type can not be used as SQL query parameter", reflect.TypeOf(value).String()))
}
}
func integerTypesToString(value interface{}) string {
switch bindVal := value.(type) {
case int:
return strconv.FormatInt(int64(bindVal), 10)
case uint:
return strconv.FormatUint(uint64(bindVal), 10)
case int8:
return strconv.FormatInt(int64(bindVal), 10)
case uint8:
return strconv.FormatUint(uint64(bindVal), 10)
case int16:
return strconv.FormatInt(int64(bindVal), 10)
case uint16:
return strconv.FormatUint(uint64(bindVal), 10)
case int32:
return strconv.FormatInt(int64(bindVal), 10)
case uint32:
return strconv.FormatUint(uint64(bindVal), 10)
case int64:
return strconv.FormatInt(bindVal, 10)
case uint64:
return strconv.FormatUint(bindVal, 10)
}
panic("jet: Unsupported integer type: " + reflect.TypeOf(value).String())
}
func shouldQuoteIdentifier(identifier string) bool {
_, err := strconv.ParseInt(identifier, 10, 64)
if err == nil { // if it is a number we should quote it
return true
}
// check if contains non ascii characters
for _, c := range identifier {
if unicode.IsNumber(c) || c == '_' {
continue
}
if c > unicode.MaxASCII || !unicode.IsLetter(c) || unicode.IsUpper(c) {
return true
}
}
return false
}
func stringQuote(value string) string {
return `'` + strings.Replace(value, "'", "''", -1) + `'`
}

View File

@@ -0,0 +1,269 @@
package jet
import (
"context"
"database/sql"
"github.com/go-jet/jet/v2/qrm"
"time"
)
// Statement is common interface for all statements(SELECT, INSERT, UPDATE, DELETE, LOCK)
type Statement interface {
// Sql returns parametrized sql query with list of arguments.
Sql() (query string, args []interface{})
// DebugSql returns debug query where every parametrized placeholder is replaced with its argument string representation.
// Do not use it in production. Use it only for debug purposes.
DebugSql() (query string)
// Query executes statement over database connection/transaction db and stores row results in destination.
// Destination can be either pointer to struct or pointer to a slice.
// If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows.
Query(db qrm.Queryable, destination interface{}) error
// QueryContext executes statement with a context over database connection/transaction db and stores row result in destination.
// Destination can be either pointer to struct or pointer to a slice.
// If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows.
QueryContext(ctx context.Context, db qrm.Queryable, destination interface{}) error
// Exec executes statement over db connection/transaction without returning any rows.
Exec(db qrm.Executable) (sql.Result, error)
// ExecContext executes statement with context over db connection/transaction without returning any rows.
ExecContext(ctx context.Context, db qrm.Executable) (sql.Result, error)
// Rows executes statements over db connection/transaction and returns rows
Rows(ctx context.Context, db qrm.Queryable) (*Rows, error)
}
// Rows wraps sql.Rows type with a support for query result mapping
type Rows struct {
*sql.Rows
scanContext *qrm.ScanContext
}
// Scan will map the Row values into struct destination
func (r *Rows) Scan(destination interface{}) error {
return qrm.ScanOneRowToDest(r.scanContext, r.Rows, destination)
}
// SerializerStatement interface
type SerializerStatement interface {
Serializer
Statement
HasProjections
}
// HasProjections interface
type HasProjections interface {
projections() ProjectionList
}
// SerializerHasProjections interface is combination of Serializer and HasProjections interface
type SerializerHasProjections interface {
Serializer
HasProjections
}
// serializerStatementInterfaceImpl struct
type serializerStatementInterfaceImpl struct {
dialect Dialect
statementType StatementType
parent SerializerStatement
}
func (s *serializerStatementInterfaceImpl) Sql() (query string, args []interface{}) {
queryData := &SQLBuilder{Dialect: s.dialect}
s.parent.serialize(s.statementType, queryData, NoWrap)
query, args = queryData.finalize()
return
}
func (s *serializerStatementInterfaceImpl) DebugSql() (query string) {
sqlBuilder := &SQLBuilder{Dialect: s.dialect, Debug: true}
s.parent.serialize(s.statementType, sqlBuilder, NoWrap)
query, _ = sqlBuilder.finalize()
return
}
func (s *serializerStatementInterfaceImpl) Query(db qrm.Queryable, destination interface{}) error {
return s.QueryContext(context.Background(), db, destination)
}
func (s *serializerStatementInterfaceImpl) QueryContext(ctx context.Context, db qrm.Queryable, destination interface{}) error {
query, args := s.Sql()
callLogger(ctx, s)
var rowsProcessed int64
var err error
duration := duration(func() {
rowsProcessed, err = qrm.Query(ctx, db, query, args, destination)
})
callQueryLoggerFunc(ctx, QueryInfo{
Statement: s,
RowsProcessed: rowsProcessed,
Duration: duration,
Err: err,
})
return err
}
func (s *serializerStatementInterfaceImpl) Exec(db qrm.Executable) (res sql.Result, err error) {
return s.ExecContext(context.Background(), db)
}
func (s *serializerStatementInterfaceImpl) ExecContext(ctx context.Context, db qrm.Executable) (res sql.Result, err error) {
query, args := s.Sql()
callLogger(ctx, s)
duration := duration(func() {
res, err = db.ExecContext(ctx, query, args...)
})
var rowsAffected int64
if err == nil {
rowsAffected, _ = res.RowsAffected()
}
callQueryLoggerFunc(ctx, QueryInfo{
Statement: s,
RowsProcessed: rowsAffected,
Duration: duration,
Err: err,
})
return res, err
}
func (s *serializerStatementInterfaceImpl) Rows(ctx context.Context, db qrm.Queryable) (*Rows, error) {
query, args := s.Sql()
callLogger(ctx, s)
var rows *sql.Rows
var err error
duration := duration(func() {
rows, err = db.QueryContext(ctx, query, args...)
})
callQueryLoggerFunc(ctx, QueryInfo{
Statement: s,
Duration: duration,
Err: err,
})
if err != nil {
return nil, err
}
scanContext, err := qrm.NewScanContext(rows)
if err != nil {
return nil, err
}
return &Rows{
Rows: rows,
scanContext: scanContext,
}, nil
}
func duration(f func()) time.Duration {
start := time.Now()
f()
return time.Since(start)
}
// ExpressionStatement interfacess
type ExpressionStatement interface {
Expression
Statement
HasProjections
}
// NewExpressionStatementImpl creates new expression statement
func NewExpressionStatementImpl(Dialect Dialect, statementType StatementType, parent ExpressionStatement, clauses ...Clause) ExpressionStatement {
return &expressionStatementImpl{
ExpressionInterfaceImpl{Parent: parent},
statementImpl{
serializerStatementInterfaceImpl: serializerStatementInterfaceImpl{
parent: parent,
dialect: Dialect,
statementType: statementType,
},
Clauses: clauses,
},
}
}
type expressionStatementImpl struct {
ExpressionInterfaceImpl
statementImpl
}
func (s *expressionStatementImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
s.serialize(statement, out)
}
// NewStatementImpl creates new statementImpl
func NewStatementImpl(Dialect Dialect, statementType StatementType, parent SerializerStatement, clauses ...Clause) SerializerStatement {
return &statementImpl{
serializerStatementInterfaceImpl: serializerStatementInterfaceImpl{
parent: parent,
dialect: Dialect,
statementType: statementType,
},
Clauses: clauses,
}
}
type statementImpl struct {
serializerStatementInterfaceImpl
Clauses []Clause
}
func (s *statementImpl) projections() ProjectionList {
for _, clause := range s.Clauses {
if selectClause, ok := clause.(ClauseWithProjections); ok {
return selectClause.Projections()
}
}
return nil
}
func (s *statementImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if !contains(options, NoWrap) {
out.WriteString("(")
out.IncreaseIdent()
}
if contains(options, Ident) {
out.IncreaseIdent()
}
for _, clause := range s.Clauses {
clause.Serialize(s.statementType, out, FallTrough(options)...)
}
if contains(options, Ident) {
out.DecreaseIdent()
out.NewLine()
}
if !contains(options, NoWrap) {
out.DecreaseIdent()
out.NewLine()
out.WriteString(")")
}
}

View File

@@ -0,0 +1,115 @@
package jet
// StringExpression interface
type StringExpression interface {
Expression
EQ(rhs StringExpression) BoolExpression
NOT_EQ(rhs StringExpression) BoolExpression
IS_DISTINCT_FROM(rhs StringExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs StringExpression) BoolExpression
LT(rhs StringExpression) BoolExpression
LT_EQ(rhs StringExpression) BoolExpression
GT(rhs StringExpression) BoolExpression
GT_EQ(rhs StringExpression) BoolExpression
BETWEEN(min, max StringExpression) BoolExpression
NOT_BETWEEN(min, max StringExpression) BoolExpression
CONCAT(rhs Expression) StringExpression
LIKE(pattern StringExpression) BoolExpression
NOT_LIKE(pattern StringExpression) BoolExpression
REGEXP_LIKE(pattern StringExpression, caseSensitive ...bool) BoolExpression
NOT_REGEXP_LIKE(pattern StringExpression, caseSensitive ...bool) BoolExpression
}
type stringInterfaceImpl struct {
parent StringExpression
}
func (s *stringInterfaceImpl) EQ(rhs StringExpression) BoolExpression {
return Eq(s.parent, rhs)
}
func (s *stringInterfaceImpl) NOT_EQ(rhs StringExpression) BoolExpression {
return NotEq(s.parent, rhs)
}
func (s *stringInterfaceImpl) IS_DISTINCT_FROM(rhs StringExpression) BoolExpression {
return IsDistinctFrom(s.parent, rhs)
}
func (s *stringInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs StringExpression) BoolExpression {
return IsNotDistinctFrom(s.parent, rhs)
}
func (s *stringInterfaceImpl) GT(rhs StringExpression) BoolExpression {
return Gt(s.parent, rhs)
}
func (s *stringInterfaceImpl) GT_EQ(rhs StringExpression) BoolExpression {
return GtEq(s.parent, rhs)
}
func (s *stringInterfaceImpl) LT(rhs StringExpression) BoolExpression {
return Lt(s.parent, rhs)
}
func (s *stringInterfaceImpl) LT_EQ(rhs StringExpression) BoolExpression {
return LtEq(s.parent, rhs)
}
func (s *stringInterfaceImpl) BETWEEN(min, max StringExpression) BoolExpression {
return NewBetweenOperatorExpression(s.parent, min, max, false)
}
func (s *stringInterfaceImpl) NOT_BETWEEN(min, max StringExpression) BoolExpression {
return NewBetweenOperatorExpression(s.parent, min, max, true)
}
func (s *stringInterfaceImpl) CONCAT(rhs Expression) StringExpression {
return newBinaryStringOperatorExpression(s.parent, rhs, StringConcatOperator)
}
func (s *stringInterfaceImpl) LIKE(pattern StringExpression) BoolExpression {
return newBinaryBoolOperatorExpression(s.parent, pattern, "LIKE")
}
func (s *stringInterfaceImpl) NOT_LIKE(pattern StringExpression) BoolExpression {
return newBinaryBoolOperatorExpression(s.parent, pattern, "NOT LIKE")
}
func (s *stringInterfaceImpl) REGEXP_LIKE(pattern StringExpression, caseSensitive ...bool) BoolExpression {
return newBinaryBoolOperatorExpression(s.parent, pattern, StringRegexpLikeOperator, Bool(len(caseSensitive) > 0 && caseSensitive[0]))
}
func (s *stringInterfaceImpl) NOT_REGEXP_LIKE(pattern StringExpression, caseSensitive ...bool) BoolExpression {
return newBinaryBoolOperatorExpression(s.parent, pattern, StringNotRegexpLikeOperator, Bool(len(caseSensitive) > 0 && caseSensitive[0]))
}
// ---------------------------------------------------//
func newBinaryStringOperatorExpression(lhs, rhs Expression, operator string) StringExpression {
return StringExp(NewBinaryOperatorExpression(lhs, rhs, operator))
}
//---------------------------------------------------//
type stringExpressionWrapper struct {
stringInterfaceImpl
Expression
}
func newStringExpressionWrap(expression Expression) StringExpression {
stringExpressionWrap := stringExpressionWrapper{Expression: expression}
stringExpressionWrap.stringInterfaceImpl.parent = &stringExpressionWrap
return &stringExpressionWrap
}
// StringExp is string expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as string expression.
// Does not add sql cast to generated sql builder output.
func StringExp(expression Expression) StringExpression {
return newStringExpressionWrap(expression)
}

194
vendor/github.com/go-jet/jet/v2/internal/jet/table.go generated vendored Normal file
View File

@@ -0,0 +1,194 @@
package jet
import "github.com/go-jet/jet/v2/internal/utils/is"
// SerializerTable interface
type SerializerTable interface {
Serializer
Table
}
// Table interface
type Table interface {
columns() []Column
SchemaName() string
TableName() string
Alias() string
}
// NewTable creates new table with schema Name, table Name and list of columns
func NewTable(schemaName, name, alias string, columns ...ColumnExpression) SerializerTable {
t := tableImpl{
schemaName: schemaName,
name: name,
alias: alias,
columnList: columns,
}
columnTableName := name
if alias != "" {
columnTableName = alias
}
for _, c := range columns {
c.setTableName(columnTableName)
}
return &t
}
type tableImpl struct {
schemaName string
name string
alias string
columnList []ColumnExpression
}
func (t *tableImpl) SchemaName() string {
return t.schemaName
}
func (t *tableImpl) TableName() string {
return t.name
}
func (t *tableImpl) columns() []Column {
ret := []Column{}
for _, col := range t.columnList {
ret = append(ret, col)
}
return ret
}
func (t *tableImpl) Alias() string {
return t.alias
}
func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if t == nil {
panic("jet: tableImpl is nil")
}
// Use default schema if the schema name is not set
if len(t.schemaName) > 0 {
out.WriteIdentifier(t.schemaName)
out.WriteString(".")
}
out.WriteIdentifier(t.name)
if len(t.alias) > 0 {
out.WriteString("AS")
out.WriteIdentifier(t.alias)
}
}
// JoinType is type of table join
type JoinType int
// Table join types
const (
InnerJoin JoinType = iota
LeftJoin
RightJoin
FullJoin
CrossJoin
)
// Join expressions are pseudo readable tables.
type joinTableImpl struct {
lhs Serializer
rhs Serializer
joinType JoinType
onCondition BoolExpression
}
// JoinTable interface
type JoinTable SerializerTable
// NewJoinTable creates new join table
func NewJoinTable(lhs Serializer, rhs Serializer, joinType JoinType, onCondition BoolExpression) JoinTable {
joinTable := joinTableImpl{
lhs: lhs,
rhs: rhs,
joinType: joinType,
onCondition: onCondition,
}
return &joinTable
}
func (t *joinTableImpl) SchemaName() string {
if table, ok := t.lhs.(Table); ok {
return table.SchemaName()
}
return ""
}
func (t *joinTableImpl) TableName() string {
return ""
}
func (t *joinTableImpl) columns() []Column {
var ret []Column
if lhsTable, ok := t.lhs.(Table); ok {
ret = append(ret, lhsTable.columns()...)
}
if rhsTable, ok := t.rhs.(Table); ok {
ret = append(ret, rhsTable.columns()...)
}
return ret
}
func (t *joinTableImpl) Alias() string {
return ""
}
func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if t == nil {
panic("jet: Join table is nil. ")
}
if is.Nil(t.lhs) {
panic("jet: left hand side of join operation is nil table")
}
t.lhs.serialize(statement, out, FallTrough(options)...)
out.NewLine()
switch t.joinType {
case InnerJoin:
out.WriteString("INNER JOIN")
case LeftJoin:
out.WriteString("LEFT JOIN")
case RightJoin:
out.WriteString("RIGHT JOIN")
case FullJoin:
out.WriteString("FULL JOIN")
case CrossJoin:
out.WriteString("CROSS JOIN")
}
if is.Nil(t.rhs) {
panic("jet: right hand side of join operation is nil table")
}
t.rhs.serialize(statement, out)
if t.onCondition == nil && t.joinType != CrossJoin {
panic("jet: join condition is nil")
}
if t.onCondition != nil {
out.WriteString("ON")
t.onCondition.serialize(statement, out)
}
}

View File

@@ -0,0 +1,91 @@
package jet
import (
"github.com/stretchr/testify/require"
"strconv"
"testing"
)
var defaultDialect = NewDialect(DialectParams{ // just for tests
AliasQuoteChar: '"',
IdentifierQuoteChar: '"',
ArgumentPlaceholder: func(ord int) string {
return "$" + strconv.Itoa(ord)
},
})
var (
table1Col1 = IntegerColumn("col1")
table1ColInt = IntegerColumn("col_int")
table1ColFloat = FloatColumn("col_float")
table1Col3 = IntegerColumn("col3")
table1ColTime = TimeColumn("col_time")
table1ColTimez = TimezColumn("col_timez")
table1ColTimestamp = TimestampColumn("col_timestamp")
table1ColTimestampz = TimestampzColumn("col_timestampz")
table1ColBool = BoolColumn("col_bool")
table1ColDate = DateColumn("col_date")
table1ColRange = RangeColumn[Int8Expression]("col_range")
)
var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1Col3, table1ColTime, table1ColTimez, table1ColBool, table1ColDate, table1ColRange, table1ColTimestamp, table1ColTimestampz)
var (
table2Col3 = IntegerColumn("col3")
table2Col4 = IntegerColumn("col4")
table2ColInt = IntegerColumn("col_int")
table2ColFloat = FloatColumn("col_float")
table2ColStr = StringColumn("col_str")
table2ColBool = BoolColumn("col_bool")
table2ColTime = TimeColumn("col_time")
table2ColTimez = TimezColumn("col_timez")
table2ColTimestamp = TimestampColumn("col_timestamp")
table2ColTimestampz = TimestampzColumn("col_timestampz")
table2ColDate = DateColumn("col_date")
table2ColRange = RangeColumn[Int8Expression]("col_range")
)
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColRange, table2ColTimestamp, table2ColTimestampz)
var (
table3Col1 = IntegerColumn("col1")
table3ColInt = IntegerColumn("col_int")
table3StrCol = StringColumn("col2")
)
var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol)
func assertClauseSerialize(t *testing.T, clause Serializer, query string, args ...interface{}) {
out := SQLBuilder{Dialect: defaultDialect}
clause.serialize(SelectStatementType, &out)
//fmt.Println(out.Buff.String())
require.Equal(t, out.Buff.String(), query)
require.Equal(t, out.Args, args)
}
func assertClauseSerializeErr(t *testing.T, clause Serializer, errString string) {
defer func() {
r := recover()
require.Equal(t, r, errString)
}()
out := SQLBuilder{Dialect: defaultDialect}
clause.serialize(SelectStatementType, &out)
}
func assertClauseDebugSerialize(t *testing.T, clause Serializer, query string, args ...interface{}) {
out := SQLBuilder{Dialect: defaultDialect, Debug: true}
clause.serialize(SelectStatementType, &out)
//fmt.Println(out.Buff.String())
require.Equal(t, out.Buff.String(), query)
require.Equal(t, out.Args, args)
}
func assertProjectionSerialize(t *testing.T, projection Projection, query string, args ...interface{}) {
out := SQLBuilder{Dialect: defaultDialect}
projection.serializeForProjection(SelectStatementType, &out)
require.Equal(t, out.Buff.String(), query)
require.Equal(t, out.Args, args)
}

View File

@@ -0,0 +1,93 @@
package jet
// TimeExpression interface
type TimeExpression interface {
Expression
EQ(rhs TimeExpression) BoolExpression
NOT_EQ(rhs TimeExpression) BoolExpression
IS_DISTINCT_FROM(rhs TimeExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs TimeExpression) BoolExpression
LT(rhs TimeExpression) BoolExpression
LT_EQ(rhs TimeExpression) BoolExpression
GT(rhs TimeExpression) BoolExpression
GT_EQ(rhs TimeExpression) BoolExpression
BETWEEN(min, max TimeExpression) BoolExpression
NOT_BETWEEN(min, max TimeExpression) BoolExpression
ADD(rhs Interval) TimeExpression
SUB(rhs Interval) TimeExpression
}
type timeInterfaceImpl struct {
parent TimeExpression
}
func (t *timeInterfaceImpl) EQ(rhs TimeExpression) BoolExpression {
return Eq(t.parent, rhs)
}
func (t *timeInterfaceImpl) NOT_EQ(rhs TimeExpression) BoolExpression {
return NotEq(t.parent, rhs)
}
func (t *timeInterfaceImpl) IS_DISTINCT_FROM(rhs TimeExpression) BoolExpression {
return IsDistinctFrom(t.parent, rhs)
}
func (t *timeInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimeExpression) BoolExpression {
return IsNotDistinctFrom(t.parent, rhs)
}
func (t *timeInterfaceImpl) LT(rhs TimeExpression) BoolExpression {
return Lt(t.parent, rhs)
}
func (t *timeInterfaceImpl) LT_EQ(rhs TimeExpression) BoolExpression {
return LtEq(t.parent, rhs)
}
func (t *timeInterfaceImpl) GT(rhs TimeExpression) BoolExpression {
return Gt(t.parent, rhs)
}
func (t *timeInterfaceImpl) GT_EQ(rhs TimeExpression) BoolExpression {
return GtEq(t.parent, rhs)
}
func (t *timeInterfaceImpl) BETWEEN(min, max TimeExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, false)
}
func (t *timeInterfaceImpl) NOT_BETWEEN(min, max TimeExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, true)
}
func (t *timeInterfaceImpl) ADD(rhs Interval) TimeExpression {
return TimeExp(Add(t.parent, rhs))
}
func (t *timeInterfaceImpl) SUB(rhs Interval) TimeExpression {
return TimeExp(Sub(t.parent, rhs))
}
//---------------------------------------------------//
type timeExpressionWrapper struct {
timeInterfaceImpl
Expression
}
func newTimeExpressionWrap(expression Expression) TimeExpression {
timeExpressionWrap := timeExpressionWrapper{Expression: expression}
timeExpressionWrap.timeInterfaceImpl.parent = &timeExpressionWrap
return &timeExpressionWrap
}
// TimeExp is time expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as time expression.
// Does not add sql cast to generated sql builder output.
func TimeExp(expression Expression) TimeExpression {
return newTimeExpressionWrap(expression)
}

View File

@@ -0,0 +1,93 @@
package jet
// TimestampExpression interface
type TimestampExpression interface {
Expression
EQ(rhs TimestampExpression) BoolExpression
NOT_EQ(rhs TimestampExpression) BoolExpression
IS_DISTINCT_FROM(rhs TimestampExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs TimestampExpression) BoolExpression
LT(rhs TimestampExpression) BoolExpression
LT_EQ(rhs TimestampExpression) BoolExpression
GT(rhs TimestampExpression) BoolExpression
GT_EQ(rhs TimestampExpression) BoolExpression
BETWEEN(min, max TimestampExpression) BoolExpression
NOT_BETWEEN(min, max TimestampExpression) BoolExpression
ADD(rhs Interval) TimestampExpression
SUB(rhs Interval) TimestampExpression
}
type timestampInterfaceImpl struct {
parent TimestampExpression
}
func (t *timestampInterfaceImpl) EQ(rhs TimestampExpression) BoolExpression {
return Eq(t.parent, rhs)
}
func (t *timestampInterfaceImpl) NOT_EQ(rhs TimestampExpression) BoolExpression {
return NotEq(t.parent, rhs)
}
func (t *timestampInterfaceImpl) IS_DISTINCT_FROM(rhs TimestampExpression) BoolExpression {
return IsDistinctFrom(t.parent, rhs)
}
func (t *timestampInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimestampExpression) BoolExpression {
return IsNotDistinctFrom(t.parent, rhs)
}
func (t *timestampInterfaceImpl) LT(rhs TimestampExpression) BoolExpression {
return Lt(t.parent, rhs)
}
func (t *timestampInterfaceImpl) LT_EQ(rhs TimestampExpression) BoolExpression {
return LtEq(t.parent, rhs)
}
func (t *timestampInterfaceImpl) GT(rhs TimestampExpression) BoolExpression {
return Gt(t.parent, rhs)
}
func (t *timestampInterfaceImpl) GT_EQ(rhs TimestampExpression) BoolExpression {
return GtEq(t.parent, rhs)
}
func (t *timestampInterfaceImpl) BETWEEN(min, max TimestampExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, false)
}
func (t *timestampInterfaceImpl) NOT_BETWEEN(min, max TimestampExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, true)
}
func (t *timestampInterfaceImpl) ADD(rhs Interval) TimestampExpression {
return TimestampExp(Add(t.parent, rhs))
}
func (t *timestampInterfaceImpl) SUB(rhs Interval) TimestampExpression {
return TimestampExp(Sub(t.parent, rhs))
}
//-------------------------------------------------
type timestampExpressionWrapper struct {
timestampInterfaceImpl
Expression
}
func newTimestampExpressionWrap(expression Expression) TimestampExpression {
timestampExpressionWrap := timestampExpressionWrapper{Expression: expression}
timestampExpressionWrap.timestampInterfaceImpl.parent = &timestampExpressionWrap
return &timestampExpressionWrap
}
// TimestampExp is timestamp expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as timestamp expression.
// Does not add sql cast to generated sql builder output.
func TimestampExp(expression Expression) TimestampExpression {
return newTimestampExpressionWrap(expression)
}

View File

@@ -0,0 +1,93 @@
package jet
// TimestampzExpression interface
type TimestampzExpression interface {
Expression
EQ(rhs TimestampzExpression) BoolExpression
NOT_EQ(rhs TimestampzExpression) BoolExpression
IS_DISTINCT_FROM(rhs TimestampzExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs TimestampzExpression) BoolExpression
LT(rhs TimestampzExpression) BoolExpression
LT_EQ(rhs TimestampzExpression) BoolExpression
GT(rhs TimestampzExpression) BoolExpression
GT_EQ(rhs TimestampzExpression) BoolExpression
BETWEEN(min, max TimestampzExpression) BoolExpression
NOT_BETWEEN(min, max TimestampzExpression) BoolExpression
ADD(rhs Interval) TimestampzExpression
SUB(rhs Interval) TimestampzExpression
}
type timestampzInterfaceImpl struct {
parent TimestampzExpression
}
func (t *timestampzInterfaceImpl) EQ(rhs TimestampzExpression) BoolExpression {
return Eq(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) NOT_EQ(rhs TimestampzExpression) BoolExpression {
return NotEq(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) IS_DISTINCT_FROM(rhs TimestampzExpression) BoolExpression {
return IsDistinctFrom(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimestampzExpression) BoolExpression {
return IsNotDistinctFrom(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) LT(rhs TimestampzExpression) BoolExpression {
return Lt(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) LT_EQ(rhs TimestampzExpression) BoolExpression {
return LtEq(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) GT(rhs TimestampzExpression) BoolExpression {
return Gt(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) GT_EQ(rhs TimestampzExpression) BoolExpression {
return GtEq(t.parent, rhs)
}
func (t *timestampzInterfaceImpl) BETWEEN(min, max TimestampzExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, false)
}
func (t *timestampzInterfaceImpl) NOT_BETWEEN(min, max TimestampzExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, true)
}
func (t *timestampzInterfaceImpl) ADD(rhs Interval) TimestampzExpression {
return TimestampzExp(Add(t.parent, rhs))
}
func (t *timestampzInterfaceImpl) SUB(rhs Interval) TimestampzExpression {
return TimestampzExp(Sub(t.parent, rhs))
}
//-------------------------------------------------
type timestampzExpressionWrapper struct {
timestampzInterfaceImpl
Expression
}
func newTimestampzExpressionWrap(expression Expression) TimestampzExpression {
timestampzExpressionWrap := timestampzExpressionWrapper{Expression: expression}
timestampzExpressionWrap.timestampzInterfaceImpl.parent = &timestampzExpressionWrap
return &timestampzExpressionWrap
}
// TimestampzExp is timestamp with time zone expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as timestamp with time zone expression.
// Does not add sql cast to generated sql builder output.
func TimestampzExp(expression Expression) TimestampzExpression {
return newTimestampzExpressionWrap(expression)
}

View File

@@ -0,0 +1,93 @@
package jet
// TimezExpression interface for 'time with time zone' types
type TimezExpression interface {
Expression
EQ(rhs TimezExpression) BoolExpression
NOT_EQ(rhs TimezExpression) BoolExpression
IS_DISTINCT_FROM(rhs TimezExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs TimezExpression) BoolExpression
LT(rhs TimezExpression) BoolExpression
LT_EQ(rhs TimezExpression) BoolExpression
GT(rhs TimezExpression) BoolExpression
GT_EQ(rhs TimezExpression) BoolExpression
BETWEEN(min, max TimezExpression) BoolExpression
NOT_BETWEEN(min, max TimezExpression) BoolExpression
ADD(rhs Interval) TimezExpression
SUB(rhs Interval) TimezExpression
}
type timezInterfaceImpl struct {
parent TimezExpression
}
func (t *timezInterfaceImpl) EQ(rhs TimezExpression) BoolExpression {
return Eq(t.parent, rhs)
}
func (t *timezInterfaceImpl) NOT_EQ(rhs TimezExpression) BoolExpression {
return NotEq(t.parent, rhs)
}
func (t *timezInterfaceImpl) IS_DISTINCT_FROM(rhs TimezExpression) BoolExpression {
return IsDistinctFrom(t.parent, rhs)
}
func (t *timezInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs TimezExpression) BoolExpression {
return IsNotDistinctFrom(t.parent, rhs)
}
func (t *timezInterfaceImpl) LT(rhs TimezExpression) BoolExpression {
return Lt(t.parent, rhs)
}
func (t *timezInterfaceImpl) LT_EQ(rhs TimezExpression) BoolExpression {
return LtEq(t.parent, rhs)
}
func (t *timezInterfaceImpl) GT(rhs TimezExpression) BoolExpression {
return Gt(t.parent, rhs)
}
func (t *timezInterfaceImpl) GT_EQ(rhs TimezExpression) BoolExpression {
return GtEq(t.parent, rhs)
}
func (t *timezInterfaceImpl) BETWEEN(min, max TimezExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, false)
}
func (t *timezInterfaceImpl) NOT_BETWEEN(min, max TimezExpression) BoolExpression {
return NewBetweenOperatorExpression(t.parent, min, max, true)
}
func (t *timezInterfaceImpl) ADD(rhs Interval) TimezExpression {
return TimezExp(Add(t.parent, rhs))
}
func (t *timezInterfaceImpl) SUB(rhs Interval) TimezExpression {
return TimezExp(Sub(t.parent, rhs))
}
//---------------------------------------------------//
type timezExpressionWrapper struct {
timezInterfaceImpl
Expression
}
func newTimezExpressionWrap(expression Expression) TimezExpression {
timezExpressionWrap := timezExpressionWrapper{Expression: expression}
timezExpressionWrap.timezInterfaceImpl.parent = &timezExpressionWrap
return &timezExpressionWrap
}
// TimezExp is time with time zone expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as time with time zone expression.
// Does not add sql cast to generated sql builder output.
func TimezExp(expression Expression) TimezExpression {
return newTimezExpressionWrap(expression)
}

293
vendor/github.com/go-jet/jet/v2/internal/jet/utils.go generated vendored Normal file
View File

@@ -0,0 +1,293 @@
package jet
import (
"reflect"
"strings"
"github.com/go-jet/jet/v2/internal/utils/dbidentifier"
"github.com/go-jet/jet/v2/internal/utils/must"
)
// SerializeClauseList func
func SerializeClauseList(statement StatementType, clauses []Serializer, out *SQLBuilder) {
for i, c := range clauses {
if i > 0 {
out.WriteString(", ")
}
if c == nil {
panic("jet: nil clause")
}
c.serialize(statement, out)
}
}
func serializeExpressionList(
statement StatementType,
expressions []Expression,
separator string,
out *SQLBuilder,
options ...SerializeOption) {
for i, expression := range expressions {
if i > 0 {
out.WriteString(separator)
}
if expression != nil {
expression.serialize(statement, out, options...)
}
}
}
// SerializeProjectionList func
func SerializeProjectionList(statement StatementType, projections []Projection, out *SQLBuilder) {
for i, col := range projections {
if i > 0 {
out.WriteString(",")
out.NewLine()
}
if col == nil {
panic("jet: Projection is nil")
}
col.serializeForProjection(statement, out)
}
}
// SerializeColumnNames func
func SerializeColumnNames(columns []Column, out *SQLBuilder) {
for i, col := range columns {
if i > 0 {
out.WriteString(", ")
}
if col == nil {
panic("jet: nil column in columns list")
}
out.WriteIdentifier(col.Name())
}
}
// SerializeColumnExpressions func
func SerializeColumnExpressions(columns []ColumnExpression, statementType StatementType,
out *SQLBuilder, options ...SerializeOption) {
for i, col := range columns {
if i > 0 {
out.WriteString(", ")
}
if col == nil {
panic("jet: nil column in columns list")
}
col.serialize(statementType, out, options...)
}
}
// SerializeColumnExpressionNames func
func SerializeColumnExpressionNames(columns []ColumnExpression, out *SQLBuilder) {
for i, col := range columns {
if i > 0 {
out.WriteString(", ")
}
if col == nil {
panic("jet: nil column in columns list")
}
out.WriteIdentifier(col.Name())
}
}
// ExpressionListToSerializerList converts list of expressions to list of serializers
func ExpressionListToSerializerList(expressions []Expression) []Serializer {
var ret []Serializer
for _, expr := range expressions {
ret = append(ret, expr)
}
return ret
}
// BoolExpressionListToExpressionList converts list of bool expressions to list of expressions
func BoolExpressionListToExpressionList(expressions []BoolExpression) []Expression {
var ret []Expression
for _, expression := range expressions {
ret = append(ret, expression)
}
return ret
}
// ColumnListToProjectionList func
func ColumnListToProjectionList(columns []ColumnExpression) []Projection {
var ret []Projection
for _, column := range columns {
ret = append(ret, column)
}
return ret
}
// ToSerializerValue creates Serializer type from the value
func ToSerializerValue(value interface{}) Serializer {
if clause, ok := value.(Serializer); ok {
return clause
}
return literal(value)
}
// UnwindRowFromModel func
func UnwindRowFromModel(columns []Column, data interface{}) []Serializer {
structValue := reflect.Indirect(reflect.ValueOf(data))
row := []Serializer{}
must.ValueBeOfTypeKind(structValue, reflect.Struct, "jet: data has to be a struct")
for _, column := range columns {
columnName := column.Name()
structFieldName := dbidentifier.ToGoIdentifier(columnName)
structField := structValue.FieldByName(structFieldName)
if !structField.IsValid() {
panic("missing struct field for column : " + columnName)
}
var field interface{}
if structField.Kind() == reflect.Ptr && structField.IsNil() {
field = nil
} else {
field = reflect.Indirect(structField).Interface()
}
row = append(row, literal(field))
}
return row
}
// UnwindRowsFromModels func
func UnwindRowsFromModels(columns []Column, data interface{}) [][]Serializer {
sliceValue := reflect.Indirect(reflect.ValueOf(data))
must.ValueBeOfTypeKind(sliceValue, reflect.Slice, "jet: data has to be a slice.")
rows := [][]Serializer{}
for i := 0; i < sliceValue.Len(); i++ {
structValue := sliceValue.Index(i)
rows = append(rows, UnwindRowFromModel(columns, structValue.Interface()))
}
return rows
}
// UnwindRowFromValues func
func UnwindRowFromValues(value interface{}, values []interface{}) []Serializer {
row := []Serializer{}
allValues := append([]interface{}{value}, values...)
for _, val := range allValues {
row = append(row, ToSerializerValue(val))
}
return row
}
// UnwindColumns func
func UnwindColumns(column1 Column, columns ...Column) []Column {
columnList := []Column{}
if val, ok := column1.(ColumnList); ok {
for _, col := range val {
columnList = append(columnList, col)
}
columnList = append(columnList, columns...)
} else {
columnList = append(columnList, column1)
columnList = append(columnList, columns...)
}
return columnList
}
// UnwidColumnList func
func UnwidColumnList(columns []Column) []Column {
ret := []Column{}
for _, col := range columns {
if columnList, ok := col.(ColumnList); ok {
for _, c := range columnList {
ret = append(ret, c)
}
} else {
ret = append(ret, col)
}
}
return ret
}
// OptionalOrDefaultString will return first value from variable argument list str or
// defaultStr if variable argument list is empty
func OptionalOrDefaultString(defaultStr string, str ...string) string {
if len(str) > 0 {
return str[0]
}
return defaultStr
}
// OptionalOrDefaultExpression will return first value from variable argument list expression or
// defaultExpression if variable argument list is empty
func OptionalOrDefaultExpression(defaultExpression Expression, expression ...Expression) Expression {
if len(expression) > 0 {
return expression[0]
}
return defaultExpression
}
func extractTableAndColumnName(alias string) (tableName string, columnName string) {
parts := strings.Split(alias, ".")
if len(parts) >= 2 {
tableName = parts[0]
columnName = parts[1]
} else {
columnName = parts[0]
}
return
}
func serializeToDefaultDebugString(expr Serializer) string {
out := SQLBuilder{Dialect: defaultDialect, Debug: true}
expr.serialize(SelectStatementType, &out)
return out.Buff.String()
}
// joinAlias examples:
//
// joinAlias("foo", "bar") // "foo.bar"
// joinAlias("foo.*", "bar") // "foo.bar"
// joinAlias("", "bar") // "bar"
func joinAlias(tableAlias, columnAlias string) string {
if tableAlias == "" {
return columnAlias
}
return strings.TrimRight(tableAlias, ".*") + "." + columnAlias
}

35
vendor/github.com/go-jet/jet/v2/internal/jet/values.go generated vendored Normal file
View File

@@ -0,0 +1,35 @@
package jet
// Values hold a set of one or more rows
type Values []RowExpression
func (v Values) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteByte('(')
out.IncreaseIdent(5)
out.NewLine()
out.WriteString("VALUES")
for rowIndex, row := range v {
if rowIndex > 0 {
out.WriteString(",")
out.NewLine()
} else {
out.IncreaseIdent(7)
}
row.serialize(statement, out, options...)
}
out.DecreaseIdent(7)
out.DecreaseIdent(5)
out.NewLine()
out.WriteByte(')')
}
func (v Values) projections() ProjectionList {
if len(v) == 0 {
return nil
}
return v[0].projections()
}

View File

@@ -0,0 +1,146 @@
package jet
type commonWindowImpl struct {
expression Expression
window Window
}
func (w *commonWindowImpl) over(window ...Window) {
if len(window) > 0 {
w.window = window[0]
} else {
w.window = newWindowImpl(nil)
}
}
func (w *commonWindowImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
w.expression.serialize(statement, out)
if w.window != nil {
out.WriteString("OVER")
w.window.serialize(statement, out, FallTrough(options)...)
}
}
// --------------------------------------
type windowExpression interface {
Expression
OVER(window ...Window) Expression
}
func newWindowExpression(Exp Expression) windowExpression {
newExp := &windowExpressionImpl{
Expression: Exp,
}
newExp.commonWindowImpl.expression = Exp
return newExp
}
type windowExpressionImpl struct {
Expression
commonWindowImpl
}
func (f *windowExpressionImpl) OVER(window ...Window) Expression {
f.commonWindowImpl.over(window...)
return f
}
func (f *windowExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
f.commonWindowImpl.serialize(statement, out, FallTrough(options)...)
}
// -----------------------------------------------------
type floatWindowExpression interface {
FloatExpression
OVER(window ...Window) FloatExpression
}
func newFloatWindowExpression(floatExp FloatExpression) floatWindowExpression {
newExp := &floatWindowExpressionImpl{
FloatExpression: floatExp,
}
newExp.commonWindowImpl.expression = floatExp
return newExp
}
type floatWindowExpressionImpl struct {
FloatExpression
commonWindowImpl
}
func (f *floatWindowExpressionImpl) OVER(window ...Window) FloatExpression {
f.commonWindowImpl.over(window...)
return f
}
func (f *floatWindowExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
f.commonWindowImpl.serialize(statement, out, FallTrough(options)...)
}
// ------------------------------------------------
type integerWindowExpression interface {
IntegerExpression
OVER(window ...Window) IntegerExpression
}
func newIntegerWindowExpression(intExp IntegerExpression) integerWindowExpression {
newExp := &integerWindowExpressionImpl{
IntegerExpression: intExp,
}
newExp.commonWindowImpl.expression = intExp
return newExp
}
type integerWindowExpressionImpl struct {
IntegerExpression
commonWindowImpl
}
func (f *integerWindowExpressionImpl) OVER(window ...Window) IntegerExpression {
f.commonWindowImpl.over(window...)
return f
}
func (f *integerWindowExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
f.commonWindowImpl.serialize(statement, out, FallTrough(options)...)
}
// ------------------------------------------------
type boolWindowExpression interface {
BoolExpression
OVER(window ...Window) BoolExpression
}
func newBoolWindowExpression(boolExp BoolExpression) boolWindowExpression {
newExp := &boolWindowExpressionImpl{
BoolExpression: boolExp,
}
newExp.commonWindowImpl.expression = boolExp
return newExp
}
type boolWindowExpressionImpl struct {
BoolExpression
commonWindowImpl
}
func (f *boolWindowExpressionImpl) OVER(window ...Window) BoolExpression {
f.commonWindowImpl.over(window...)
return f
}
func (f *boolWindowExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
f.commonWindowImpl.serialize(statement, out, FallTrough(options)...)
}

View File

@@ -0,0 +1,186 @@
package jet
// Window interface
type Window interface {
Serializer
ORDER_BY(expr ...OrderByClause) Window
ROWS(start FrameExtent, end ...FrameExtent) Window
RANGE(start FrameExtent, end ...FrameExtent) Window
GROUPS(start FrameExtent, end ...FrameExtent) Window
}
type windowImpl struct {
partitionBy []Expression
orderBy ClauseOrderBy
frameUnits string
start, end FrameExtent
parent Window
}
func newWindowImpl(parent Window) *windowImpl {
newWindow := &windowImpl{}
if parent == nil {
newWindow.parent = newWindow
} else {
newWindow.parent = parent
}
return newWindow
}
func (w *windowImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if !contains(options, NoWrap) {
out.WriteByte('(')
}
if w.partitionBy != nil {
out.WriteString("PARTITION BY")
serializeExpressionList(statement, w.partitionBy, ", ", out)
}
w.orderBy.SkipNewLine = true
w.orderBy.Serialize(statement, out, FallTrough(options)...)
if w.frameUnits != "" {
out.WriteString(w.frameUnits)
if w.end == nil {
w.start.serialize(statement, out)
} else {
out.WriteString("BETWEEN")
w.start.serialize(statement, out)
out.WriteString("AND")
w.end.serialize(statement, out)
}
}
if !contains(options, NoWrap) {
out.WriteByte(')')
}
}
func (w *windowImpl) ORDER_BY(exprs ...OrderByClause) Window {
w.orderBy.List = exprs
return w.parent
}
func (w *windowImpl) ROWS(start FrameExtent, end ...FrameExtent) Window {
w.frameUnits = "ROWS"
w.setFrameRange(start, end...)
return w.parent
}
func (w *windowImpl) RANGE(start FrameExtent, end ...FrameExtent) Window {
w.frameUnits = "RANGE"
w.setFrameRange(start, end...)
return w.parent
}
func (w *windowImpl) GROUPS(start FrameExtent, end ...FrameExtent) Window {
w.frameUnits = "GROUPS"
w.setFrameRange(start, end...)
return w.parent
}
func (w *windowImpl) setFrameRange(start FrameExtent, end ...FrameExtent) {
w.start = start
if len(end) > 0 {
w.end = end[0]
}
}
// PARTITION_BY window function constructor
func PARTITION_BY(exp Expression, exprs ...Expression) Window {
funImpl := newWindowImpl(nil)
funImpl.partitionBy = append([]Expression{exp}, exprs...)
return funImpl
}
// ORDER_BY window function constructor
func ORDER_BY(expr ...OrderByClause) Window {
funImpl := newWindowImpl(nil)
funImpl.orderBy.List = expr
return funImpl
}
// -----------------------------------------------
// FrameExtent interface
type FrameExtent interface {
Serializer
isFrameExtent()
}
// PRECEDING window frame clause
func PRECEDING(offset Serializer) FrameExtent {
return &frameExtentImpl{
preceding: true,
offset: offset,
}
}
// FOLLOWING window frame clause
func FOLLOWING(offset Serializer) FrameExtent {
return &frameExtentImpl{
preceding: false,
offset: offset,
}
}
type frameExtentImpl struct {
preceding bool
offset Serializer
}
func (f *frameExtentImpl) isFrameExtent() {}
func (f *frameExtentImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if f == nil {
return
}
f.offset.serialize(statement, out, FallTrough(options)...)
if f.preceding {
out.WriteString("PRECEDING")
} else {
out.WriteString("FOLLOWING")
}
}
// -----------------------------------------------
// Window function keywords
var (
UNBOUNDED = Keyword("UNBOUNDED")
CURRENT_ROW = frameExtentKeyword{"CURRENT ROW"}
)
type frameExtentKeyword struct {
Keyword
}
func (f frameExtentKeyword) isFrameExtent() {}
// -----------------------------------------------
// WindowName is used to specify window reference from WINDOW clause
func WindowName(name string) Window {
newWindow := &windowName{name: name}
newWindow.parent = newWindow
return newWindow
}
type windowName struct {
windowImpl
name string
}
func (w windowName) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteByte('(')
out.WriteString(w.name)
w.windowImpl.serialize(statement, out, NoWrap.WithFallTrough(options)...)
out.WriteByte(')')
}

View File

@@ -0,0 +1,101 @@
package jet
import "fmt"
// WITH function creates new with statement from list of common table expressions for specified dialect
func WITH(dialect Dialect, recursive bool, cte ...*CommonTableExpression) func(statement Statement) Statement {
newWithImpl := &withImpl{
recursive: recursive,
ctes: cte,
serializerStatementInterfaceImpl: serializerStatementInterfaceImpl{
dialect: dialect,
statementType: WithStatementType,
},
}
newWithImpl.parent = newWithImpl
return func(primaryStatement Statement) Statement {
serializerStatement, ok := primaryStatement.(SerializerStatement)
if !ok {
panic("jet: unsupported main WITH statement.")
}
newWithImpl.primaryStatement = serializerStatement
return newWithImpl
}
}
type withImpl struct {
serializerStatementInterfaceImpl
recursive bool
ctes []*CommonTableExpression
primaryStatement SerializerStatement
}
func (w withImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.NewLine()
out.WriteString("WITH")
if w.recursive {
out.WriteString("RECURSIVE")
}
for i, cte := range w.ctes {
if i > 0 {
out.WriteString(",")
}
cte.serialize(statement, out, FallTrough(options)...)
}
w.primaryStatement.serialize(statement, out, NoWrap.WithFallTrough(options)...)
}
func (w withImpl) projections() ProjectionList {
return ProjectionList{}
}
// CommonTableExpression contains information about a CTE.
type CommonTableExpression struct {
selectTableImpl
NotMaterialized bool
Columns []ColumnExpression
}
// CTE creates new named CommonTableExpression
func CTE(name string, columns ...ColumnExpression) CommonTableExpression {
cte := CommonTableExpression{
selectTableImpl: NewSelectTable(nil, name, columns),
Columns: columns,
}
for _, column := range cte.Columns {
column.setSubQuery(cte)
}
return cte
}
func (c CommonTableExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if statement == WithStatementType { // serialize CTE definition
out.WriteIdentifier(c.alias)
if len(c.Columns) > 0 {
out.WriteByte('(')
SerializeColumnExpressionNames(c.Columns, out)
out.WriteByte(')')
}
out.WriteString("AS")
if c.NotMaterialized {
out.WriteString("NOT MATERIALIZED")
}
if c.Statement == nil {
panic(fmt.Sprintf("jet: '%s' CTE is not defined", c.alias))
}
c.Statement.serialize(statement, out, FallTrough(options)...)
} else { // serialize CTE in FROM clause
out.WriteIdentifier(c.alias)
}
}

View File

@@ -0,0 +1,107 @@
package dbidentifier
import (
"github.com/go-jet/jet/v2/internal/3rdparty/snaker"
"strings"
"unicode"
)
// ToGoIdentifier converts database identifier to Go identifier.
func ToGoIdentifier(databaseIdentifier string) string {
return snaker.SnakeToCamel(replaceInvalidChars(databaseIdentifier))
}
// ToGoFileName converts database identifier to Go file name.
func ToGoFileName(databaseIdentifier string) string {
return strings.ToLower(replaceInvalidChars(databaseIdentifier))
}
func replaceInvalidChars(identifier string) string {
increase, needs := needsCharReplacement(identifier)
if !needs {
return identifier
}
var b strings.Builder
b.Grow(len(identifier) + increase)
for _, c := range identifier {
switch {
case unicode.IsSpace(c):
b.WriteByte('_')
case unicode.IsControl(c):
continue
default:
replacement, ok := asciiCharacterReplacement[c]
if ok {
b.WriteByte('_')
b.WriteString(replacement)
b.WriteByte('_')
} else {
b.WriteRune(c)
}
}
}
return b.String()
}
func needsCharReplacement(identifier string) (increase int, needs bool) {
for _, c := range identifier {
switch {
case unicode.IsSpace(c):
needs = true
case unicode.IsControl(c):
increase += -1
needs = true
continue
default:
replacement, ok := asciiCharacterReplacement[c]
if ok {
increase += len(replacement) + 1
needs = true
}
}
}
return increase, needs
}
var asciiCharacterReplacement = map[rune]string{
'!': "exclamation",
'"': "quotation",
'#': "number",
'$': "dollar",
'%': "percent",
'&': "ampersand",
'\'': "apostrophe",
'(': "opening_parentheses",
')': "closing_parentheses",
'*': "asterisk",
'+': "plus",
',': "comma",
'-': "_",
'.': "_",
'/': "slash",
':': "colon",
';': "semicolon",
'<': "less",
'=': "equal",
'>': "greater",
'?': "question",
'@': "at",
'[': "opening_bracket",
'\\': "backslash",
']': "closing_bracket",
'^': "caret",
'`': "accent",
'{': "opening_braces",
'|': "vertical_bar",
'}': "closing_braces",
'~': "tilde",
}

Some files were not shown because too many files have changed in this diff Show More