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  }