go.temporal.io/server@v1.23.0/common/archiver/s3store/query_parser.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 //go:generate mockgen -copyright_file ../../../LICENSE -package $GOPACKAGE -source query_parser.go -destination query_parser_mock.go -mock_names Interface=MockQueryParser 26 27 package s3store 28 29 import ( 30 "errors" 31 "fmt" 32 "strconv" 33 "time" 34 35 "github.com/temporalio/sqlparser" 36 37 "go.temporal.io/server/common/convert" 38 "go.temporal.io/server/common/primitives/timestamp" 39 ) 40 41 type ( 42 // QueryParser parses a limited SQL where clause into a struct 43 QueryParser interface { 44 Parse(query string) (*parsedQuery, error) 45 } 46 47 queryParser struct{} 48 49 parsedQuery struct { 50 workflowTypeName *string 51 workflowID *string 52 startTime *time.Time 53 closeTime *time.Time 54 searchPrecision *string 55 } 56 ) 57 58 // All allowed fields for filtering 59 const ( 60 WorkflowTypeName = "WorkflowTypeName" 61 WorkflowID = "WorkflowId" 62 StartTime = "StartTime" 63 CloseTime = "CloseTime" 64 SearchPrecision = "SearchPrecision" 65 ) 66 67 // Precision specific values 68 const ( 69 PrecisionDay = "Day" 70 PrecisionHour = "Hour" 71 PrecisionMinute = "Minute" 72 PrecisionSecond = "Second" 73 ) 74 const ( 75 queryTemplate = "select * from dummy where %s" 76 defaultDateTimeFormat = time.RFC3339 77 ) 78 79 // NewQueryParser creates a new query parser for filestore 80 func NewQueryParser() QueryParser { 81 return &queryParser{} 82 } 83 84 func (p *queryParser) Parse(query string) (*parsedQuery, error) { 85 stmt, err := sqlparser.Parse(fmt.Sprintf(queryTemplate, query)) 86 if err != nil { 87 return nil, err 88 } 89 whereExpr := stmt.(*sqlparser.Select).Where.Expr 90 parsedQuery := &parsedQuery{} 91 if err := p.convertWhereExpr(whereExpr, parsedQuery); err != nil { 92 return nil, err 93 } 94 if parsedQuery.workflowID == nil && parsedQuery.workflowTypeName == nil { 95 return nil, errors.New("WorkflowId or WorkflowTypeName is required in query") 96 } 97 if parsedQuery.workflowID != nil && parsedQuery.workflowTypeName != nil { 98 return nil, errors.New("only one of WorkflowId or WorkflowTypeName can be specified in a query") 99 } 100 if parsedQuery.closeTime != nil && parsedQuery.startTime != nil { 101 return nil, errors.New("only one of StartTime or CloseTime can be specified in a query") 102 } 103 if (parsedQuery.closeTime != nil || parsedQuery.startTime != nil) && parsedQuery.searchPrecision == nil { 104 return nil, errors.New("SearchPrecision is required when searching for a StartTime or CloseTime") 105 } 106 107 if parsedQuery.closeTime == nil && parsedQuery.startTime == nil && parsedQuery.searchPrecision != nil { 108 return nil, errors.New("SearchPrecision requires a StartTime or CloseTime") 109 } 110 return parsedQuery, nil 111 } 112 113 func (p *queryParser) convertWhereExpr(expr sqlparser.Expr, parsedQuery *parsedQuery) error { 114 if expr == nil { 115 return errors.New("where expression is nil") 116 } 117 118 switch expr := expr.(type) { 119 case *sqlparser.ComparisonExpr: 120 return p.convertComparisonExpr(expr, parsedQuery) 121 case *sqlparser.AndExpr: 122 return p.convertAndExpr(expr, parsedQuery) 123 case *sqlparser.ParenExpr: 124 return p.convertParenExpr(expr, parsedQuery) 125 default: 126 return errors.New("only comparison and \"and\" expression is supported") 127 } 128 } 129 130 func (p *queryParser) convertParenExpr(parenExpr *sqlparser.ParenExpr, parsedQuery *parsedQuery) error { 131 return p.convertWhereExpr(parenExpr.Expr, parsedQuery) 132 } 133 134 func (p *queryParser) convertAndExpr(andExpr *sqlparser.AndExpr, parsedQuery *parsedQuery) error { 135 if err := p.convertWhereExpr(andExpr.Left, parsedQuery); err != nil { 136 return err 137 } 138 return p.convertWhereExpr(andExpr.Right, parsedQuery) 139 } 140 141 func (p *queryParser) convertComparisonExpr(compExpr *sqlparser.ComparisonExpr, parsedQuery *parsedQuery) error { 142 colName, ok := compExpr.Left.(*sqlparser.ColName) 143 if !ok { 144 return fmt.Errorf("invalid filter name: %s", sqlparser.String(compExpr.Left)) 145 } 146 colNameStr := sqlparser.String(colName) 147 op := compExpr.Operator 148 valExpr, ok := compExpr.Right.(*sqlparser.SQLVal) 149 if !ok { 150 return fmt.Errorf("invalid value: %s", sqlparser.String(compExpr.Right)) 151 } 152 valStr := sqlparser.String(valExpr) 153 154 switch colNameStr { 155 case WorkflowTypeName: 156 val, err := extractStringValue(valStr) 157 if err != nil { 158 return err 159 } 160 if op != "=" { 161 return fmt.Errorf("only operation = is support for %s", WorkflowTypeName) 162 } 163 if parsedQuery.workflowTypeName != nil { 164 return fmt.Errorf("can not query %s multiple times", WorkflowTypeName) 165 } 166 parsedQuery.workflowTypeName = convert.StringPtr(val) 167 case WorkflowID: 168 val, err := extractStringValue(valStr) 169 if err != nil { 170 return err 171 } 172 if op != "=" { 173 return fmt.Errorf("only operation = is support for %s", WorkflowID) 174 } 175 if parsedQuery.workflowID != nil { 176 return fmt.Errorf("can not query %s multiple times", WorkflowID) 177 } 178 parsedQuery.workflowID = convert.StringPtr(val) 179 case CloseTime: 180 timestamp, err := convertToTime(valStr) 181 if err != nil { 182 return err 183 } 184 if op != "=" { 185 return fmt.Errorf("only operation = is support for %s", CloseTime) 186 } 187 parsedQuery.closeTime = ×tamp 188 case StartTime: 189 timestamp, err := convertToTime(valStr) 190 if err != nil { 191 return err 192 } 193 if op != "=" { 194 return fmt.Errorf("only operation = is support for %s", CloseTime) 195 } 196 parsedQuery.startTime = ×tamp 197 case SearchPrecision: 198 val, err := extractStringValue(valStr) 199 if err != nil { 200 return err 201 } 202 if op != "=" { 203 return fmt.Errorf("only operation = is support for %s", SearchPrecision) 204 } 205 if parsedQuery.searchPrecision != nil && *parsedQuery.searchPrecision != val { 206 return fmt.Errorf("only one expression is allowed for %s", SearchPrecision) 207 } 208 switch val { 209 case PrecisionDay: 210 case PrecisionHour: 211 case PrecisionMinute: 212 case PrecisionSecond: 213 default: 214 return fmt.Errorf("invalid value for %s: %s", SearchPrecision, val) 215 } 216 parsedQuery.searchPrecision = convert.StringPtr(val) 217 218 default: 219 return fmt.Errorf("unknown filter name: %s", colNameStr) 220 } 221 222 return nil 223 } 224 225 func convertToTime(timeStr string) (time.Time, error) { 226 ts, err := strconv.ParseInt(timeStr, 10, 64) 227 if err == nil { 228 return timestamp.UnixOrZeroTime(ts), nil 229 } 230 timestampStr, err := extractStringValue(timeStr) 231 if err != nil { 232 return time.Time{}, err 233 } 234 parsedTime, err := time.Parse(defaultDateTimeFormat, timestampStr) 235 if err != nil { 236 return time.Time{}, err 237 } 238 return parsedTime, nil 239 } 240 241 func extractStringValue(s string) (string, error) { 242 if len(s) >= 2 && s[0] == '\'' && s[len(s)-1] == '\'' { 243 return s[1 : len(s)-1], nil 244 } 245 return "", fmt.Errorf("value %s is not a string value", s) 246 }