xsqlparser

package module
v1.0.0-alpha Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2019 License: Apache-2.0 Imports: 9 Imported by: 7

README

xsqlparser

Build Status Go Report Card codecov

sql parser for golang.

This repo is ported of sqlparser-rs in Go.

Getting Started

Prerequisites
  • Go 1.12+
Installing
$ go get -u github.com/akito0107/xsqlparser/...
How to use

Currently supports SELECT,CREATE TABLE,CREATE VIEW,INSERT,UPDATE,DELETE.

  • simple case
import (
	"bytes"
	"log"

	"github.com/k0kubun/pp"

	"github.com/akito0107/xsqlparser"
	"github.com/akito0107/xsqlparser/dialect"
)

... 
str := "SELECT * from test_table"
parser, err := xsqlparser.NewParser(bytes.NewBufferString(str), &dialect.GenericSQLDialect{})
if err != nil {
	log.Fatal(err)
}

stmt, err := parser.ParseStatement()
if err != nil {
	log.Fatal(err)
}
pp.Println(stmt)

got:

&sqlast.SQLQuery{
  CTEs: []*sqlast.CTE{},
  Body: &sqlast.SQLSelect{
    Distinct:   false,
    Projection: []sqlast.SQLSelectItem{
      &sqlast.UnnamedExpression{
        Node: &sqlast.SQLWildcard{},
      },
    },
    Relation: &sqlast.Table{
      Name: &sqlast.SQLObjectName{
        Idents: []*sqlast.SQLIdent{
          &"test_table",
        },
      },
      Alias:     (*sqlast.SQLIdent)(nil),
      Args:      []sqlast.ASTNode{},
      WithHints: []sqlast.ASTNode{},
    },
    Joins:     []*sqlast.Join{},
    Selection: nil,
    GroupBy:   []sqlast.ASTNode{},
    Having:    nil,
  },
  OrderBy: []*sqlast.SQLOrderByExpr{},
  Limit:   nil,
}

You can also create sql from ast via ToSQLString().

log.Println(stmt.ToSQLString())

got:

2019/05/07 11:59:36 SELECT * FROM test_table
  • complicated select
str := "SELECT orders.product, SUM(orders.quantity) AS product_units, accounts.* " +
	"FROM orders LEFT JOIN accounts ON orders.account_id = accounts.id " +
	"WHERE orders.region IN (SELECT region FROM top_regions) " +
	"ORDER BY product_units LIMIT 100"

parser, err := xsqlparser.NewParser(bytes.NewBufferString(str), &dialect.GenericSQLDialect{})
if err != nil {
	log.Fatal(err)
}

stmt, err := parser.ParseStatement()
if err != nil {
	log.Fatal(err)
}
pp.Println(stmt)

got:

{
  CTEs: []*sqlast.CTE{},
  Body: &sqlast.SQLSelect{
    Distinct:   false,
    Projection: []sqlast.SQLSelectItem{
      &sqlast.UnnamedExpression{
        Node: &sqlast.SQLCompoundIdentifier{
          Idents: []*sqlast.SQLIdent{
            &"orders",
            &"product",
          },
        },
      },
      &sqlast.ExpressionWithAlias{
        Expr: &sqlast.SQLFunction{
          Name: &sqlast.SQLObjectName{
            Idents: []*sqlast.SQLIdent{
              &"SUM",
            },
          },
          Args: []sqlast.ASTNode{
            &sqlast.SQLCompoundIdentifier{
              Idents: []*sqlast.SQLIdent{
                &"orders",
                &"quantity",
              },
            },
          },
          Over: (*sqlast.SQLWindowSpec)(nil),
        },
        Alias: &"product_units",
      },
      &sqlast.QualifiedWildcard{
        Prefix: &sqlast.SQLObjectName{
          Idents: []*sqlast.SQLIdent{
            &"accounts",
          },
        },
      },
    },
    Relation: &sqlast.Table{
      Name: &sqlast.SQLObjectName{
        Idents: []*sqlast.SQLIdent{
          &"orders",
        },
      },
      Alias:     (*sqlast.SQLIdent)(nil),
      Args:      []sqlast.ASTNode{},
      WithHints: []sqlast.ASTNode{},
    },
    Joins: []*sqlast.Join{
      &sqlast.Join{
        Relation: &sqlast.Table{
          Name: &sqlast.SQLObjectName{
            Idents: []*sqlast.SQLIdent{
              &"accounts",
            },
          },
          Alias:     (*sqlast.SQLIdent)(nil),
          Args:      []sqlast.ASTNode{},
          WithHints: []sqlast.ASTNode{},
        },
        Op:       1,
        Constant: &sqlast.OnJoinConstant{
          Node: &sqlast.SQLBinaryExpr{
            Left: &sqlast.SQLCompoundIdentifier{
              Idents: []*sqlast.SQLIdent{
                &"orders",
                &"account_id",
              },
            },
            Op:    9,
            Right: &sqlast.SQLCompoundIdentifier{
              Idents: []*sqlast.SQLIdent{
                &"accounts",
                &"id",
              },
            },
          },
        },
      },
    },
    Selection: &sqlast.SQLInSubQuery{
      Expr: &sqlast.SQLCompoundIdentifier{
        Idents: []*sqlast.SQLIdent{
          &"orders",
          &"region",
        },
      },
      SubQuery: &sqlast.SQLQuery{
        CTEs: []*sqlast.CTE{},
        Body: &sqlast.SQLSelect{
          Distinct:   false,
          Projection: []sqlast.SQLSelectItem{
            &sqlast.UnnamedExpression{
              Node: &sqlast.SQLIdentifier{
                Ident: &"region",
              },
            },
          },
          Relation: &sqlast.Table{
            Name: &sqlast.SQLObjectName{
              Idents: []*sqlast.SQLIdent{
                &"top_regions",
              },
            },
            Alias:     (*sqlast.SQLIdent)(nil),
            Args:      []sqlast.ASTNode{},
            WithHints: []sqlast.ASTNode{},
          },
          Joins:     []*sqlast.Join{},
          Selection: nil,
          GroupBy:   []sqlast.ASTNode{},
          Having:    nil,
        },
        OrderBy: []*sqlast.SQLOrderByExpr{},
        Limit:   nil,
      },
      Negated: false,
    },
    GroupBy: []sqlast.ASTNode{},
    Having:  nil,
  },
  OrderBy: []*sqlast.SQLOrderByExpr{
    &sqlast.SQLOrderByExpr{
      Expr: &sqlast.SQLIdentifier{
        Ident: &"product_units",
      },
      ASC: (*bool)(nil),
    },
  },
  Limit: &100,
}
  • with CTE
str := "WITH regional_sales AS (" +
	"SELECT region, SUM(amount) AS total_sales " +
	"FROM orders GROUP BY region) " +
	"SELECT product, SUM(quantity) AS product_units " +
	"FROM orders " +
	"WHERE region IN (SELECT region FROM top_regions) " +
	"GROUP BY region, product"

parser, err := xsqlparser.NewParser(bytes.NewBufferString(str), &dialect.GenericSQLDialect{})
if err != nil {
	log.Fatal(err)
}

stmt, err := parser.ParseStatement()
if err != nil {
	log.Fatal(err)
}
pp.Println(stmt)

got:

&sqlast.SQLQuery{
  CTEs: []*sqlast.CTE{
    &sqlast.CTE{
      Alias: &"regional_sales",
      Query: &sqlast.SQLQuery{
        CTEs: []*sqlast.CTE{},
        Body: &sqlast.SQLSelect{
          Distinct:   false,
          Projection: []sqlast.SQLSelectItem{
            &sqlast.UnnamedExpression{
              Node: &sqlast.SQLIdentifier{
                Ident: &"region",
              },
            },
            &sqlast.ExpressionWithAlias{
              Expr: &sqlast.SQLFunction{
                Name: &sqlast.SQLObjectName{
                  Idents: []*sqlast.SQLIdent{
                    &"SUM",
                  },
                },
                Args: []sqlast.ASTNode{
                  &sqlast.SQLIdentifier{
                    Ident: &"amount",
                  },
                },
                Over: (*sqlast.SQLWindowSpec)(nil),
              },
              Alias: &"total_sales",
            },
          },
          Relation: &sqlast.Table{
            Name: &sqlast.SQLObjectName{
              Idents: []*sqlast.SQLIdent{
                &"orders",
              },
            },
            Alias:     (*sqlast.SQLIdent)(nil),
            Args:      []sqlast.ASTNode{},
            WithHints: []sqlast.ASTNode{},
          },
          Joins:     []*sqlast.Join{},
          Selection: nil,
          GroupBy:   []sqlast.ASTNode{
            &sqlast.SQLIdentifier{
              Ident: &"region",
            },
          },
          Having: nil,
        },
        OrderBy: []*sqlast.SQLOrderByExpr{},
        Limit:   nil,
      },
    },
  },
  Body: &sqlast.SQLSelect{
    Distinct:   false,
    Projection: []sqlast.SQLSelectItem{
      &sqlast.UnnamedExpression{
        Node: &sqlast.SQLIdentifier{
          Ident: &"product",
        },
      },
      &sqlast.ExpressionWithAlias{
        Expr: &sqlast.SQLFunction{
          Name: &sqlast.SQLObjectName{
            Idents: []*sqlast.SQLIdent{
              &"SUM",
            },
          },
          Args: []sqlast.ASTNode{
            &sqlast.SQLIdentifier{
              Ident: &"quantity",
            },
          },
          Over: (*sqlast.SQLWindowSpec)(nil),
        },
        Alias: &"product_units",
      },
    },
    Relation: &sqlast.Table{
      Name: &sqlast.SQLObjectName{
        Idents: []*sqlast.SQLIdent{
          &"orders",
        },
      },
      Alias:     (*sqlast.SQLIdent)(nil),
      Args:      []sqlast.ASTNode{},
      WithHints: []sqlast.ASTNode{},
    },
    Joins:     []*sqlast.Join{},
    Selection: &sqlast.SQLInSubQuery{
      Expr: &sqlast.SQLIdentifier{
        Ident: &"region",
      },
      SubQuery: &sqlast.SQLQuery{
        CTEs: []*sqlast.CTE{},
        Body: &sqlast.SQLSelect{
          Distinct:   false,
          Projection: []sqlast.SQLSelectItem{
            &sqlast.UnnamedExpression{
              Node: &sqlast.SQLIdentifier{
                Ident: &"region",
              },
            },
          },
          Relation: &sqlast.Table{
            Name: &sqlast.SQLObjectName{
              Idents: []*sqlast.SQLIdent{
                &"top_regions",
              },
            },
            Alias:     (*sqlast.SQLIdent)(nil),
            Args:      []sqlast.ASTNode{},
            WithHints: []sqlast.ASTNode{},
          },
          Joins:     []*sqlast.Join{},
          Selection: nil,
          GroupBy:   []sqlast.ASTNode{},
          Having:    nil,
        },
        OrderBy: []*sqlast.SQLOrderByExpr{},
        Limit:   nil,
      },
      Negated: false,
    },
    GroupBy: []sqlast.ASTNode{
      &sqlast.SQLIdentifier{
        Ident: &"region",
      },
      &sqlast.SQLIdentifier{
        Ident: &"product",
      },
    },
    Having: nil,
  },
  OrderBy: []*sqlast.SQLOrderByExpr{},
  Limit:   nil,
}

License

This project is licensed under the Apache License 2.0 License - see the LICENSE file for details

Documentation

Index

Constants

This section is empty.

Variables

View Source
var TokenAlreadyConsumed = errors.New("tokens are already consumed")

Functions

This section is empty.

Types

type Parser

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

func NewParser

func NewParser(src io.Reader, dialect dialect.Dialect) (*Parser, error)

func (*Parser) Debug

func (p *Parser) Debug()

func (*Parser) ParseDataType

func (p *Parser) ParseDataType() (sqlast.SQLType, error)

func (*Parser) ParseExpr

func (p *Parser) ParseExpr() (sqlast.Node, error)

func (*Parser) ParseSQL

func (p *Parser) ParseSQL() ([]sqlast.SQLStmt, error)

func (*Parser) ParseStatement

func (p *Parser) ParseStatement() (sqlast.SQLStmt, error)

type SQLWord

type SQLWord struct {
	Value      string
	QuoteStyle rune
	Keyword    string
}

func MakeKeyword

func MakeKeyword(word string, quoteStyle rune) *SQLWord

func (*SQLWord) AsSQLIdent

func (s *SQLWord) AsSQLIdent() *sqlast.Ident

func (*SQLWord) String

func (s *SQLWord) String() string

type Token

type Token int
const (
	// A keyword (like SELECT)
	SQLKeyword Token = iota
	// Numeric literal
	Number
	// A character that cloud not be tokenized
	Char
	// Single quoted string i.e: 'string'
	SingleQuotedString
	// National string i.e: N'string'
	NationalStringLiteral
	// Comma
	Comma
	// Whitespace
	Whitespace
	// = operator
	Eq
	// != or <> operator
	Neq
	// <  operator
	Lt
	// > operator
	Gt
	// <= operator
	LtEq
	// >= operator
	GtEq
	// + operator
	Plus
	// - operator
	Minus
	// * operator
	Mult
	// / operator
	Div
	// % operator
	Mod
	// Left parenthesis `(`
	LParen
	// Right parenthesis `)`
	RParen
	// Period
	Period
	// Colon
	Colon
	// DoubleColon
	DoubleColon
	// Semicolon
	Semicolon
	// Backslash
	Backslash
	// Left bracket `]`
	LBracket
	// Right bracket `[`
	RBracket
	// &
	Ampersand
	// Left brace `{`
	LBrace
	// Right brace `}`
	RBrace
	// ILLEGAL token
	ILLEGAL
)

func (Token) String

func (i Token) String() string

type TokenPos

type TokenPos struct {
	Line int
	Col  int
}

func (*TokenPos) String

func (t *TokenPos) String() string

type TokenSet

type TokenSet struct {
	Tok   Token
	Value interface{}
	Pos   *TokenPos
}

type Tokenizer

type Tokenizer struct {
	Dialect dialect.Dialect
	Scanner *scanner.Scanner
	Line    int
	Col     int
}

func NewTokenizer

func NewTokenizer(src io.Reader, dialect dialect.Dialect) *Tokenizer

func (*Tokenizer) NextToken

func (t *Tokenizer) NextToken() (*TokenSet, error)

func (*Tokenizer) Pos

func (t *Tokenizer) Pos() *TokenPos

func (*Tokenizer) Tokenize

func (t *Tokenizer) Tokenize() ([]*TokenSet, error)

Directories

Path Synopsis
cmd
Code generated by genmark.
Code generated by genmark.
tools

Jump to

Keyboard shortcuts

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