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  }