go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/postgresql/visibility.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 postgresql
    26  
    27  import (
    28  	"context"
    29  	"database/sql"
    30  	"errors"
    31  	"fmt"
    32  	"strings"
    33  
    34  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    35  	"go.temporal.io/server/common/persistence/visibility/store"
    36  )
    37  
    38  const (
    39  	templateCreateWorkflowExecutionStarted = `INSERT INTO executions_visibility (` +
    40  		`namespace_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, status, memo, encoding, task_queue) ` +
    41  		`VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
    42           ON CONFLICT (namespace_id, run_id) DO NOTHING`
    43  
    44  	templateCreateWorkflowExecutionClosed = `INSERT INTO executions_visibility (` +
    45  		`namespace_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, close_time, status, history_length, memo, encoding, task_queue) ` +
    46  		`VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
    47  		ON CONFLICT (namespace_id, run_id) DO UPDATE 
    48  		  SET workflow_id = excluded.workflow_id,
    49  		      start_time = excluded.start_time,
    50  		      execution_time = excluded.execution_time,
    51                workflow_type_name = excluded.workflow_type_name,
    52  			  close_time = excluded.close_time,
    53  			  status = excluded.status,
    54  			  history_length = excluded.history_length,
    55  			  memo = excluded.memo,
    56  			  encoding = excluded.encoding,
    57  			  task_queue = excluded.task_queue`
    58  
    59  	// RunID condition is needed for correct pagination
    60  	templateConditions1 = ` AND namespace_id = $1
    61  		 AND start_time >= $2
    62  		 AND start_time <= $3
    63  		 AND ((run_id > $4 and start_time = $5) OR (start_time < $6))
    64           ORDER BY start_time DESC, run_id
    65           LIMIT $7`
    66  
    67  	templateConditions2 = ` AND namespace_id = $2
    68  		 AND start_time >= $3
    69  		 AND start_time <= $4
    70  		 AND ((run_id > $5 and start_time = $6) OR (start_time < $7))
    71           ORDER BY start_time DESC, run_id
    72  		 LIMIT $8`
    73  
    74  	templateConditionsClosedWorkflow1 = ` AND namespace_id = $1
    75  		 AND close_time >= $2
    76  		 AND close_time <= $3
    77  		 AND ((run_id > $4 and close_time = $5) OR (close_time < $6))
    78           ORDER BY close_time DESC, run_id
    79           LIMIT $7`
    80  
    81  	templateConditionsClosedWorkflow2 = ` AND namespace_id = $2
    82  		 AND close_time >= $3
    83  		 AND close_time <= $4
    84   		 AND ((run_id > $5 and close_time = $6) OR (close_time < $7))
    85           ORDER BY close_time DESC, run_id
    86           LIMIT $8`
    87  
    88  	templateOpenFieldNames = `workflow_id, run_id, start_time, execution_time, workflow_type_name, status, memo, encoding, task_queue`
    89  	templateOpenSelect     = `SELECT ` + templateOpenFieldNames + ` FROM executions_visibility WHERE status = 1 `
    90  
    91  	templateClosedSelect = `SELECT ` + templateOpenFieldNames + `, close_time, history_length
    92  		 FROM executions_visibility WHERE status != 1 `
    93  
    94  	templateGetOpenWorkflowExecutions = templateOpenSelect + templateConditions1
    95  
    96  	templateGetClosedWorkflowExecutions = templateClosedSelect + templateConditionsClosedWorkflow1
    97  
    98  	templateGetOpenWorkflowExecutionsByType = templateOpenSelect + `AND workflow_type_name = $1` + templateConditions2
    99  
   100  	templateGetClosedWorkflowExecutionsByType = templateClosedSelect + `AND workflow_type_name = $1` + templateConditionsClosedWorkflow2
   101  
   102  	templateGetOpenWorkflowExecutionsByID = templateOpenSelect + `AND workflow_id = $1` + templateConditions2
   103  
   104  	templateGetClosedWorkflowExecutionsByID = templateClosedSelect + `AND workflow_id = $1` + templateConditionsClosedWorkflow2
   105  
   106  	templateGetClosedWorkflowExecutionsByStatus = templateClosedSelect + `AND status = $1` + templateConditionsClosedWorkflow2
   107  
   108  	templateGetClosedWorkflowExecution = `SELECT workflow_id, run_id, start_time, execution_time, memo, encoding, close_time, workflow_type_name, status, history_length, task_queue
   109  		 FROM executions_visibility
   110  		 WHERE namespace_id = $1 AND status != 1
   111  		 AND run_id = $2`
   112  
   113  	templateGetWorkflowExecution = `
   114  		SELECT
   115  			workflow_id,
   116  			run_id,
   117  			start_time,
   118  			execution_time,
   119  			memo,
   120  			encoding,
   121  			close_time,
   122  			workflow_type_name,
   123  			status,
   124  			history_length,
   125  			task_queue
   126  		FROM executions_visibility
   127  		WHERE namespace_id = $1 AND run_id = $2`
   128  
   129  	templateDeleteWorkflowExecution = "DELETE FROM executions_visibility WHERE namespace_id = $1 AND run_id = $2"
   130  )
   131  
   132  var errCloseParams = errors.New("missing one of {closeTime, historyLength} params")
   133  
   134  // InsertIntoVisibility inserts a row into visibility table. If an row already exist,
   135  // its left as such and no update will be made
   136  func (pdb *db) InsertIntoVisibility(
   137  	ctx context.Context,
   138  	row *sqlplugin.VisibilityRow,
   139  ) (sql.Result, error) {
   140  	row.StartTime = pdb.converter.ToPostgreSQLDateTime(row.StartTime)
   141  	row.ExecutionTime = pdb.converter.ToPostgreSQLDateTime(row.ExecutionTime)
   142  	return pdb.conn.ExecContext(ctx,
   143  		templateCreateWorkflowExecutionStarted,
   144  		row.NamespaceID,
   145  		row.WorkflowID,
   146  		row.RunID,
   147  		row.StartTime,
   148  		row.ExecutionTime,
   149  		row.WorkflowTypeName,
   150  		row.Status,
   151  		row.Memo,
   152  		row.Encoding,
   153  		row.TaskQueue,
   154  	)
   155  }
   156  
   157  // ReplaceIntoVisibility replaces an existing row if it exist or creates a new row in visibility table
   158  func (pdb *db) ReplaceIntoVisibility(
   159  	ctx context.Context,
   160  	row *sqlplugin.VisibilityRow,
   161  ) (sql.Result, error) {
   162  	switch {
   163  	case row.CloseTime != nil && row.HistoryLength != nil:
   164  		row.StartTime = pdb.converter.ToPostgreSQLDateTime(row.StartTime)
   165  		row.ExecutionTime = pdb.converter.ToPostgreSQLDateTime(row.ExecutionTime)
   166  		closeTime := pdb.converter.ToPostgreSQLDateTime(*row.CloseTime)
   167  		return pdb.conn.ExecContext(ctx,
   168  			templateCreateWorkflowExecutionClosed,
   169  			row.NamespaceID,
   170  			row.WorkflowID,
   171  			row.RunID,
   172  			row.StartTime,
   173  			row.ExecutionTime,
   174  			row.WorkflowTypeName,
   175  			closeTime,
   176  			row.Status,
   177  			*row.HistoryLength,
   178  			row.Memo,
   179  			row.Encoding,
   180  			row.TaskQueue,
   181  		)
   182  	default:
   183  		return nil, errCloseParams
   184  	}
   185  }
   186  
   187  // DeleteFromVisibility deletes a row from visibility table if it exist
   188  func (pdb *db) DeleteFromVisibility(
   189  	ctx context.Context,
   190  	filter sqlplugin.VisibilityDeleteFilter,
   191  ) (sql.Result, error) {
   192  	return pdb.conn.ExecContext(ctx,
   193  		templateDeleteWorkflowExecution,
   194  		filter.NamespaceID,
   195  		filter.RunID,
   196  	)
   197  }
   198  
   199  // SelectFromVisibility reads one or more rows from visibility table
   200  func (pdb *db) SelectFromVisibility(
   201  	ctx context.Context,
   202  	filter sqlplugin.VisibilitySelectFilter,
   203  ) ([]sqlplugin.VisibilityRow, error) {
   204  	var err error
   205  	var rows []sqlplugin.VisibilityRow
   206  	if filter.MinTime != nil {
   207  		*filter.MinTime = pdb.converter.ToPostgreSQLDateTime(*filter.MinTime)
   208  	}
   209  	if filter.MaxTime != nil {
   210  		*filter.MaxTime = pdb.converter.ToPostgreSQLDateTime(*filter.MaxTime)
   211  	}
   212  	// If filter.Status == 0 (UNSPECIFIED) then only closed workflows will be returned (all excluding 1 (RUNNING)).
   213  	switch {
   214  	case filter.MinTime == nil && filter.RunID != nil && filter.Status != 1:
   215  		var row sqlplugin.VisibilityRow
   216  		err = pdb.conn.GetContext(ctx,
   217  			&row,
   218  			templateGetClosedWorkflowExecution,
   219  			filter.NamespaceID,
   220  			*filter.RunID,
   221  		)
   222  		if err == nil {
   223  			rows = append(rows, row)
   224  		}
   225  	case filter.MinTime != nil && filter.MaxTime != nil &&
   226  		filter.WorkflowID != nil && filter.RunID != nil && filter.PageSize != nil:
   227  		qry := templateGetOpenWorkflowExecutionsByID
   228  		if filter.Status != 1 {
   229  			qry = templateGetClosedWorkflowExecutionsByID
   230  		}
   231  		err = pdb.conn.SelectContext(ctx,
   232  			&rows,
   233  			qry,
   234  			*filter.WorkflowID,
   235  			filter.NamespaceID,
   236  			*filter.MinTime,
   237  			*filter.MaxTime,
   238  			*filter.RunID,
   239  			*filter.MaxTime,
   240  			*filter.MaxTime,
   241  			*filter.PageSize)
   242  	case filter.MinTime != nil && filter.MaxTime != nil &&
   243  		filter.WorkflowTypeName != nil && filter.RunID != nil && filter.PageSize != nil:
   244  		qry := templateGetOpenWorkflowExecutionsByType
   245  		if filter.Status != 1 {
   246  			qry = templateGetClosedWorkflowExecutionsByType
   247  		}
   248  		err = pdb.conn.SelectContext(ctx,
   249  			&rows,
   250  			qry,
   251  			*filter.WorkflowTypeName,
   252  			filter.NamespaceID,
   253  			*filter.MinTime,
   254  			*filter.MaxTime,
   255  			*filter.RunID,
   256  			*filter.MaxTime,
   257  			*filter.MaxTime,
   258  			*filter.PageSize)
   259  	case filter.MinTime != nil && filter.MaxTime != nil &&
   260  		filter.RunID != nil && filter.PageSize != nil &&
   261  		filter.Status != 0 && filter.Status != 1: // 0 is UNSPECIFIED, 1 is RUNNING
   262  		err = pdb.conn.SelectContext(ctx,
   263  			&rows,
   264  			templateGetClosedWorkflowExecutionsByStatus,
   265  			filter.Status,
   266  			filter.NamespaceID,
   267  			*filter.MinTime,
   268  			*filter.MaxTime,
   269  			*filter.RunID,
   270  			*filter.MaxTime,
   271  			*filter.MaxTime,
   272  			*filter.PageSize)
   273  	case filter.MinTime != nil && filter.MaxTime != nil &&
   274  		filter.RunID != nil && filter.PageSize != nil:
   275  		qry := templateGetOpenWorkflowExecutions
   276  		if filter.Status != 1 {
   277  			qry = templateGetClosedWorkflowExecutions
   278  		}
   279  		err = pdb.conn.SelectContext(ctx,
   280  			&rows,
   281  			qry,
   282  			filter.NamespaceID,
   283  			*filter.MinTime,
   284  			*filter.MaxTime,
   285  			*filter.RunID,
   286  			*filter.MaxTime,
   287  			*filter.MaxTime,
   288  			*filter.PageSize)
   289  	default:
   290  		return nil, fmt.Errorf("invalid query filter")
   291  	}
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  	for i := range rows {
   296  		pdb.processRowFromDB(&rows[i])
   297  	}
   298  	return rows, nil
   299  }
   300  
   301  // GetFromVisibility reads one row from visibility table
   302  func (pdb *db) GetFromVisibility(
   303  	ctx context.Context,
   304  	filter sqlplugin.VisibilityGetFilter,
   305  ) (*sqlplugin.VisibilityRow, error) {
   306  	var row sqlplugin.VisibilityRow
   307  	err := pdb.conn.GetContext(ctx,
   308  		&row,
   309  		templateGetWorkflowExecution,
   310  		filter.NamespaceID,
   311  		filter.RunID,
   312  	)
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	pdb.processRowFromDB(&row)
   317  	return &row, nil
   318  }
   319  
   320  func (pdb *db) CountFromVisibility(
   321  	ctx context.Context,
   322  	filter sqlplugin.VisibilitySelectFilter,
   323  ) (int64, error) {
   324  	return 0, store.OperationNotSupportedErr
   325  }
   326  
   327  func (pdb *db) CountGroupByFromVisibility(
   328  	ctx context.Context,
   329  	filter sqlplugin.VisibilitySelectFilter,
   330  ) ([]sqlplugin.VisibilityCountRow, error) {
   331  	return nil, store.OperationNotSupportedErr
   332  }
   333  
   334  func (pdb *db) processRowFromDB(row *sqlplugin.VisibilityRow) {
   335  	row.StartTime = pdb.converter.FromPostgreSQLDateTime(row.StartTime)
   336  	row.ExecutionTime = pdb.converter.FromPostgreSQLDateTime(row.ExecutionTime)
   337  	if row.CloseTime != nil {
   338  		closeTime := pdb.converter.FromPostgreSQLDateTime(*row.CloseTime)
   339  		row.CloseTime = &closeTime
   340  	}
   341  	// need to trim the run ID, or otherwise the returned value will
   342  	//  come with lots of trailing spaces, probably due to the CHAR(64) type
   343  	row.RunID = strings.TrimSpace(row.RunID)
   344  }