go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/postgresql/visibility_v12.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 "fmt" 31 "strings" 32 33 "go.temporal.io/server/common/persistence/sql/sqlplugin" 34 ) 35 36 var ( 37 templateInsertWorkflowExecution = fmt.Sprintf( 38 `INSERT INTO executions_visibility (%s) 39 VALUES (%s) 40 ON CONFLICT (namespace_id, run_id) DO NOTHING`, 41 strings.Join(sqlplugin.DbFields, ", "), 42 sqlplugin.BuildNamedPlaceholder(sqlplugin.DbFields...), 43 ) 44 45 templateUpsertWorkflowExecution = fmt.Sprintf( 46 `INSERT INTO executions_visibility (%s) 47 VALUES (%s) 48 %s`, 49 strings.Join(sqlplugin.DbFields, ", "), 50 sqlplugin.BuildNamedPlaceholder(sqlplugin.DbFields...), 51 buildOnDuplicateKeyUpdate(sqlplugin.DbFields...), 52 ) 53 54 templateDeleteWorkflowExecution_v12 = ` 55 DELETE FROM executions_visibility 56 WHERE namespace_id = :namespace_id AND run_id = :run_id` 57 58 templateGetWorkflowExecution_v12 = fmt.Sprintf( 59 `SELECT %s FROM executions_visibility 60 WHERE namespace_id = :namespace_id AND run_id = :run_id`, 61 strings.Join(sqlplugin.DbFields, ", "), 62 ) 63 ) 64 65 func buildOnDuplicateKeyUpdate(fields ...string) string { 66 items := make([]string, len(fields)) 67 for i, field := range fields { 68 items[i] = fmt.Sprintf("%s = excluded.%s", field, field) 69 } 70 return fmt.Sprintf( 71 "ON CONFLICT (namespace_id, run_id) DO UPDATE SET %s", 72 strings.Join(items, ", "), 73 ) 74 } 75 76 // InsertIntoVisibility inserts a row into visibility table. If an row already exist, 77 // its left as such and no update will be made 78 func (pdb *dbV12) InsertIntoVisibility( 79 ctx context.Context, 80 row *sqlplugin.VisibilityRow, 81 ) (sql.Result, error) { 82 finalRow := pdb.prepareRowForDB(row) 83 return pdb.conn.NamedExecContext(ctx, templateInsertWorkflowExecution, finalRow) 84 } 85 86 // ReplaceIntoVisibility replaces an existing row if it exist or creates a new row in visibility table 87 func (pdb *dbV12) ReplaceIntoVisibility( 88 ctx context.Context, 89 row *sqlplugin.VisibilityRow, 90 ) (sql.Result, error) { 91 finalRow := pdb.prepareRowForDB(row) 92 return pdb.conn.NamedExecContext(ctx, templateUpsertWorkflowExecution, finalRow) 93 } 94 95 // DeleteFromVisibility deletes a row from visibility table if it exist 96 func (pdb *dbV12) DeleteFromVisibility( 97 ctx context.Context, 98 filter sqlplugin.VisibilityDeleteFilter, 99 ) (sql.Result, error) { 100 return pdb.conn.NamedExecContext(ctx, templateDeleteWorkflowExecution_v12, filter) 101 } 102 103 // SelectFromVisibility reads one or more rows from visibility table 104 func (pdb *dbV12) SelectFromVisibility( 105 ctx context.Context, 106 filter sqlplugin.VisibilitySelectFilter, 107 ) ([]sqlplugin.VisibilityRow, error) { 108 if len(filter.Query) == 0 { 109 // backward compatibility for existing tests 110 err := sqlplugin.GenerateSelectQuery(&filter, pdb.converter.ToPostgreSQLDateTime) 111 if err != nil { 112 return nil, err 113 } 114 } 115 116 // Rebind will replace default placeholder `?` with the right placeholder for PostgreSQL. 117 filter.Query = pdb.db.db.Rebind(filter.Query) 118 var rows []sqlplugin.VisibilityRow 119 err := pdb.conn.SelectContext(ctx, &rows, filter.Query, filter.QueryArgs...) 120 if err != nil { 121 return nil, err 122 } 123 for i := range rows { 124 err = pdb.processRowFromDB(&rows[i]) 125 if err != nil { 126 return nil, err 127 } 128 } 129 return rows, nil 130 } 131 132 // GetFromVisibility reads one row from visibility table 133 func (pdb *dbV12) GetFromVisibility( 134 ctx context.Context, 135 filter sqlplugin.VisibilityGetFilter, 136 ) (*sqlplugin.VisibilityRow, error) { 137 var row sqlplugin.VisibilityRow 138 stmt, err := pdb.conn.PrepareNamedContext(ctx, templateGetWorkflowExecution_v12) 139 if err != nil { 140 return nil, err 141 } 142 err = stmt.GetContext(ctx, &row, filter) 143 if err != nil { 144 return nil, err 145 } 146 err = pdb.processRowFromDB(&row) 147 if err != nil { 148 return nil, err 149 } 150 return &row, nil 151 } 152 153 func (pdb *dbV12) CountFromVisibility( 154 ctx context.Context, 155 filter sqlplugin.VisibilitySelectFilter, 156 ) (int64, error) { 157 var count int64 158 filter.Query = pdb.db.db.Rebind(filter.Query) 159 err := pdb.conn.GetContext(ctx, &count, filter.Query, filter.QueryArgs...) 160 if err != nil { 161 return 0, err 162 } 163 return count, nil 164 } 165 166 func (pdb *dbV12) CountGroupByFromVisibility( 167 ctx context.Context, 168 filter sqlplugin.VisibilitySelectFilter, 169 ) ([]sqlplugin.VisibilityCountRow, error) { 170 filter.Query = pdb.db.db.Rebind(filter.Query) 171 rows, err := pdb.db.db.QueryContext(ctx, filter.Query, filter.QueryArgs...) 172 if err != nil { 173 return nil, err 174 } 175 defer rows.Close() 176 return sqlplugin.ParseCountGroupByRows(rows, filter.GroupBy) 177 } 178 179 func (pdb *dbV12) prepareRowForDB(row *sqlplugin.VisibilityRow) *sqlplugin.VisibilityRow { 180 if row == nil { 181 return nil 182 } 183 finalRow := *row 184 finalRow.StartTime = pdb.converter.ToPostgreSQLDateTime(finalRow.StartTime) 185 finalRow.ExecutionTime = pdb.converter.ToPostgreSQLDateTime(finalRow.ExecutionTime) 186 if finalRow.CloseTime != nil { 187 *finalRow.CloseTime = pdb.converter.ToPostgreSQLDateTime(*finalRow.CloseTime) 188 } 189 return &finalRow 190 } 191 192 func (pdb *dbV12) processRowFromDB(row *sqlplugin.VisibilityRow) error { 193 if row == nil { 194 return nil 195 } 196 row.StartTime = pdb.converter.FromPostgreSQLDateTime(row.StartTime) 197 row.ExecutionTime = pdb.converter.FromPostgreSQLDateTime(row.ExecutionTime) 198 if row.CloseTime != nil { 199 closeTime := pdb.converter.FromPostgreSQLDateTime(*row.CloseTime) 200 row.CloseTime = &closeTime 201 } 202 if row.SearchAttributes != nil { 203 for saName, saValue := range *row.SearchAttributes { 204 switch typedSaValue := saValue.(type) { 205 case []interface{}: 206 // the only valid type is slice of strings 207 strSlice := make([]string, len(typedSaValue)) 208 for i, item := range typedSaValue { 209 switch v := item.(type) { 210 case string: 211 strSlice[i] = v 212 default: 213 return fmt.Errorf("Unexpected data type in keyword list: %T (expected string)", v) 214 } 215 } 216 (*row.SearchAttributes)[saName] = strSlice 217 default: 218 // no-op 219 } 220 } 221 } 222 // need to trim the run ID, or otherwise the returned value will 223 // come with lots of trailing spaces, probably due to the CHAR(64) type 224 row.RunID = strings.TrimSpace(row.RunID) 225 return nil 226 }