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 }