go.temporal.io/server@v1.23.0/common/persistence/visibility/store/sql/visibility_store.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 package sql 26 27 import ( 28 "context" 29 "errors" 30 "fmt" 31 "strings" 32 "time" 33 34 "go.temporal.io/api/common/v1" 35 enumspb "go.temporal.io/api/enums/v1" 36 "go.temporal.io/api/serviceerror" 37 "go.temporal.io/api/workflowservice/v1" 38 39 "go.temporal.io/server/common/config" 40 "go.temporal.io/server/common/log" 41 "go.temporal.io/server/common/namespace" 42 "go.temporal.io/server/common/persistence" 43 persistencesql "go.temporal.io/server/common/persistence/sql" 44 "go.temporal.io/server/common/persistence/sql/sqlplugin" 45 "go.temporal.io/server/common/persistence/visibility/manager" 46 "go.temporal.io/server/common/persistence/visibility/store" 47 "go.temporal.io/server/common/persistence/visibility/store/query" 48 "go.temporal.io/server/common/resolver" 49 "go.temporal.io/server/common/searchattribute" 50 ) 51 52 type ( 53 VisibilityStore struct { 54 sqlStore persistencesql.SqlStore 55 searchAttributesProvider searchattribute.Provider 56 searchAttributesMapperProvider searchattribute.MapperProvider 57 } 58 ) 59 60 var _ store.VisibilityStore = (*VisibilityStore)(nil) 61 62 var maxTime, _ = time.Parse(time.RFC3339, "9999-12-31T23:59:59Z") 63 64 // NewSQLVisibilityStore creates an instance of VisibilityStore 65 func NewSQLVisibilityStore( 66 cfg config.SQL, 67 r resolver.ServiceResolver, 68 searchAttributesProvider searchattribute.Provider, 69 searchAttributesMapperProvider searchattribute.MapperProvider, 70 logger log.Logger, 71 ) (*VisibilityStore, error) { 72 refDbConn := persistencesql.NewRefCountedDBConn(sqlplugin.DbKindVisibility, &cfg, r) 73 db, err := refDbConn.Get() 74 if err != nil { 75 return nil, err 76 } 77 return &VisibilityStore{ 78 sqlStore: persistencesql.NewSqlStore(db, logger), 79 searchAttributesProvider: searchAttributesProvider, 80 searchAttributesMapperProvider: searchAttributesMapperProvider, 81 }, nil 82 } 83 84 func (s *VisibilityStore) Close() { 85 s.sqlStore.Close() 86 } 87 88 func (s *VisibilityStore) GetName() string { 89 return s.sqlStore.GetName() 90 } 91 92 func (s *VisibilityStore) GetIndexName() string { 93 return s.sqlStore.GetDbName() 94 } 95 96 func (s *VisibilityStore) ValidateCustomSearchAttributes( 97 searchAttributes map[string]any, 98 ) (map[string]any, error) { 99 return searchAttributes, nil 100 } 101 102 func (s *VisibilityStore) RecordWorkflowExecutionStarted( 103 ctx context.Context, 104 request *store.InternalRecordWorkflowExecutionStartedRequest, 105 ) error { 106 row, err := s.generateVisibilityRow(request.InternalVisibilityRequestBase) 107 if err != nil { 108 return err 109 } 110 111 _, err = s.sqlStore.Db.InsertIntoVisibility(ctx, row) 112 return err 113 } 114 115 func (s *VisibilityStore) RecordWorkflowExecutionClosed( 116 ctx context.Context, 117 request *store.InternalRecordWorkflowExecutionClosedRequest, 118 ) error { 119 row, err := s.generateVisibilityRow(request.InternalVisibilityRequestBase) 120 if err != nil { 121 return err 122 } 123 124 row.CloseTime = &request.CloseTime 125 row.HistoryLength = &request.HistoryLength 126 row.HistorySizeBytes = &request.HistorySizeBytes 127 row.ExecutionDuration = &request.ExecutionDuration 128 row.StateTransitionCount = &request.StateTransitionCount 129 130 result, err := s.sqlStore.Db.ReplaceIntoVisibility(ctx, row) 131 if err != nil { 132 return err 133 } 134 noRowsAffected, err := result.RowsAffected() 135 if err != nil { 136 return fmt.Errorf("RecordWorkflowExecutionClosed rowsAffected error: %v", err) 137 } 138 if noRowsAffected > 2 { // either adds a new row or deletes old row and adds new row 139 return fmt.Errorf( 140 "RecordWorkflowExecutionClosed unexpected numRows (%v) updated", 141 noRowsAffected, 142 ) 143 } 144 return nil 145 } 146 147 func (s *VisibilityStore) UpsertWorkflowExecution( 148 ctx context.Context, 149 request *store.InternalUpsertWorkflowExecutionRequest, 150 ) error { 151 row, err := s.generateVisibilityRow(request.InternalVisibilityRequestBase) 152 if err != nil { 153 return err 154 } 155 156 result, err := s.sqlStore.Db.ReplaceIntoVisibility(ctx, row) 157 if err != nil { 158 return err 159 } 160 noRowsAffected, err := result.RowsAffected() 161 if err != nil { 162 return err 163 } 164 if noRowsAffected > 2 { // either adds a new or deletes old row and adds new row 165 return fmt.Errorf("UpsertWorkflowExecution unexpected numRows (%v) updates", noRowsAffected) 166 } 167 return nil 168 } 169 170 func (s *VisibilityStore) ListOpenWorkflowExecutions( 171 ctx context.Context, 172 request *manager.ListWorkflowExecutionsRequest, 173 ) (*store.InternalListWorkflowExecutionsResponse, error) { 174 return s.ListWorkflowExecutions( 175 ctx, 176 &manager.ListWorkflowExecutionsRequestV2{ 177 NamespaceID: request.NamespaceID, 178 Namespace: request.Namespace, 179 PageSize: request.PageSize, 180 NextPageToken: request.NextPageToken, 181 Query: s.buildQueryStringFromListRequest( 182 request, 183 enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, 184 "", 185 "", 186 ), 187 }, 188 ) 189 } 190 191 func (s *VisibilityStore) ListClosedWorkflowExecutions( 192 ctx context.Context, 193 request *manager.ListWorkflowExecutionsRequest, 194 ) (*store.InternalListWorkflowExecutionsResponse, error) { 195 return s.ListWorkflowExecutions( 196 ctx, 197 &manager.ListWorkflowExecutionsRequestV2{ 198 NamespaceID: request.NamespaceID, 199 Namespace: request.Namespace, 200 PageSize: request.PageSize, 201 NextPageToken: request.NextPageToken, 202 Query: s.buildQueryStringFromListRequest( 203 request, 204 enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED, 205 "", 206 "", 207 ), 208 }, 209 ) 210 } 211 212 func (s *VisibilityStore) ListOpenWorkflowExecutionsByType( 213 ctx context.Context, 214 request *manager.ListWorkflowExecutionsByTypeRequest, 215 ) (*store.InternalListWorkflowExecutionsResponse, error) { 216 return s.ListWorkflowExecutions( 217 ctx, 218 &manager.ListWorkflowExecutionsRequestV2{ 219 NamespaceID: request.NamespaceID, 220 Namespace: request.Namespace, 221 PageSize: request.PageSize, 222 NextPageToken: request.NextPageToken, 223 Query: s.buildQueryStringFromListRequest( 224 request.ListWorkflowExecutionsRequest, 225 enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, 226 "", 227 request.WorkflowTypeName, 228 ), 229 }, 230 ) 231 } 232 233 func (s *VisibilityStore) ListClosedWorkflowExecutionsByType( 234 ctx context.Context, 235 request *manager.ListWorkflowExecutionsByTypeRequest, 236 ) (*store.InternalListWorkflowExecutionsResponse, error) { 237 return s.ListWorkflowExecutions( 238 ctx, 239 &manager.ListWorkflowExecutionsRequestV2{ 240 NamespaceID: request.NamespaceID, 241 Namespace: request.Namespace, 242 PageSize: request.PageSize, 243 NextPageToken: request.NextPageToken, 244 Query: s.buildQueryStringFromListRequest( 245 request.ListWorkflowExecutionsRequest, 246 enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED, 247 "", 248 request.WorkflowTypeName, 249 ), 250 }, 251 ) 252 } 253 254 func (s *VisibilityStore) ListOpenWorkflowExecutionsByWorkflowID( 255 ctx context.Context, 256 request *manager.ListWorkflowExecutionsByWorkflowIDRequest, 257 ) (*store.InternalListWorkflowExecutionsResponse, error) { 258 return s.ListWorkflowExecutions( 259 ctx, 260 &manager.ListWorkflowExecutionsRequestV2{ 261 NamespaceID: request.NamespaceID, 262 Namespace: request.Namespace, 263 PageSize: request.PageSize, 264 NextPageToken: request.NextPageToken, 265 Query: s.buildQueryStringFromListRequest( 266 request.ListWorkflowExecutionsRequest, 267 enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING, 268 request.WorkflowID, 269 "", 270 ), 271 }, 272 ) 273 } 274 275 func (s *VisibilityStore) ListClosedWorkflowExecutionsByWorkflowID( 276 ctx context.Context, 277 request *manager.ListWorkflowExecutionsByWorkflowIDRequest, 278 ) (*store.InternalListWorkflowExecutionsResponse, error) { 279 return s.ListWorkflowExecutions( 280 ctx, 281 &manager.ListWorkflowExecutionsRequestV2{ 282 NamespaceID: request.NamespaceID, 283 Namespace: request.Namespace, 284 PageSize: request.PageSize, 285 NextPageToken: request.NextPageToken, 286 Query: s.buildQueryStringFromListRequest( 287 request.ListWorkflowExecutionsRequest, 288 enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED, 289 request.WorkflowID, 290 "", 291 ), 292 }, 293 ) 294 } 295 296 func (s *VisibilityStore) ListClosedWorkflowExecutionsByStatus( 297 ctx context.Context, 298 request *manager.ListClosedWorkflowExecutionsByStatusRequest, 299 ) (*store.InternalListWorkflowExecutionsResponse, error) { 300 return s.ListWorkflowExecutions( 301 ctx, 302 &manager.ListWorkflowExecutionsRequestV2{ 303 NamespaceID: request.NamespaceID, 304 Namespace: request.Namespace, 305 PageSize: request.PageSize, 306 NextPageToken: request.NextPageToken, 307 Query: s.buildQueryStringFromListRequest( 308 request.ListWorkflowExecutionsRequest, 309 request.Status, 310 "", 311 "", 312 ), 313 }, 314 ) 315 } 316 317 func (s *VisibilityStore) DeleteWorkflowExecution( 318 ctx context.Context, 319 request *manager.VisibilityDeleteWorkflowExecutionRequest, 320 ) error { 321 _, err := s.sqlStore.Db.DeleteFromVisibility(ctx, sqlplugin.VisibilityDeleteFilter{ 322 NamespaceID: request.NamespaceID.String(), 323 RunID: request.RunID, 324 }) 325 if err != nil { 326 return serviceerror.NewUnavailable(err.Error()) 327 } 328 return nil 329 } 330 331 func (s *VisibilityStore) ListWorkflowExecutions( 332 ctx context.Context, 333 request *manager.ListWorkflowExecutionsRequestV2, 334 ) (*store.InternalListWorkflowExecutionsResponse, error) { 335 saTypeMap, err := s.searchAttributesProvider.GetSearchAttributes(s.GetIndexName(), false) 336 if err != nil { 337 return nil, err 338 } 339 340 saMapper, err := s.searchAttributesMapperProvider.GetMapper(request.Namespace) 341 if err != nil { 342 return nil, err 343 } 344 345 converter := NewQueryConverter( 346 s.GetName(), 347 request.Namespace, 348 request.NamespaceID, 349 saTypeMap, 350 saMapper, 351 request.Query, 352 ) 353 selectFilter, err := converter.BuildSelectStmt(request.PageSize, request.NextPageToken) 354 if err != nil { 355 // Convert ConverterError to InvalidArgument and pass through all other errors (which should be only mapper errors). 356 var converterErr *query.ConverterError 357 if errors.As(err, &converterErr) { 358 return nil, converterErr.ToInvalidArgument() 359 } 360 return nil, err 361 } 362 363 rows, err := s.sqlStore.Db.SelectFromVisibility(ctx, *selectFilter) 364 if err != nil { 365 return nil, serviceerror.NewUnavailable( 366 fmt.Sprintf("ListWorkflowExecutions operation failed. Select failed: %v", err)) 367 } 368 if len(rows) == 0 { 369 return &store.InternalListWorkflowExecutionsResponse{}, nil 370 } 371 372 var infos = make([]*store.InternalWorkflowExecutionInfo, len(rows)) 373 for i, row := range rows { 374 infos[i], err = s.rowToInfo(&row, request.Namespace) 375 if err != nil { 376 return nil, err 377 } 378 } 379 380 var nextPageToken []byte 381 if len(rows) == request.PageSize { 382 lastRow := rows[len(rows)-1] 383 closeTime := maxTime 384 if lastRow.CloseTime != nil { 385 closeTime = *lastRow.CloseTime 386 } 387 nextPageToken, err = serializePageToken(&pageToken{ 388 CloseTime: closeTime, 389 StartTime: lastRow.StartTime, 390 RunID: lastRow.RunID, 391 }) 392 if err != nil { 393 return nil, err 394 } 395 } 396 return &store.InternalListWorkflowExecutionsResponse{ 397 Executions: infos, 398 NextPageToken: nextPageToken, 399 }, nil 400 } 401 402 func (s *VisibilityStore) ScanWorkflowExecutions( 403 ctx context.Context, 404 request *manager.ListWorkflowExecutionsRequestV2, 405 ) (*store.InternalListWorkflowExecutionsResponse, error) { 406 return s.ListWorkflowExecutions(ctx, request) 407 } 408 409 func (s *VisibilityStore) CountWorkflowExecutions( 410 ctx context.Context, 411 request *manager.CountWorkflowExecutionsRequest, 412 ) (*manager.CountWorkflowExecutionsResponse, error) { 413 saTypeMap, err := s.searchAttributesProvider.GetSearchAttributes(s.GetIndexName(), false) 414 if err != nil { 415 return nil, err 416 } 417 418 saMapper, err := s.searchAttributesMapperProvider.GetMapper(request.Namespace) 419 if err != nil { 420 return nil, err 421 } 422 423 converter := NewQueryConverter( 424 s.GetName(), 425 request.Namespace, 426 request.NamespaceID, 427 saTypeMap, 428 saMapper, 429 request.Query, 430 ) 431 selectFilter, err := converter.BuildCountStmt() 432 if err != nil { 433 // Convert ConverterError to InvalidArgument and pass through all other errors (which should be only mapper errors). 434 var converterErr *query.ConverterError 435 if errors.As(err, &converterErr) { 436 return nil, converterErr.ToInvalidArgument() 437 } 438 return nil, err 439 } 440 441 if len(selectFilter.GroupBy) > 0 { 442 return s.countGroupByWorkflowExecutions(ctx, selectFilter, saTypeMap) 443 } 444 445 count, err := s.sqlStore.Db.CountFromVisibility(ctx, *selectFilter) 446 if err != nil { 447 return nil, serviceerror.NewUnavailable( 448 fmt.Sprintf("CountWorkflowExecutions operation failed. Query failed: %v", err)) 449 } 450 451 return &manager.CountWorkflowExecutionsResponse{Count: count}, nil 452 } 453 454 func (s *VisibilityStore) countGroupByWorkflowExecutions( 455 ctx context.Context, 456 selectFilter *sqlplugin.VisibilitySelectFilter, 457 saTypeMap searchattribute.NameTypeMap, 458 ) (*manager.CountWorkflowExecutionsResponse, error) { 459 var err error 460 groupByTypes := make([]enumspb.IndexedValueType, len(selectFilter.GroupBy)) 461 for i, fieldName := range selectFilter.GroupBy { 462 groupByTypes[i], err = saTypeMap.GetType(fieldName) 463 if err != nil { 464 return nil, err 465 } 466 } 467 468 rows, err := s.sqlStore.Db.CountGroupByFromVisibility(ctx, *selectFilter) 469 if err != nil { 470 return nil, serviceerror.NewUnavailable( 471 fmt.Sprintf("CountWorkflowExecutions operation failed. Query failed: %v", err)) 472 } 473 resp := &manager.CountWorkflowExecutionsResponse{ 474 Count: 0, 475 Groups: make([]*workflowservice.CountWorkflowExecutionsResponse_AggregationGroup, 0, len(rows)), 476 } 477 for _, row := range rows { 478 groupValues := make([]*common.Payload, len(row.GroupValues)) 479 for i, val := range row.GroupValues { 480 groupValues[i], err = searchattribute.EncodeValue(val, groupByTypes[i]) 481 if err != nil { 482 return nil, err 483 } 484 } 485 resp.Groups = append( 486 resp.Groups, 487 &workflowservice.CountWorkflowExecutionsResponse_AggregationGroup{ 488 GroupValues: groupValues, 489 Count: row.Count, 490 }, 491 ) 492 resp.Count += row.Count 493 } 494 return resp, nil 495 } 496 497 func (s *VisibilityStore) GetWorkflowExecution( 498 ctx context.Context, 499 request *manager.GetWorkflowExecutionRequest, 500 ) (*store.InternalGetWorkflowExecutionResponse, error) { 501 row, err := s.sqlStore.Db.GetFromVisibility(ctx, sqlplugin.VisibilityGetFilter{ 502 NamespaceID: request.NamespaceID.String(), 503 RunID: request.RunID, 504 }) 505 if err != nil { 506 return nil, serviceerror.NewUnavailable( 507 fmt.Sprintf("GetWorkflowExecution operation failed. Select failed: %v", err)) 508 } 509 info, err := s.rowToInfo(row, request.Namespace) 510 if err != nil { 511 return nil, err 512 } 513 return &store.InternalGetWorkflowExecutionResponse{ 514 Execution: info, 515 }, nil 516 } 517 518 func (s *VisibilityStore) generateVisibilityRow( 519 request *store.InternalVisibilityRequestBase, 520 ) (*sqlplugin.VisibilityRow, error) { 521 searchAttributes, err := s.prepareSearchAttributesForDb(request) 522 if err != nil { 523 return nil, err 524 } 525 526 return &sqlplugin.VisibilityRow{ 527 NamespaceID: request.NamespaceID, 528 WorkflowID: request.WorkflowID, 529 RunID: request.RunID, 530 StartTime: request.StartTime, 531 ExecutionTime: request.ExecutionTime, 532 WorkflowTypeName: request.WorkflowTypeName, 533 Status: int32(request.Status), 534 Memo: request.Memo.Data, 535 Encoding: request.Memo.EncodingType.String(), 536 TaskQueue: request.TaskQueue, 537 SearchAttributes: searchAttributes, 538 ParentWorkflowID: request.ParentWorkflowID, 539 ParentRunID: request.ParentRunID, 540 }, nil 541 } 542 543 func (s *VisibilityStore) prepareSearchAttributesForDb( 544 request *store.InternalVisibilityRequestBase, 545 ) (*sqlplugin.VisibilitySearchAttributes, error) { 546 if request.SearchAttributes == nil { 547 return nil, nil 548 } 549 550 saTypeMap, err := s.searchAttributesProvider.GetSearchAttributes( 551 s.GetIndexName(), 552 false, 553 ) 554 if err != nil { 555 return nil, serviceerror.NewUnavailable( 556 fmt.Sprintf("Unable to read search attributes types: %v", err)) 557 } 558 559 var searchAttributes sqlplugin.VisibilitySearchAttributes 560 searchAttributes, err = searchattribute.Decode(request.SearchAttributes, &saTypeMap, false) 561 if err != nil { 562 return nil, err 563 } 564 // This is to prevent existing tasks to fail indefinitely. 565 // If it's only invalid values error, then silently continue without them. 566 searchAttributes, err = s.ValidateCustomSearchAttributes(searchAttributes) 567 if err != nil { 568 if _, ok := err.(*store.VisibilityStoreInvalidValuesError); !ok { 569 return nil, err 570 } 571 } 572 573 for name, value := range searchAttributes { 574 if value == nil { 575 delete(searchAttributes, name) 576 continue 577 } 578 tp, err := saTypeMap.GetType(name) 579 if err != nil { 580 return nil, err 581 } 582 if tp == enumspb.INDEXED_VALUE_TYPE_DATETIME { 583 if dt, ok := value.(time.Time); ok { 584 searchAttributes[name] = dt.Format(time.RFC3339Nano) 585 } 586 } 587 } 588 return &searchAttributes, nil 589 } 590 591 func (s *VisibilityStore) rowToInfo( 592 row *sqlplugin.VisibilityRow, 593 nsName namespace.Name, 594 ) (*store.InternalWorkflowExecutionInfo, error) { 595 if row.ExecutionTime.UnixNano() == 0 { 596 row.ExecutionTime = row.StartTime 597 } 598 info := &store.InternalWorkflowExecutionInfo{ 599 WorkflowID: row.WorkflowID, 600 RunID: row.RunID, 601 TypeName: row.WorkflowTypeName, 602 StartTime: row.StartTime, 603 ExecutionTime: row.ExecutionTime, 604 Status: enumspb.WorkflowExecutionStatus(row.Status), 605 TaskQueue: row.TaskQueue, 606 Memo: persistence.NewDataBlob(row.Memo, row.Encoding), 607 } 608 if row.SearchAttributes != nil && len(*row.SearchAttributes) > 0 { 609 searchAttributes, err := s.processRowSearchAttributes(*row.SearchAttributes, nsName) 610 if err != nil { 611 return nil, err 612 } 613 info.SearchAttributes = searchAttributes 614 } 615 if row.CloseTime != nil { 616 info.CloseTime = *row.CloseTime 617 } 618 if row.HistoryLength != nil { 619 info.HistoryLength = *row.HistoryLength 620 } 621 if row.HistorySizeBytes != nil { 622 info.HistorySizeBytes = *row.HistorySizeBytes 623 } 624 if row.StateTransitionCount != nil { 625 info.StateTransitionCount = *row.StateTransitionCount 626 } 627 if row.ParentWorkflowID != nil { 628 info.ParentWorkflowID = *row.ParentWorkflowID 629 } 630 if row.ParentRunID != nil { 631 info.ParentRunID = *row.ParentRunID 632 } 633 return info, nil 634 } 635 636 func (s *VisibilityStore) processRowSearchAttributes( 637 rowSearchAttributes sqlplugin.VisibilitySearchAttributes, 638 nsName namespace.Name, 639 ) (*common.SearchAttributes, error) { 640 saTypeMap, err := s.searchAttributesProvider.GetSearchAttributes( 641 s.GetIndexName(), 642 false, 643 ) 644 if err != nil { 645 return nil, serviceerror.NewUnavailable( 646 fmt.Sprintf("Unable to read search attributes types: %v", err)) 647 } 648 // In SQLite, keyword list can return a string when there's only one element. 649 // This changes it into a slice. 650 for name, value := range rowSearchAttributes { 651 tp, err := saTypeMap.GetType(name) 652 if err != nil { 653 return nil, err 654 } 655 if tp == enumspb.INDEXED_VALUE_TYPE_KEYWORD_LIST { 656 switch v := value.(type) { 657 case []string: 658 // no-op 659 case string: 660 (rowSearchAttributes)[name] = []string{v} 661 default: 662 return nil, serviceerror.NewInternal( 663 fmt.Sprintf("Unexpected data type for keyword list: %T (expected list of strings)", v), 664 ) 665 } 666 } 667 } 668 searchAttributes, err := searchattribute.Encode(rowSearchAttributes, &saTypeMap) 669 if err != nil { 670 return nil, err 671 } 672 aliasedSas, err := searchattribute.AliasFields( 673 s.searchAttributesMapperProvider, 674 searchAttributes, 675 nsName.String(), 676 ) 677 if err != nil { 678 return nil, err 679 } 680 if aliasedSas != nil { 681 searchAttributes = aliasedSas 682 } 683 return searchAttributes, nil 684 } 685 686 func (s *VisibilityStore) buildQueryStringFromListRequest( 687 request *manager.ListWorkflowExecutionsRequest, 688 executionStatus enumspb.WorkflowExecutionStatus, 689 workflowID string, 690 workflowTypeName string, 691 ) string { 692 var queryTerms []string 693 694 switch executionStatus { 695 case enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED: 696 queryTerms = append( 697 queryTerms, 698 fmt.Sprintf( 699 "%s != %d", 700 searchattribute.ExecutionStatus, 701 int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING), 702 ), 703 ) 704 default: 705 queryTerms = append( 706 queryTerms, 707 fmt.Sprintf("%s = %d", searchattribute.ExecutionStatus, int32(executionStatus)), 708 ) 709 } 710 711 var timeAttr string 712 if executionStatus == enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING { 713 timeAttr = searchattribute.StartTime 714 } else { 715 timeAttr = searchattribute.CloseTime 716 } 717 queryTerms = append( 718 queryTerms, 719 fmt.Sprintf( 720 "%s BETWEEN '%s' AND '%s'", 721 timeAttr, 722 request.EarliestStartTime.UTC().Format(time.RFC3339Nano), 723 request.LatestStartTime.UTC().Format(time.RFC3339Nano), 724 ), 725 ) 726 727 if request.NamespaceDivision != "" { 728 queryTerms = append( 729 queryTerms, 730 fmt.Sprintf( 731 "%s = '%s'", 732 searchattribute.TemporalNamespaceDivision, 733 request.NamespaceDivision, 734 ), 735 ) 736 } else { 737 queryTerms = append( 738 queryTerms, 739 fmt.Sprintf("%s IS NULL", searchattribute.TemporalNamespaceDivision), 740 ) 741 } 742 743 if workflowID != "" { 744 queryTerms = append( 745 queryTerms, 746 fmt.Sprintf("%s = '%s'", searchattribute.WorkflowID, workflowID), 747 ) 748 } 749 750 if workflowTypeName != "" { 751 queryTerms = append( 752 queryTerms, 753 fmt.Sprintf("%s = '%s'", searchattribute.WorkflowType, workflowTypeName), 754 ) 755 } 756 757 return strings.Join(queryTerms, " AND ") 758 }