github.com/dolthub/go-mysql-server@v0.18.0/sql/planbuilder/parse.go (about) 1 // Copyright 2023 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package planbuilder 16 17 import ( 18 goerrors "errors" 19 "strings" 20 "unicode" 21 22 ast "github.com/dolthub/vitess/go/vt/sqlparser" 23 "go.opentelemetry.io/otel/attribute" 24 "go.opentelemetry.io/otel/trace" 25 "gopkg.in/src-d/go-errors.v1" 26 27 "github.com/dolthub/go-mysql-server/sql" 28 "github.com/dolthub/go-mysql-server/sql/plan" 29 ) 30 31 const maxAnalysisIterations = 8 32 33 // ErrMaxAnalysisIters is thrown when the analysis iterations are exceeded 34 var ErrMaxAnalysisIters = errors.NewKind("exceeded max analysis iterations (%d)") 35 36 // Parse parses the given SQL |query| using the default parsing settings and returns the corresponding node. 37 func Parse(ctx *sql.Context, cat sql.Catalog, query string) (ret sql.Node, err error) { 38 sqlMode := sql.LoadSqlMode(ctx) 39 var parserOpts ast.ParserOptions 40 if err != nil { 41 parserOpts = ast.ParserOptions{} 42 } else { 43 parserOpts = sqlMode.ParserOptions() 44 } 45 return ParseWithOptions(ctx, cat, query, parserOpts) 46 } 47 48 func ParseWithOptions(ctx *sql.Context, cat sql.Catalog, query string, options ast.ParserOptions) (ret sql.Node, err error) { 49 defer func() { 50 if r := recover(); r != nil { 51 switch r := r.(type) { 52 case parseErr: 53 err = r.err 54 default: 55 panic(r) 56 } 57 } 58 }() 59 ret, _, _, err = parse(ctx, cat, query, false, options) 60 return 61 } 62 63 func ParseOne(ctx *sql.Context, cat sql.Catalog, query string) (sql.Node, string, string, error) { 64 sqlMode := sql.LoadSqlMode(ctx) 65 return parse(ctx, cat, query, true, sqlMode.ParserOptions()) 66 } 67 68 func parse(ctx *sql.Context, cat sql.Catalog, query string, multi bool, options ast.ParserOptions) (sql.Node, string, string, error) { 69 span, ctx := ctx.Span("parse", trace.WithAttributes(attribute.String("query", query))) 70 defer span.End() 71 72 s := RemoveSpaceAndDelimiter(query, ';') 73 74 var stmt ast.Statement 75 var err error 76 var parsed string 77 var remainder string 78 79 parsed = s 80 if !multi { 81 stmt, err = ast.ParseWithOptions(s, options) 82 } else { 83 var ri int 84 stmt, ri, err = ast.ParseOneWithOptions(s, options) 85 if ri != 0 && ri < len(s) { 86 parsed = s[:ri] 87 parsed = RemoveSpaceAndDelimiter(parsed, ';') 88 remainder = s[ri:] 89 } 90 return nil, parsed, remainder, err 91 } 92 93 if err != nil { 94 if goerrors.Is(err, ast.ErrEmpty) { 95 ctx.Warn(0, "query was empty after trimming comments, so it will be ignored") 96 return plan.NothingImpl, parsed, remainder, nil 97 } 98 return nil, parsed, remainder, sql.ErrSyntaxError.New(err.Error()) 99 } 100 101 b := New(ctx, cat) 102 b.SetParserOptions(options) 103 outScope := b.build(nil, stmt, s) 104 105 return outScope.node, parsed, remainder, err 106 } 107 108 func (b *Builder) Parse(query string, multi bool) (ret sql.Node, parsed, remainder string, err error) { 109 b.nesting++ 110 if b.nesting > maxAnalysisIterations { 111 return nil, "", "", ErrMaxAnalysisIters.New(maxAnalysisIterations) 112 } 113 defer func() { 114 b.nesting-- 115 if r := recover(); r != nil { 116 switch r := r.(type) { 117 case parseErr: 118 err = r.err 119 default: 120 panic(r) 121 } 122 } 123 }() 124 span, ctx := b.ctx.Span("parse", trace.WithAttributes(attribute.String("query", query))) 125 defer span.End() 126 127 s := RemoveSpaceAndDelimiter(query, ';') 128 129 var stmt ast.Statement 130 131 parsed = s 132 if !multi { 133 stmt, err = ast.ParseWithOptions(s, b.parserOpts) 134 } else { 135 var ri int 136 stmt, ri, err = ast.ParseOneWithOptions(s, b.parserOpts) 137 if ri != 0 && ri < len(s) { 138 parsed = s[:ri] 139 parsed = RemoveSpaceAndDelimiter(parsed, ';') 140 remainder = s[ri:] 141 } 142 } 143 144 if err != nil { 145 if goerrors.Is(err, ast.ErrEmpty) { 146 ctx.Warn(0, "query was empty after trimming comments, so it will be ignored") 147 return plan.NothingImpl, parsed, remainder, nil 148 } 149 return nil, parsed, remainder, sql.ErrSyntaxError.New(err.Error()) 150 } 151 152 outScope := b.build(nil, stmt, s) 153 154 return outScope.node, parsed, remainder, err 155 } 156 157 func (b *Builder) ParseOne(query string) (ret sql.Node, err error) { 158 ret, _, _, err = b.Parse(query, false) 159 return ret, err 160 } 161 162 func (b *Builder) BindOnly(stmt ast.Statement, s string) (ret sql.Node, err error) { 163 defer func() { 164 if r := recover(); r != nil { 165 switch r := r.(type) { 166 case parseErr: 167 err = r.err 168 default: 169 panic(r) 170 } 171 } 172 }() 173 outScope := b.build(nil, stmt, s) 174 return outScope.node, err 175 } 176 177 func ParseOnly(ctx *sql.Context, query string, multi bool) (ast.Statement, string, string, error) { 178 sqlMode := sql.LoadSqlMode(ctx) 179 options := sqlMode.ParserOptions() 180 181 s := RemoveSpaceAndDelimiter(query, ';') 182 183 var stmt ast.Statement 184 var parsed string 185 var remainder string 186 var err error 187 188 parsed = s 189 if !multi { 190 stmt, err = ast.ParseWithOptions(s, options) 191 } else { 192 var ri int 193 stmt, ri, err = ast.ParseOneWithOptions(s, options) 194 if ri != 0 && ri < len(s) { 195 parsed = s[:ri] 196 parsed = RemoveSpaceAndDelimiter(parsed, ';') 197 remainder = s[ri:] 198 } 199 } 200 return stmt, query, remainder, err 201 } 202 203 // RemoveSpaceAndDelimiter removes space characters and given delimiter characters from the given query. 204 func RemoveSpaceAndDelimiter(query string, d rune) string { 205 query = strings.TrimSpace(query) 206 // trim spaces and empty statements 207 return strings.TrimRightFunc(query, func(r rune) bool { 208 return r == d || unicode.IsSpace(r) 209 }) 210 }