mirror of
				https://github.com/minio/minio.git
				synced 2025-10-31 08:11:19 +01:00 
			
		
		
		
	This is to ensure that there are no projects that try to import `minio/minio/pkg` into their own repo. Any such common packages should go to `https://github.com/minio/pkg`
		
			
				
	
	
		
			201 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2015-2021 MinIO, Inc.
 | |
| //
 | |
| // This file is part of MinIO Object Storage stack
 | |
| //
 | |
| // This program is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU Affero General Public License as published by
 | |
| // the Free Software Foundation, either version 3 of the License, or
 | |
| // (at your option) any later version.
 | |
| //
 | |
| // This program is distributed in the hope that it will be useful
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| // GNU Affero General Public License for more details.
 | |
| //
 | |
| // You should have received a copy of the GNU Affero General Public License
 | |
| // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| package sql
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	errMalformedEscapeSequence  = errors.New("Malformed escape sequence in LIKE clause")
 | |
| 	errInvalidTrimArg           = errors.New("Trim argument is invalid - this should not happen")
 | |
| 	errInvalidSubstringIndexLen = errors.New("Substring start index or length falls outside the string")
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	percent    rune = '%'
 | |
| 	underscore rune = '_'
 | |
| 	runeZero   rune = 0
 | |
| )
 | |
| 
 | |
| func evalSQLLike(text, pattern string, escape rune) (match bool, err error) {
 | |
| 	s := []rune{}
 | |
| 	prev := runeZero
 | |
| 	hasLeadingPercent := false
 | |
| 	patLen := len([]rune(pattern))
 | |
| 	for i, r := range pattern {
 | |
| 		if i > 0 && prev == escape {
 | |
| 			switch r {
 | |
| 			case percent, escape, underscore:
 | |
| 				s = append(s, r)
 | |
| 				prev = r
 | |
| 				if r == escape {
 | |
| 					prev = runeZero
 | |
| 				}
 | |
| 			default:
 | |
| 				return false, errMalformedEscapeSequence
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		prev = r
 | |
| 
 | |
| 		var ok bool
 | |
| 		switch r {
 | |
| 		case percent:
 | |
| 			if len(s) == 0 {
 | |
| 				hasLeadingPercent = true
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			text, ok = matcher(text, string(s), hasLeadingPercent)
 | |
| 			if !ok {
 | |
| 				return false, nil
 | |
| 			}
 | |
| 			hasLeadingPercent = true
 | |
| 			s = []rune{}
 | |
| 
 | |
| 			if i == patLen-1 {
 | |
| 				// Last pattern character is a %, so
 | |
| 				// we are done.
 | |
| 				return true, nil
 | |
| 			}
 | |
| 
 | |
| 		case underscore:
 | |
| 			if len(s) == 0 {
 | |
| 				text, ok = dropRune(text)
 | |
| 				if !ok {
 | |
| 					return false, nil
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			text, ok = matcher(text, string(s), hasLeadingPercent)
 | |
| 			if !ok {
 | |
| 				return false, nil
 | |
| 			}
 | |
| 			hasLeadingPercent = false
 | |
| 
 | |
| 			text, ok = dropRune(text)
 | |
| 			if !ok {
 | |
| 				return false, nil
 | |
| 			}
 | |
| 			s = []rune{}
 | |
| 
 | |
| 		case escape:
 | |
| 			if i == patLen-1 {
 | |
| 				return false, errMalformedEscapeSequence
 | |
| 			}
 | |
| 			// Otherwise do nothing.
 | |
| 
 | |
| 		default:
 | |
| 			s = append(s, r)
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 	if hasLeadingPercent {
 | |
| 		return strings.HasSuffix(text, string(s)), nil
 | |
| 	}
 | |
| 	return string(s) == text, nil
 | |
| }
 | |
| 
 | |
| // matcher - Finds `pat` in `text`, and returns the part remainder of
 | |
| // `text`, after the match. If leadingPercent is false, `pat` must be
 | |
| // the prefix of `text`, otherwise it must be a substring.
 | |
| func matcher(text, pat string, leadingPercent bool) (res string, match bool) {
 | |
| 	if !leadingPercent {
 | |
| 		res = strings.TrimPrefix(text, pat)
 | |
| 		if len(text) == len(res) {
 | |
| 			return "", false
 | |
| 		}
 | |
| 	} else {
 | |
| 		parts := strings.SplitN(text, pat, 2)
 | |
| 		if len(parts) == 1 {
 | |
| 			return "", false
 | |
| 		}
 | |
| 		res = parts[1]
 | |
| 	}
 | |
| 	return res, true
 | |
| }
 | |
| 
 | |
| func dropRune(text string) (res string, ok bool) {
 | |
| 	r := []rune(text)
 | |
| 	if len(r) == 0 {
 | |
| 		return "", false
 | |
| 	}
 | |
| 	return string(r[1:]), true
 | |
| }
 | |
| 
 | |
| func evalSQLSubstring(s string, startIdx, length int) (res string, err error) {
 | |
| 	rs := []rune(s)
 | |
| 
 | |
| 	// According to s3 document, if startIdx < 1, it is set to 1.
 | |
| 	if startIdx < 1 {
 | |
| 		startIdx = 1
 | |
| 	}
 | |
| 
 | |
| 	if startIdx > len(rs) {
 | |
| 		startIdx = len(rs) + 1
 | |
| 	}
 | |
| 
 | |
| 	// StartIdx is 1-based in the input
 | |
| 	startIdx--
 | |
| 	endIdx := len(rs)
 | |
| 	if length != -1 {
 | |
| 		if length < 0 {
 | |
| 			return "", errInvalidSubstringIndexLen
 | |
| 		}
 | |
| 
 | |
| 		if length > (endIdx - startIdx) {
 | |
| 			length = endIdx - startIdx
 | |
| 		}
 | |
| 
 | |
| 		endIdx = startIdx + length
 | |
| 	}
 | |
| 
 | |
| 	return string(rs[startIdx:endIdx]), nil
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	trimLeading  = "LEADING"
 | |
| 	trimTrailing = "TRAILING"
 | |
| 	trimBoth     = "BOTH"
 | |
| )
 | |
| 
 | |
| func evalSQLTrim(where *string, trimChars, text string) (result string, err error) {
 | |
| 	cutSet := " "
 | |
| 	if trimChars != "" {
 | |
| 		cutSet = trimChars
 | |
| 	}
 | |
| 
 | |
| 	trimFunc := strings.Trim
 | |
| 	switch {
 | |
| 	case where == nil:
 | |
| 	case *where == trimBoth:
 | |
| 	case *where == trimLeading:
 | |
| 		trimFunc = strings.TrimLeft
 | |
| 	case *where == trimTrailing:
 | |
| 		trimFunc = strings.TrimRight
 | |
| 	default:
 | |
| 		return "", errInvalidTrimArg
 | |
| 	}
 | |
| 
 | |
| 	return trimFunc(text, cutSet), nil
 | |
| }
 |