github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/s3select/sql/utils.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package sql
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  )
    24  
    25  // String functions
    26  
    27  // String - returns the JSONPath representation
    28  func (e *JSONPath) String() string {
    29  	if len(e.pathString) == 0 {
    30  		parts := make([]string, len(e.PathExpr)+1)
    31  		parts[0] = e.BaseKey.String()
    32  		for i, pe := range e.PathExpr {
    33  			parts[i+1] = pe.String()
    34  		}
    35  		e.pathString = strings.Join(parts, "")
    36  	}
    37  	return e.pathString
    38  }
    39  
    40  // StripTableAlias removes a table alias from the path. The result is also
    41  // cached for repeated lookups during SQL query evaluation.
    42  func (e *JSONPath) StripTableAlias(tableAlias string) []*JSONPathElement {
    43  	if e.strippedTableAlias == tableAlias {
    44  		return e.strippedPathExpr
    45  	}
    46  
    47  	hasTableAlias := e.BaseKey.String() == tableAlias || strings.EqualFold(e.BaseKey.String(), baseTableName)
    48  	var pathExpr []*JSONPathElement
    49  	if hasTableAlias {
    50  		pathExpr = e.PathExpr
    51  	} else {
    52  		pathExpr = make([]*JSONPathElement, len(e.PathExpr)+1)
    53  		pathExpr[0] = &JSONPathElement{Key: &ObjectKey{ID: e.BaseKey}}
    54  		copy(pathExpr[1:], e.PathExpr)
    55  	}
    56  	e.strippedTableAlias = tableAlias
    57  	e.strippedPathExpr = pathExpr
    58  	return e.strippedPathExpr
    59  }
    60  
    61  func (e *JSONPathElement) String() string {
    62  	switch {
    63  	case e.Key != nil:
    64  		return e.Key.String()
    65  	case e.Index != nil:
    66  		return fmt.Sprintf("[%d]", *e.Index)
    67  	case e.ObjectWildcard:
    68  		return ".*"
    69  	case e.ArrayWildcard:
    70  		return "[*]"
    71  	}
    72  	return ""
    73  }
    74  
    75  // String removes double quotes in quoted identifiers
    76  func (i *Identifier) String() string {
    77  	if i.Unquoted != nil {
    78  		return *i.Unquoted
    79  	}
    80  	return string(*i.Quoted)
    81  }
    82  
    83  func (o *ObjectKey) String() string {
    84  	if o.Lit != nil {
    85  		return fmt.Sprintf("['%s']", string(*o.Lit))
    86  	}
    87  	return fmt.Sprintf(".%s", o.ID.String())
    88  }
    89  
    90  func (o *ObjectKey) keyString() string {
    91  	if o.Lit != nil {
    92  		return string(*o.Lit)
    93  	}
    94  	return o.ID.String()
    95  }
    96  
    97  // getLastKeypathComponent checks if the given expression is a path
    98  // expression, and if so extracts the last dot separated component of
    99  // the path. Otherwise it returns false.
   100  func getLastKeypathComponent(e *Expression) (string, bool) {
   101  	if len(e.And) > 1 ||
   102  		len(e.And[0].Condition) > 1 ||
   103  		e.And[0].Condition[0].Not != nil ||
   104  		e.And[0].Condition[0].Operand.ConditionRHS != nil {
   105  		return "", false
   106  	}
   107  
   108  	operand := e.And[0].Condition[0].Operand.Operand
   109  	if operand.Right != nil ||
   110  		operand.Left.Right != nil ||
   111  		operand.Left.Left.Negated != nil ||
   112  		operand.Left.Left.Primary.JPathExpr == nil {
   113  		return "", false
   114  	}
   115  
   116  	// Check if path expression ends in a key
   117  	jpath := operand.Left.Left.Primary.JPathExpr
   118  	n := len(jpath.PathExpr)
   119  	if n > 0 && jpath.PathExpr[n-1].Key == nil {
   120  		return "", false
   121  	}
   122  	ps := jpath.String()
   123  	if idx := strings.LastIndex(ps, "."); idx >= 0 {
   124  		// Get last part of path string.
   125  		ps = ps[idx+1:]
   126  	}
   127  	return ps, true
   128  }
   129  
   130  // HasKeypath returns if the from clause has a key path -
   131  // e.g. S3object[*].id
   132  func (from *TableExpression) HasKeypath() bool {
   133  	return len(from.Table.PathExpr) > 1
   134  }