init - add project files
This commit is contained in:
22
vendor/github.com/aymerick/douceur/LICENSE
generated
vendored
Normal file
22
vendor/github.com/aymerick/douceur/LICENSE
generated
vendored
Normal 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
60
vendor/github.com/aymerick/douceur/css/declaration.go
generated
vendored
Normal 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
230
vendor/github.com/aymerick/douceur/css/rule.go
generated
vendored
Normal 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
25
vendor/github.com/aymerick/douceur/css/stylesheet.go
generated
vendored
Normal 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
409
vendor/github.com/aymerick/douceur/parser/parser.go
generated
vendored
Normal 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
16
vendor/github.com/btcsuite/btcutil/LICENSE
generated
vendored
Normal 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
34
vendor/github.com/btcsuite/btcutil/base58/README.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
base58
|
||||
==========
|
||||
|
||||
[](https://travis-ci.org/btcsuite/btcutil)
|
||||
[](http://copyfree.org)
|
||||
[](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
49
vendor/github.com/btcsuite/btcutil/base58/alphabet.go
generated
vendored
Normal 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
75
vendor/github.com/btcsuite/btcutil/base58/base58.go
generated
vendored
Normal 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)
|
||||
}
|
||||
52
vendor/github.com/btcsuite/btcutil/base58/base58check.go
generated
vendored
Normal file
52
vendor/github.com/btcsuite/btcutil/base58/base58check.go
generated
vendored
Normal 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
|
||||
}
|
||||
17
vendor/github.com/btcsuite/btcutil/base58/cov_report.sh
generated
vendored
Normal file
17
vendor/github.com/btcsuite/btcutil/base58/cov_report.sh
generated
vendored
Normal 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
29
vendor/github.com/btcsuite/btcutil/base58/doc.go
generated
vendored
Normal 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
28
vendor/github.com/caarlos0/env/v11/.editorconfig
generated
vendored
Normal 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
5
vendor/github.com/caarlos0/env/v11/.gitignore
generated
vendored
Normal 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
32
vendor/github.com/caarlos0/env/v11/.golangci.yml
generated
vendored
Normal 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
5
vendor/github.com/caarlos0/env/v11/.goreleaser.yml
generated
vendored
Normal 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
7
vendor/github.com/caarlos0/env/v11/.mailmap
generated
vendored
Normal 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
21
vendor/github.com/caarlos0/env/v11/LICENSE.md
generated
vendored
Normal 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
37
vendor/github.com/caarlos0/env/v11/Makefile
generated
vendored
Normal 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
156
vendor/github.com/caarlos0/env/v11/README.md
generated
vendored
Normal 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
|
||||
|
||||
[](https://github.com/goreleaser/goreleaser/releases/latest)
|
||||
[](/LICENSE.md)
|
||||
[](https://github.com/caarlos0/env/actions?workflow=build)
|
||||
[](https://codecov.io/gh/caarlos0/env)
|
||||
[](http://godoc.org/github.com/caarlos0/env/v11)
|
||||
[](https://github.com/goreleaser)
|
||||
[](https://conventionalcommits.org)
|
||||
|
||||
## Related projects
|
||||
|
||||
- [envdoc](https://github.com/g4s8/envdoc) - generate documentation for environment variables from `env` tags
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/caarlos0/env)
|
||||
845
vendor/github.com/caarlos0/env/v11/env.go
generated
vendored
Normal file
845
vendor/github.com/caarlos0/env/v11/env.go
generated
vendored
Normal 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
16
vendor/github.com/caarlos0/env/v11/env_tomap.go
generated
vendored
Normal 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
|
||||
}
|
||||
29
vendor/github.com/caarlos0/env/v11/env_tomap_windows.go
generated
vendored
Normal file
29
vendor/github.com/caarlos0/env/v11/env_tomap_windows.go
generated
vendored
Normal 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
173
vendor/github.com/caarlos0/env/v11/error.go
generated
vendored
Normal 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
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal 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
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal 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
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal 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
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal 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
306
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal 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
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal 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
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal 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
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal 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
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal 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
20
vendor/github.com/go-co-op/gocron/v2/.gitignore
generated
vendored
Normal 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
44
vendor/github.com/go-co-op/gocron/v2/.golangci.yaml
generated
vendored
Normal 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
|
||||
24
vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml
generated
vendored
Normal file
24
vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml
generated
vendored
Normal 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
|
||||
73
vendor/github.com/go-co-op/gocron/v2/CODE_OF_CONDUCT.md
generated
vendored
Normal file
73
vendor/github.com/go-co-op/gocron/v2/CODE_OF_CONDUCT.md
generated
vendored
Normal 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
38
vendor/github.com/go-co-op/gocron/v2/CONTRIBUTING.md
generated
vendored
Normal 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
21
vendor/github.com/go-co-op/gocron/v2/LICENSE
generated
vendored
Normal 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
22
vendor/github.com/go-co-op/gocron/v2/Makefile
generated
vendored
Normal 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
180
vendor/github.com/go-co-op/gocron/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
# gocron: A Golang Job Scheduling Package
|
||||
|
||||
[](https://github.com/go-co-op/gocron/actions)
|
||||
 [](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)
|
||||

|
||||
|
||||
|
||||
[Sentry](https://sentry.io/welcome/)
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#go-co-op/gocron&Date)
|
||||
16
vendor/github.com/go-co-op/gocron/v2/SECURITY.md
generated
vendored
Normal file
16
vendor/github.com/go-co-op/gocron/v2/SECURITY.md
generated
vendored
Normal 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
30
vendor/github.com/go-co-op/gocron/v2/distributed.go
generated
vendored
Normal 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
65
vendor/github.com/go-co-op/gocron/v2/errors.go
generated
vendored
Normal 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
545
vendor/github.com/go-co-op/gocron/v2/executor.go
generated
vendored
Normal 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
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
101
vendor/github.com/go-co-op/gocron/v2/logger.go
generated
vendored
Normal 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
36
vendor/github.com/go-co-op/gocron/v2/monitor.go
generated
vendored
Normal 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
989
vendor/github.com/go-co-op/gocron/v2/scheduler.go
generated
vendored
Normal 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
118
vendor/github.com/go-co-op/gocron/v2/util.go
generated
vendored
Normal 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
262
vendor/github.com/go-jet/jet/v2/LICENSE
generated
vendored
Normal 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
14
vendor/github.com/go-jet/jet/v2/NOTICE
generated
vendored
Normal 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)
|
||||
42
vendor/github.com/go-jet/jet/v2/internal/3rdparty/pq/format_timestamp.go
generated
vendored
Normal file
42
vendor/github.com/go-jet/jet/v2/internal/3rdparty/pq/format_timestamp.go
generated
vendored
Normal 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
|
||||
}
|
||||
119
vendor/github.com/go-jet/jet/v2/internal/3rdparty/snaker/snaker.go
generated
vendored
Normal file
119
vendor/github.com/go-jet/jet/v2/internal/3rdparty/snaker/snaker.go
generated
vendored
Normal 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
32
vendor/github.com/go-jet/jet/v2/internal/jet/alias.go
generated
vendored
Normal 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)
|
||||
}
|
||||
115
vendor/github.com/go-jet/jet/v2/internal/jet/bool_expression.go
generated
vendored
Normal file
115
vendor/github.com/go-jet/jet/v2/internal/jet/bool_expression.go
generated
vendored
Normal 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
53
vendor/github.com/go-jet/jet/v2/internal/jet/cast.go
generated
vendored
Normal 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
672
vendor/github.com/go-jet/jet/v2/internal/jet/clause.go
generated
vendored
Normal 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
117
vendor/github.com/go-jet/jet/v2/internal/jet/column.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
27
vendor/github.com/go-jet/jet/v2/internal/jet/column_assigment.go
generated
vendored
Normal file
27
vendor/github.com/go-jet/jet/v2/internal/jet/column_assigment.go
generated
vendored
Normal 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)...)
|
||||
}
|
||||
100
vendor/github.com/go-jet/jet/v2/internal/jet/column_list.go
generated
vendored
Normal file
100
vendor/github.com/go-jet/jet/v2/internal/jet/column_list.go
generated
vendored
Normal 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)
|
||||
}
|
||||
401
vendor/github.com/go-jet/jet/v2/internal/jet/column_types.go
generated
vendored
Normal file
401
vendor/github.com/go-jet/jet/v2/internal/jet/column_types.go
generated
vendored
Normal 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 := ×tampColumnImpl{}
|
||||
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 := ×tampzColumnImpl{}
|
||||
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
|
||||
}
|
||||
93
vendor/github.com/go-jet/jet/v2/internal/jet/date_expression.go
generated
vendored
Normal file
93
vendor/github.com/go-jet/jet/v2/internal/jet/date_expression.go
generated
vendored
Normal 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
125
vendor/github.com/go-jet/jet/v2/internal/jet/dialect.go
generated
vendored
Normal 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
|
||||
}
|
||||
22
vendor/github.com/go-jet/jet/v2/internal/jet/enum_value.go
generated
vendored
Normal file
22
vendor/github.com/go-jet/jet/v2/internal/jet/enum_value.go
generated
vendored
Normal 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)
|
||||
}
|
||||
321
vendor/github.com/go-jet/jet/v2/internal/jet/expression.go
generated
vendored
Normal file
321
vendor/github.com/go-jet/jet/v2/internal/jet/expression.go
generated
vendored
Normal 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)
|
||||
}
|
||||
115
vendor/github.com/go-jet/jet/v2/internal/jet/float_expression.go
generated
vendored
Normal file
115
vendor/github.com/go-jet/jet/v2/internal/jet/float_expression.go
generated
vendored
Normal 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)
|
||||
}
|
||||
926
vendor/github.com/go-jet/jet/v2/internal/jet/func_expression.go
generated
vendored
Normal file
926
vendor/github.com/go-jet/jet/v2/internal/jet/func_expression.go
generated
vendored
Normal 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 := ×tampFunc{}
|
||||
|
||||
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 := ×tampzFunc{}
|
||||
|
||||
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
|
||||
}
|
||||
41
vendor/github.com/go-jet/jet/v2/internal/jet/group_by_clause.go
generated
vendored
Normal file
41
vendor/github.com/go-jet/jet/v2/internal/jet/group_by_clause.go
generated
vendored
Normal 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 1’s 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"),
|
||||
)
|
||||
}
|
||||
156
vendor/github.com/go-jet/jet/v2/internal/jet/integer_expression.go
generated
vendored
Normal file
156
vendor/github.com/go-jet/jet/v2/internal/jet/integer_expression.go
generated
vendored
Normal 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)
|
||||
}
|
||||
37
vendor/github.com/go-jet/jet/v2/internal/jet/interval.go
generated
vendored
Normal file
37
vendor/github.com/go-jet/jet/v2/internal/jet/interval.go
generated
vendored
Normal 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)...)
|
||||
}
|
||||
13
vendor/github.com/go-jet/jet/v2/internal/jet/keyword.go
generated
vendored
Normal file
13
vendor/github.com/go-jet/jet/v2/internal/jet/keyword.go
generated
vendored
Normal 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))
|
||||
}
|
||||
480
vendor/github.com/go-jet/jet/v2/internal/jet/literal_expression.go
generated
vendored
Normal file
480
vendor/github.com/go-jet/jet/v2/internal/jet/literal_expression.go
generated
vendored
Normal 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 := ×tampLiteral{}
|
||||
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 := ×tampLiteral{}
|
||||
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 := ×tampzLiteral{}
|
||||
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 := ×tampzLiteral{}
|
||||
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
81
vendor/github.com/go-jet/jet/v2/internal/jet/logger.go
generated
vendored
Normal 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++
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/go-jet/jet/v2/internal/jet/numeric_expression.go
generated
vendored
Normal file
15
vendor/github.com/go-jet/jet/v2/internal/jet/numeric_expression.go
generated
vendored
Normal 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() {}
|
||||
194
vendor/github.com/go-jet/jet/v2/internal/jet/operators.go
generated
vendored
Normal file
194
vendor/github.com/go-jet/jet/v2/internal/jet/operators.go
generated
vendored
Normal 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)
|
||||
}
|
||||
79
vendor/github.com/go-jet/jet/v2/internal/jet/order_by_clause.go
generated
vendored
Normal file
79
vendor/github.com/go-jet/jet/v2/internal/jet/order_by_clause.go
generated
vendored
Normal 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}
|
||||
}
|
||||
65
vendor/github.com/go-jet/jet/v2/internal/jet/order_set_aggregate_functions.go
generated
vendored
Normal file
65
vendor/github.com/go-jet/jet/v2/internal/jet/order_set_aggregate_functions.go
generated
vendored
Normal 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)
|
||||
}
|
||||
81
vendor/github.com/go-jet/jet/v2/internal/jet/projection.go
generated
vendored
Normal file
81
vendor/github.com/go-jet/jet/v2/internal/jet/projection.go
generated
vendored
Normal 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
|
||||
}
|
||||
141
vendor/github.com/go-jet/jet/v2/internal/jet/range_expression.go
generated
vendored
Normal file
141
vendor/github.com/go-jet/jet/v2/internal/jet/range_expression.go
generated
vendored
Normal 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]
|
||||
)
|
||||
47
vendor/github.com/go-jet/jet/v2/internal/jet/raw_statement.go
generated
vendored
Normal file
47
vendor/github.com/go-jet/jet/v2/internal/jet/raw_statement.go
generated
vendored
Normal 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(")")
|
||||
}
|
||||
}
|
||||
102
vendor/github.com/go-jet/jet/v2/internal/jet/row_expression.go
generated
vendored
Normal file
102
vendor/github.com/go-jet/jet/v2/internal/jet/row_expression.go
generated
vendored
Normal 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
|
||||
}
|
||||
71
vendor/github.com/go-jet/jet/v2/internal/jet/select_lock.go
generated
vendored
Normal file
71
vendor/github.com/go-jet/jet/v2/internal/jet/select_lock.go
generated
vendored
Normal 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")
|
||||
}
|
||||
}
|
||||
78
vendor/github.com/go-jet/jet/v2/internal/jet/select_table.go
generated
vendored
Normal file
78
vendor/github.com/go-jet/jet/v2/internal/jet/select_table.go
generated
vendored
Normal 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)
|
||||
}
|
||||
109
vendor/github.com/go-jet/jet/v2/internal/jet/serializer.go
generated
vendored
Normal file
109
vendor/github.com/go-jet/jet/v2/internal/jet/serializer.go
generated
vendored
Normal 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))
|
||||
}
|
||||
303
vendor/github.com/go-jet/jet/v2/internal/jet/sql_builder.go
generated
vendored
Normal file
303
vendor/github.com/go-jet/jet/v2/internal/jet/sql_builder.go
generated
vendored
Normal 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) + `'`
|
||||
}
|
||||
269
vendor/github.com/go-jet/jet/v2/internal/jet/statement.go
generated
vendored
Normal file
269
vendor/github.com/go-jet/jet/v2/internal/jet/statement.go
generated
vendored
Normal 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(")")
|
||||
}
|
||||
}
|
||||
115
vendor/github.com/go-jet/jet/v2/internal/jet/string_expression.go
generated
vendored
Normal file
115
vendor/github.com/go-jet/jet/v2/internal/jet/string_expression.go
generated
vendored
Normal 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
194
vendor/github.com/go-jet/jet/v2/internal/jet/table.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
91
vendor/github.com/go-jet/jet/v2/internal/jet/testutils.go
generated
vendored
Normal file
91
vendor/github.com/go-jet/jet/v2/internal/jet/testutils.go
generated
vendored
Normal 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)
|
||||
}
|
||||
93
vendor/github.com/go-jet/jet/v2/internal/jet/time_expression.go
generated
vendored
Normal file
93
vendor/github.com/go-jet/jet/v2/internal/jet/time_expression.go
generated
vendored
Normal 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)
|
||||
}
|
||||
93
vendor/github.com/go-jet/jet/v2/internal/jet/timestamp_expression.go
generated
vendored
Normal file
93
vendor/github.com/go-jet/jet/v2/internal/jet/timestamp_expression.go
generated
vendored
Normal 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 = ×tampExpressionWrap
|
||||
return ×tampExpressionWrap
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
93
vendor/github.com/go-jet/jet/v2/internal/jet/timestampz_expression.go
generated
vendored
Normal file
93
vendor/github.com/go-jet/jet/v2/internal/jet/timestampz_expression.go
generated
vendored
Normal 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 = ×tampzExpressionWrap
|
||||
return ×tampzExpressionWrap
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
93
vendor/github.com/go-jet/jet/v2/internal/jet/timez_expression.go
generated
vendored
Normal file
93
vendor/github.com/go-jet/jet/v2/internal/jet/timez_expression.go
generated
vendored
Normal 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
293
vendor/github.com/go-jet/jet/v2/internal/jet/utils.go
generated
vendored
Normal 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
35
vendor/github.com/go-jet/jet/v2/internal/jet/values.go
generated
vendored
Normal 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()
|
||||
}
|
||||
146
vendor/github.com/go-jet/jet/v2/internal/jet/window_expression.go
generated
vendored
Normal file
146
vendor/github.com/go-jet/jet/v2/internal/jet/window_expression.go
generated
vendored
Normal 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)...)
|
||||
}
|
||||
186
vendor/github.com/go-jet/jet/v2/internal/jet/window_func.go
generated
vendored
Normal file
186
vendor/github.com/go-jet/jet/v2/internal/jet/window_func.go
generated
vendored
Normal 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(')')
|
||||
}
|
||||
101
vendor/github.com/go-jet/jet/v2/internal/jet/with_statement.go
generated
vendored
Normal file
101
vendor/github.com/go-jet/jet/v2/internal/jet/with_statement.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
107
vendor/github.com/go-jet/jet/v2/internal/utils/dbidentifier/dbidentifier.go
generated
vendored
Normal file
107
vendor/github.com/go-jet/jet/v2/internal/utils/dbidentifier/dbidentifier.go
generated
vendored
Normal 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
Reference in New Issue
Block a user