func NewPreparedQueryFromString(query string) (PreparedQuery, error) {
	statement, err := Parse(query)
	if err != nil {
		return nil, err
	}
	switch statement.(type) {
	case *Select:
		return statement.(*Select), nil
	case *Insert:
		return statement.(*Insert), nil
	case *Delete:
		return statement.(*Delete), nil
	case *Update:
		return statement.(*Update), nil
	default:
		return nil, nil
	}
	return nil, nil
}
NewPreparedQueryFromString creates typed statement based on query inside Prepare statement
package sqlparser
import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cossacklabs/acra/sqlparser/dependency/querypb"
	"github.com/cossacklabs/acra/sqlparser/dependency/sqltypes"
	"github.com/cossacklabs/acra/sqlparser/dialect"
	"github.com/cossacklabs/acra/sqlparser/dialect/mysql"
	"strconv"
	"strings"
)
type SQLNode interface {
	Format(buf *TrackedBuffer)
	walkSubtree(visit Visit) error
}
type DialectFormat interface {
	FormatForDialect(dialect dialect.Dialect, buf *TrackedBuffer)
}
type Visit func(node SQLNode) (kontinue bool, err error)
func Walk(visit Visit, nodes ...SQLNode) error {
	for _, node := range nodes {
		if node == nil {
			continue
		}
		kontinue, err := visit(node)
		if err != nil {
			return err
		}
		if kontinue {
			err = node.walkSubtree(visit)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func String(node SQLNode) string {
	return StringWithDialect(defaultDialect, node)
}
func StringWithDialect(dialect dialect.Dialect, node SQLNode) string {
	if node == nil {
		return "<nil>"
	}
	buf := NewTrackedBuffer(nil)
	buf.SetDialect(dialect)
	buf.Myprintf("%v", node)
	return buf.String()
}
func Append(buf *bytes.Buffer, node SQLNode) {
	tbuf := &TrackedBuffer{
		Buffer: buf,
	}
	node.Format(tbuf)
}
type Statement interface {
	iStatement()
	SQLNode
}
func (*Union) iStatement()             {}
func (*Select) iStatement()            {}
func (*Stream) iStatement()            {}
func (*Insert) iStatement()            {}
func (*Update) iStatement()            {}
func (*Delete) iStatement()            {}
func (*Set) iStatement()               {}
func (*DBDDL) iStatement()             {}
func (*DDL) iStatement()               {}
func (*Show) iStatement()              {}
func (*Use) iStatement()               {}
func (*Begin) iStatement()             {}
func (*Commit) iStatement()            {}
func (*Rollback) iStatement()          {}
func (*OtherRead) iStatement()         {}
func (*OtherAdmin) iStatement()        {}
func (*DeallocatePrepare) iStatement() {}
func (*Prepare) iStatement()           {}
func (*Execute) iStatement()           {}
func (*ParenSelect) iStatement() {}
type SelectStatement interface {
	iSelectStatement()
	iStatement()
	iInsertRows()
	AddOrder(*Order)
	SetLimit(*Limit)
	SQLNode
}
func (*Select) iSelectStatement()      {}
func (*Union) iSelectStatement()       {}
func (*ParenSelect) iSelectStatement() {}
type Select struct {
	Cache       string
	Comments    Comments
	Distinct    string
	Hints       string
	SelectExprs SelectExprs
	From        TableExprs
	Where       *Where
	GroupBy     GroupBy
	Having      *Where
	OrderBy     OrderBy
	Limit       *Limit
	Lock        string
}
func (node *Select) AddOrder(order *Order) {
	node.OrderBy = append(node.OrderBy, order)
}
func (node *Select) SetLimit(limit *Limit) {
	node.Limit = limit
}
func (node *Select) AddWhere(expr Expr) {
	if _, ok := expr.(*OrExpr); ok {
		expr = &ParenExpr{Expr: expr}
	}
	if node.Where == nil {
		node.Where = &Where{
			Type: WhereStr,
			Expr: expr,
		}
		return
	}
	node.Where.Expr = &AndExpr{
		Left:  node.Where.Expr,
		Right: expr,
	}
	return
}
func (node *Select) AddHaving(expr Expr) {
	if _, ok := expr.(*OrExpr); ok {
		expr = &ParenExpr{Expr: expr}
	}
	if node.Having == nil {
		node.Having = &Where{
			Type: HavingStr,
			Expr: expr,
		}
		return
	}
	node.Having.Expr = &AndExpr{
		Left:  node.Having.Expr,
		Right: expr,
	}
	return
}
const (
	DistinctStr      = "distinct "
	StraightJoinHint = "straight_join "
)
const (
	ForUpdateStr = " for update"
	ShareModeStr = " lock in share mode"
)
const (
	SQLCacheStr   = "sql_cache "
	SQLNoCacheStr = "sql_no_cache "
)
type ParenSelect struct {
	Select SelectStatement
}
func (node *ParenSelect) AddOrder(order *Order) {
	panic("unreachable")
}
func (node *ParenSelect) SetLimit(limit *Limit) {
	panic("unreachable")
}
type Union struct {
	Type        string
	Left, Right SelectStatement
	OrderBy     OrderBy
	Limit       *Limit
	Lock        string
}
const (
	UnionStr         = "union"
	UnionAllStr      = "union all"
	UnionDistinctStr = "union distinct"
)
func (node *Union) AddOrder(order *Order) {
	node.OrderBy = append(node.OrderBy, order)
}
func (node *Union) SetLimit(limit *Limit) {
	node.Limit = limit
}
type Stream struct {
	Comments   Comments
	SelectExpr SelectExpr
	Table      TableName
}
type Insert struct {
	Action     string
	Comments   Comments
	Ignore     string
	Table      TableName
	Default    bool
	Partitions Partitions
	Columns    Columns
	Rows       InsertRows
	OnDup      OnDup
	Returning  Returning
}
const (
	InsertStr  = "insert"
	ReplaceStr = "replace"
)
type InsertRows interface {
	iInsertRows()
	SQLNode
}
func (*Select) iInsertRows()      {}
func (*Union) iInsertRows()       {}
func (Values) iInsertRows()       {}
func (*ParenSelect) iInsertRows() {}
type Update struct {
	Comments   Comments
	TableExprs TableExprs
	Exprs      UpdateExprs
	Where      *Where
	OrderBy    OrderBy
	Limit      *Limit
}
type Delete struct {
	Comments   Comments
	Targets    TableNames
	TableExprs TableExprs
	Partitions Partitions
	Where      *Where
	OrderBy    OrderBy
	Limit      *Limit
}
type Set struct {
	Comments Comments
	Exprs    SetExprs
	Scope    string
}
const (
	SessionStr = "session"
	GlobalStr  = "global"
)
type DBDDL struct {
	Action   string
	DBName   string
	IfExists bool
	Collate  string
	Charset  string
}
type DDL struct {
	Action        string
	Table         TableName
	NewName       TableName
	IfExists      bool
	TableSpec     *TableSpec
	PartitionSpec *PartitionSpec
	VindexSpec    *VindexSpec
	VindexCols    []ColIdent
}
const (
	CreateStr        = "create"
	AlterStr         = "alter"
	DropStr          = "drop"
	RenameStr        = "rename"
	TruncateStr      = "truncate"
	CreateVindexStr  = "create vindex"
	AddColVindexStr  = "add vindex"
	DropColVindexStr = "drop vindex"
	VindexOwnerStr = "owner"
)
type PartitionSpec struct {
	Action      string
	Name        ColIdent
	Definitions []*PartitionDefinition
}
const (
	ReorganizeStr = "reorganize partition"
)
type PartitionDefinition struct {
	Name     ColIdent
	Limit    Expr
	Maxvalue bool
}
type TableSpec struct {
	Columns []*ColumnDefinition
	Indexes []*IndexDefinition
	Options string
}
func (ts *TableSpec) AddColumn(cd *ColumnDefinition) {
	ts.Columns = append(ts.Columns, cd)
}
func (ts *TableSpec) AddIndex(id *IndexDefinition) {
	ts.Indexes = append(ts.Indexes, id)
}
type ColumnDefinition struct {
	Name ColIdent
	Type ColumnType
}
func (col *ColumnDefinition) Format(buf *TrackedBuffer) {
	buf.Myprintf("%v %v", col.Name, &col.Type)
}
func (col *ColumnDefinition) walkSubtree(visit Visit) error {
	if col == nil {
		return nil
	}
	return Walk(
		visit,
		col.Name,
		&col.Type,
	)
}
type ColumnTypes []ColumnType
type ColumnType struct {
	Type string
	NotNull       BoolVal
	Autoincrement BoolVal
	Default       *SQLVal
	OnUpdate      *SQLVal
	Comment       *SQLVal
	Length   *SQLVal
	Unsigned BoolVal
	Zerofill BoolVal
	Scale    *SQLVal
	Charset string
	Collate string
	EnumValues []string
	KeyOpt ColumnKeyOption
}
func (ct *ColumnType) DescribeType() string {
	buf := NewTrackedBuffer(nil)
	buf.Myprintf("%s", ct.Type)
	if ct.Length != nil && ct.Scale != nil {
		buf.Myprintf("(%v,%v)", ct.Length, ct.Scale)
	} else if ct.Length != nil {
		buf.Myprintf("(%v)", ct.Length)
	}
	opts := make([]string, 0, 16)
	if ct.Unsigned {
		opts = append(opts, keywordStrings[UNSIGNED])
	}
	if ct.Zerofill {
		opts = append(opts, keywordStrings[ZEROFILL])
	}
	if len(opts) != 0 {
		buf.Myprintf(" %s", strings.Join(opts, " "))
	}
	return buf.String()
}
func (ct *ColumnType) SQLType() querypb.Type {
	switch ct.Type {
	case keywordStrings[TINYINT]:
		if ct.Unsigned {
			return sqltypes.Uint8
		}
		return sqltypes.Int8
	case keywordStrings[SMALLINT]:
		if ct.Unsigned {
			return sqltypes.Uint16
		}
		return sqltypes.Int16
	case keywordStrings[MEDIUMINT]:
		if ct.Unsigned {
			return sqltypes.Uint24
		}
		return sqltypes.Int24
	case keywordStrings[INT]:
		fallthrough
	case keywordStrings[INTEGER]:
		if ct.Unsigned {
			return sqltypes.Uint32
		}
		return sqltypes.Int32
	case keywordStrings[BIGINT]:
		if ct.Unsigned {
			return sqltypes.Uint64
		}
		return sqltypes.Int64
	case keywordStrings[TEXT]:
		return sqltypes.Text
	case keywordStrings[TINYTEXT]:
		return sqltypes.Text
	case keywordStrings[MEDIUMTEXT]:
		return sqltypes.Text
	case keywordStrings[LONGTEXT]:
		return sqltypes.Text
	case keywordStrings[BLOB]:
		return sqltypes.Blob
	case keywordStrings[TINYBLOB]:
		return sqltypes.Blob
	case keywordStrings[MEDIUMBLOB]:
		return sqltypes.Blob
	case keywordStrings[LONGBLOB]:
		return sqltypes.Blob
	case keywordStrings[CHAR]:
		return sqltypes.Char
	case keywordStrings[VARCHAR]:
		return sqltypes.VarChar
	case keywordStrings[BINARY]:
		return sqltypes.Binary
	case keywordStrings[VARBINARY]:
		return sqltypes.VarBinary
	case keywordStrings[DATE]:
		return sqltypes.Date
	case keywordStrings[TIME]:
		return sqltypes.Time
	case keywordStrings[DATETIME]:
		return sqltypes.Datetime
	case keywordStrings[TIMESTAMP]:
		return sqltypes.Timestamp
	case keywordStrings[YEAR]:
		return sqltypes.Year
	case keywordStrings[FLOAT_TYPE]:
		return sqltypes.Float32
	case keywordStrings[DOUBLE]:
		return sqltypes.Float64
	case keywordStrings[DECIMAL]:
		return sqltypes.Decimal
	case keywordStrings[BIT]:
		return sqltypes.Bit
	case keywordStrings[ENUM]:
		return sqltypes.Enum
	case keywordStrings[SET]:
		return sqltypes.Set
	case keywordStrings[JSON]:
		return sqltypes.TypeJSON
	case keywordStrings[GEOMETRY]:
		return sqltypes.Geometry
	case keywordStrings[POINT]:
		return sqltypes.Geometry
	case keywordStrings[LINESTRING]:
		return sqltypes.Geometry
	case keywordStrings[POLYGON]:
		return sqltypes.Geometry
	case keywordStrings[GEOMETRYCOLLECTION]:
		return sqltypes.Geometry
	case keywordStrings[MULTIPOINT]:
		return sqltypes.Geometry
	case keywordStrings[MULTILINESTRING]:
		return sqltypes.Geometry
	case keywordStrings[MULTIPOLYGON]:
		return sqltypes.Geometry
	}
	panic("unimplemented type " + ct.Type)
}
type IndexDefinition struct {
	Info    *IndexInfo
	Columns []*IndexColumn
	Options []*IndexOption
}
type IndexInfo struct {
	Type    string
	Name    ColIdent
	Primary bool
	Spatial bool
	Unique  bool
}
type IndexColumn struct {
	Column ColIdent
	Length *SQLVal
}
type LengthScaleOption struct {
	Length *SQLVal
	Scale  *SQLVal
}
type IndexOption struct {
	Name  string
	Value *SQLVal
	Using string
}
type ColumnKeyOption int
const (
	colKeyNone ColumnKeyOption = iota
	colKeyPrimary
	colKeySpatialKey
	colKeyUnique
	colKeyUniqueKey
	colKey
)
type VindexSpec struct {
	Name   ColIdent
	Type   ColIdent
	Params []VindexParam
}
func (node *VindexSpec) ParseParams() (string, map[string]string) {
	var owner string
	params := map[string]string{}
	for _, p := range node.Params {
		if p.Key.Lowered() == VindexOwnerStr {
			owner = p.Val
		} else {
			params[p.Key.String()] = p.Val
		}
	}
	return owner, params
}
type VindexParam struct {
	Key ColIdent
	Val string
}
type Show struct {
	Type          string
	OnTable       TableName
	ShowTablesOpt *ShowTablesOpt
	Scope         string
}
func (node *Show) HasOnTable() bool {
	return node.OnTable.Name.v != ""
}
type ShowTablesOpt struct {
	Extended string
	Full     string
	DbName   string
	Filter   *ShowFilter
}
type ShowFilter struct {
	Like   string
	Filter Expr
}
type Use struct {
	DBName TableIdent
}
type Begin struct{}
type Commit struct{}
type Rollback struct{}
type OtherRead struct{}
type OtherAdmin struct{}
type PreparedQuery interface {
	iPreparedQuery()
	SQLNode
}
func (TableIdent) iPreparedQuery() {}
func (*Select) iPreparedQuery()    {}
func (*Insert) iPreparedQuery()    {}
func (*Delete) iPreparedQuery()    {}
func (*Update) iPreparedQuery()    {}