code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/cursor_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package sqlstore_test 17 18 import ( 19 "fmt" 20 "testing" 21 "time" 22 23 "code.vegaprotocol.io/vega/datanode/sqlstore" 24 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 func TestCursorPredicate(t *testing.T) { 30 type Cursor struct { 31 Foo int 32 Bar int `db:"baz"` 33 } 34 cursor := Cursor{Foo: 1, Bar: 2} 35 36 testCases := []struct { 37 name string 38 ordering sqlstore.TableOrdering 39 expectedPredicate string 40 expectedArgs []interface{} 41 }{ 42 { 43 name: "Single", 44 ordering: sqlstore.TableOrdering{ 45 sqlstore.NewColumnOrdering("foo", sqlstore.ASC), 46 }, 47 expectedPredicate: "(foo >= $1)", 48 expectedArgs: []any{1}, 49 }, 50 { 51 name: "Reversed", 52 ordering: sqlstore.TableOrdering{ 53 sqlstore.NewColumnOrdering("foo", sqlstore.DESC), 54 }, 55 expectedPredicate: "(foo <= $1)", 56 expectedArgs: []any{1}, 57 }, 58 { 59 name: "Composite", 60 ordering: sqlstore.TableOrdering{ 61 sqlstore.NewColumnOrdering("foo", sqlstore.ASC), 62 sqlstore.NewColumnOrdering("baz", sqlstore.DESC), 63 }, 64 expectedPredicate: "(foo > $1) OR (foo = $1 AND baz <= $2)", 65 expectedArgs: []any{1, 2}, 66 }, 67 } 68 69 for _, tc := range testCases { 70 t.Run(tc.name, func(tt *testing.T) { 71 predicate, args, err := sqlstore.CursorPredicate(nil, cursor, tc.ordering) 72 require.NoError(t, err) 73 assert.Equal(t, tc.expectedPredicate, predicate) 74 assert.Equal(t, tc.expectedArgs, args) 75 }) 76 } 77 } 78 79 func TestTableOrderingPrefix(t *testing.T) { 80 prefix := "prefix_str" 81 prefixSuffixed := fmt.Sprintf("%s.", prefix) 82 testCases := []struct { 83 name string 84 ordering sqlstore.TableOrdering 85 orderPrefixed string 86 orderNoPrefix string 87 }{ 88 { 89 name: "single ascending", // ascending, single column 90 ordering: sqlstore.TableOrdering{ 91 sqlstore.NewColumnOrdering("vega_time", sqlstore.ASC), 92 }, 93 orderPrefixed: fmt.Sprintf("ORDER BY %s.vega_time ASC", prefix), 94 orderNoPrefix: "ORDER BY vega_time ASC", 95 }, 96 { 97 name: "single descending", // descending, single column 98 ordering: sqlstore.TableOrdering{ 99 sqlstore.NewColumnOrdering("vega_time", sqlstore.DESC), 100 }, 101 orderPrefixed: fmt.Sprintf("ORDER BY %s.vega_time DESC", prefix), 102 orderNoPrefix: "ORDER BY vega_time DESC", 103 }, 104 { 105 name: "multiple ascending", // ascending, multiple columns 106 ordering: sqlstore.TableOrdering{ 107 sqlstore.NewColumnOrdering("vega_time", sqlstore.ASC), 108 sqlstore.NewColumnOrdering("tx_hash", sqlstore.ASC), 109 }, 110 orderPrefixed: fmt.Sprintf("ORDER BY %s.vega_time ASC,%s.tx_hash ASC", prefix, prefix), 111 orderNoPrefix: "ORDER BY vega_time ASC,tx_hash ASC", 112 }, 113 { 114 name: "multiple descending", // descending, multiple columns 115 ordering: sqlstore.TableOrdering{ 116 sqlstore.NewColumnOrdering("vega_time", sqlstore.DESC), 117 sqlstore.NewColumnOrdering("tx_hash", sqlstore.DESC), 118 }, 119 orderPrefixed: fmt.Sprintf("ORDER BY %s.vega_time DESC,%s.tx_hash DESC", prefix, prefix), 120 orderNoPrefix: "ORDER BY vega_time DESC,tx_hash DESC", 121 }, 122 { 123 name: "multiple mixed", // multiple columns, one descending, one ascending 124 ordering: sqlstore.TableOrdering{ 125 sqlstore.NewColumnOrdering("vega_time", sqlstore.DESC), 126 sqlstore.NewColumnOrdering("tx_hash", sqlstore.ASC), 127 }, 128 orderPrefixed: fmt.Sprintf("ORDER BY %s.vega_time DESC,%s.tx_hash ASC", prefix, prefix), 129 orderNoPrefix: "ORDER BY vega_time DESC,tx_hash ASC", 130 }, 131 } 132 for _, tc := range testCases { 133 t.Run(tc.name, func(tt *testing.T) { 134 require.Equal(t, tc.orderNoPrefix, tc.ordering.OrderByClause()) 135 tc.ordering.SetPrefixAll(prefix) 136 require.Equal(t, tc.orderPrefixed, tc.ordering.OrderByClause()) 137 tc.ordering.SetPrefixAll("") 138 require.Equal(t, tc.orderNoPrefix, tc.ordering.OrderByClause()) 139 // make sure that a prefix with the "." suffix included does not duplicate the "." 140 tc.ordering.SetPrefixAll(prefixSuffixed) 141 require.Equal(t, tc.orderPrefixed, tc.ordering.OrderByClause()) 142 }) 143 } 144 } 145 146 func TestCursor_Where(t *testing.T) { 147 type args struct { 148 Cmp string 149 args []interface{} 150 } 151 152 testCases := []struct { 153 name string 154 cursor sqlstore.CursorQueryParameter 155 args args 156 wantWhere string 157 wantArgs []interface{} 158 }{ 159 { 160 name: "Equal", 161 cursor: sqlstore.CursorQueryParameter{ 162 ColumnName: "vega_time", 163 Sort: sqlstore.ASC, 164 Cmp: sqlstore.EQ, 165 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 166 }, 167 args: args{ 168 args: nil, 169 }, 170 wantWhere: "vega_time = $1", 171 wantArgs: []interface{}{time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC)}, 172 }, 173 { 174 name: "Less than or equal", 175 cursor: sqlstore.CursorQueryParameter{ 176 ColumnName: "vega_time", 177 Sort: sqlstore.ASC, 178 Cmp: sqlstore.LE, 179 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 180 }, 181 args: args{ 182 args: []interface{}{"TEST"}, 183 }, 184 wantWhere: "vega_time <= $2", 185 wantArgs: []interface{}{"TEST", time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC)}, 186 }, 187 { 188 name: "Greater than or equal", 189 cursor: sqlstore.CursorQueryParameter{ 190 ColumnName: "vega_time", 191 Sort: sqlstore.ASC, 192 Cmp: sqlstore.GE, 193 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 194 }, 195 args: args{ 196 args: nil, 197 }, 198 wantWhere: "vega_time >= $1", 199 wantArgs: []interface{}{time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC)}, 200 }, 201 } 202 203 for _, tc := range testCases { 204 t.Run(tc.name, func(tt *testing.T) { 205 gotWhere, gotArgs := tc.cursor.Where(tc.args.args...) 206 assert.Equal(t, tc.wantWhere, gotWhere) 207 assert.Equal(t, tc.wantArgs, gotArgs) 208 }) 209 } 210 } 211 212 func TestCursor_OrderBy(t *testing.T) { 213 testCases := []struct { 214 name string 215 cursor sqlstore.CursorQueryParameter 216 wantOrder string 217 }{ 218 { 219 name: "Ascending", 220 cursor: sqlstore.CursorQueryParameter{ 221 ColumnName: "vega_time", 222 Sort: sqlstore.ASC, 223 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 224 }, 225 wantOrder: "vega_time ASC", 226 }, 227 { 228 name: "Descending", 229 cursor: sqlstore.CursorQueryParameter{ 230 ColumnName: "vega_time", 231 Sort: sqlstore.DESC, 232 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 233 }, 234 wantOrder: "vega_time DESC", 235 }, 236 } 237 238 for _, tc := range testCases { 239 t.Run(tc.name, func(tt *testing.T) { 240 got := tc.cursor.OrderBy() 241 assert.Equal(t, tc.wantOrder, got) 242 }) 243 } 244 } 245 246 func TestCursors_Where(t *testing.T) { 247 testCases := []struct { 248 name string 249 cursors sqlstore.CursorQueryParameters 250 wantWhere string 251 wantArgs []interface{} 252 }{ 253 { 254 name: "One cursor", 255 cursors: sqlstore.CursorQueryParameters{ 256 { 257 ColumnName: "vega_time", 258 Sort: sqlstore.ASC, 259 Cmp: sqlstore.EQ, 260 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 261 }, 262 }, 263 wantWhere: "vega_time = $1", 264 wantArgs: []interface{}{time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC)}, 265 }, 266 { 267 name: "Two cursors", 268 cursors: sqlstore.CursorQueryParameters{ 269 { 270 ColumnName: "vega_time", 271 Sort: sqlstore.ASC, 272 Cmp: sqlstore.EQ, 273 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 274 }, 275 { 276 ColumnName: "seq_num", 277 Sort: sqlstore.ASC, 278 Cmp: sqlstore.GE, 279 Value: 1, 280 }, 281 }, 282 wantWhere: "vega_time = $1 AND seq_num >= $2", 283 wantArgs: []interface{}{time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 1}, 284 }, 285 } 286 287 for _, tc := range testCases { 288 t.Run(tc.name, func(tt *testing.T) { 289 gotWhere, gotArgs := tc.cursors.Where() 290 assert.Equal(t, tc.wantWhere, gotWhere) 291 assert.Equal(t, tc.wantArgs, gotArgs) 292 }) 293 } 294 } 295 296 func TestCursors_OrderBy(t *testing.T) { 297 testCases := []struct { 298 name string 299 cursors sqlstore.CursorQueryParameters 300 wantOrder string 301 }{ 302 { 303 name: "One cursor", 304 cursors: sqlstore.CursorQueryParameters{ 305 sqlstore.CursorQueryParameter{ 306 ColumnName: "vega_time", 307 Sort: sqlstore.ASC, 308 Cmp: sqlstore.EQ, 309 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 310 }, 311 }, 312 wantOrder: "vega_time ASC", 313 }, 314 { 315 name: "Two cursors", 316 cursors: sqlstore.CursorQueryParameters{ 317 { 318 ColumnName: "vega_time", 319 Sort: sqlstore.ASC, 320 Cmp: sqlstore.EQ, 321 Value: time.Date(2022, 5, 9, 9, 0, 0, 0, time.UTC), 322 }, 323 { 324 ColumnName: "seq_num", 325 Sort: sqlstore.ASC, 326 Cmp: sqlstore.GE, 327 Value: 1, 328 }, 329 }, 330 wantOrder: "vega_time ASC, seq_num ASC", 331 }, 332 } 333 334 for _, tc := range testCases { 335 t.Run(tc.name, func(tt *testing.T) { 336 got := tc.cursors.OrderBy() 337 assert.Equal(t, tc.wantOrder, got) 338 }) 339 } 340 }