Skip to content

Commit 6eff4f5

Browse files
sid200727claude
andcommitted
feat(storage): migrate TransactionStore from CondInterpreter to squirrel (#1655)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Siddhi Khandelwal <siddhi.200727@gmail.com>
1 parent 239d7fd commit 6eff4f5

8 files changed

Lines changed: 544 additions & 376 deletions

File tree

token/services/storage/db/sql/common/querybuilder_test.go

Lines changed: 78 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"time"
1212

1313
sq "github.com/Masterminds/squirrel"
14-
q "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/storage/driver/sql/query"
1514
common2 "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/storage/driver/sql/query/common"
1615
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/storage/driver/sql/query/cond"
1716
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/storage/driver/sql/sqlite"
@@ -26,48 +25,48 @@ func TestTransactionSql(t *testing.T) {
2625
testCases := []struct {
2726
name string
2827
params driver2.QueryTransactionsParams
29-
expectedArgs []common2.Param
28+
expectedArgs []any
3029
expectedSql string
3130
}{
3231
{
3332
name: "No params",
3433
params: driver2.QueryTransactionsParams{},
35-
expectedSql: "1 = 1",
36-
expectedArgs: []common2.Param{},
34+
expectedSql: "",
35+
expectedArgs: []any{},
3736
},
3837
{
3938
name: "Only confirmed",
4039
params: driver2.QueryTransactionsParams{
4140
Statuses: []driver2.TxStatus{driver2.Confirmed},
4241
},
43-
expectedSql: "(status = $1)",
44-
expectedArgs: []common2.Param{driver2.Confirmed},
42+
expectedSql: "(status IN ($1))",
43+
expectedArgs: []any{driver2.Confirmed},
4544
},
4645
{
4746
name: "Pending or deleted",
4847
params: driver2.QueryTransactionsParams{
4948
Statuses: []driver2.TxStatus{driver2.Pending, driver2.Deleted},
5049
},
51-
expectedSql: "((status) IN (($1), ($2)))",
52-
expectedArgs: []common2.Param{driver2.Pending, driver2.Deleted},
50+
expectedSql: "(status IN ($1,$2))",
51+
expectedArgs: []any{driver2.Pending, driver2.Deleted},
5352
},
5453
{
5554
name: "Confirmed from any (only setting sender should return all)",
5655
params: driver2.QueryTransactionsParams{
5756
SenderWallet: "alice",
5857
Statuses: []driver2.TxStatus{driver2.Confirmed},
5958
},
60-
expectedSql: "(status = $1)",
61-
expectedArgs: []common2.Param{driver2.Confirmed},
59+
expectedSql: "(status IN ($1))",
60+
expectedArgs: []any{driver2.Confirmed},
6261
},
6362
{
6463
name: "Sender OR recipient matches",
6564
params: driver2.QueryTransactionsParams{
6665
SenderWallet: "alice",
6766
RecipientWallet: "bob",
6867
},
69-
expectedSql: "((sender_eid = $1) OR (recipient_eid = $2))",
70-
expectedArgs: []common2.Param{"alice", "bob"},
68+
expectedSql: "((sender_eid = $1 OR recipient_eid = $2))",
69+
expectedArgs: []any{"alice", "bob"},
7170
},
7271
{
7372
name: "Sender OR recipient matches, from last year",
@@ -76,17 +75,17 @@ func TestTransactionSql(t *testing.T) {
7675
RecipientWallet: "alice",
7776
From: &lastYear,
7877
},
79-
expectedSql: "((tbl.stored_at >= $1)) AND ((sender_eid = $2) OR (recipient_eid = $3))",
80-
expectedArgs: []common2.Param{&lastYear, "alice", "alice"},
78+
expectedSql: "(tbl.stored_at >= $1 AND (sender_eid = $2 OR recipient_eid = $3))",
79+
expectedArgs: []any{&lastYear, "alice", "alice"},
8180
},
8281
{
8382
name: "From last year to now",
8483
params: driver2.QueryTransactionsParams{
8584
To: &now,
8685
From: &lastYear,
8786
},
88-
expectedSql: "((tbl.stored_at >= $1) AND (tbl.stored_at <= $2))",
89-
expectedArgs: []common2.Param{&lastYear, &now},
87+
expectedSql: "(tbl.stored_at >= $1 AND tbl.stored_at <= $2)",
88+
expectedArgs: []any{&lastYear, &now},
9089
},
9190
{
9291
name: "Sender OR recipient matches, specific tx",
@@ -95,8 +94,8 @@ func TestTransactionSql(t *testing.T) {
9594
RecipientWallet: "bob",
9695
IDs: []string{"transactionID"},
9796
},
98-
expectedSql: "(tbl.tx_id = $1) AND ((sender_eid = $2) OR (recipient_eid = $3))",
99-
expectedArgs: []common2.Param{"transactionID", "alice", "bob"},
97+
expectedSql: "(tbl.tx_id IN ($1) AND (sender_eid = $2 OR recipient_eid = $3))",
98+
expectedArgs: []any{"transactionID", "alice", "bob"},
10099
},
101100
{
102101
name: "Sender OR recipient matches, specific tx ids",
@@ -105,22 +104,30 @@ func TestTransactionSql(t *testing.T) {
105104
RecipientWallet: "bob",
106105
IDs: []string{"transactionID1", "transactionID2", "transactionID3"},
107106
},
108-
expectedSql: "((tbl.tx_id) IN (($1), ($2), ($3))) AND ((sender_eid = $4) OR (recipient_eid = $5))",
109-
expectedArgs: []common2.Param{"transactionID1", "transactionID2", "transactionID3", "alice", "bob"},
107+
expectedSql: "(tbl.tx_id IN ($1,$2,$3) AND (sender_eid = $4 OR recipient_eid = $5))",
108+
expectedArgs: []any{"transactionID1", "transactionID2", "transactionID3", "alice", "bob"},
110109
},
111110
{
112111
name: "With Token Types",
113112
params: driver2.QueryTransactionsParams{
114113
TokenTypes: []token.Type{"Pineapple"},
115114
},
116-
expectedSql: "(token_type = $1)",
117-
expectedArgs: []common2.Param{"Pineapple"},
115+
expectedSql: "(token_type IN ($1))",
116+
expectedArgs: []any{token.Type("Pineapple")},
118117
},
119118
}
120119

121120
for _, tc := range testCases {
122121
t.Run(tc.name, func(t *testing.T) {
123-
actualSql, actualArgs := evalCondition(HasTransactionParams(tc.params, q.Table("tbl")))
122+
sqlizer := HasTransactionParams(tc.params, "tbl")
123+
if sqlizer == nil {
124+
assert.Empty(t, tc.expectedSql)
125+
assert.Empty(t, tc.expectedArgs)
126+
127+
return
128+
}
129+
actualSql, actualArgs, err := evalSqlizer(sqlizer)
130+
assert.NoError(t, err)
124131
assert.Equal(t, tc.expectedSql, actualSql)
125132
compareArgs(t, tc.expectedArgs, actualArgs)
126133
})
@@ -131,52 +138,52 @@ func TestMovementConditions(t *testing.T) {
131138
testCases := []struct {
132139
name string
133140
params driver2.QueryMovementsParams
134-
expectedArgs []common2.Param
141+
expectedArgs []any
135142
expectedSql string
136143
}{
137144
{
138145
name: "All",
139146
params: driver2.QueryMovementsParams{
140147
MovementDirection: driver2.All,
141148
},
142-
expectedSql: "(status != $1)",
143-
expectedArgs: []common2.Param{3},
149+
expectedSql: "(status <> $1)",
150+
expectedArgs: []any{driver2.Deleted},
144151
},
145152
{
146153
name: "Max 5",
147154
params: driver2.QueryMovementsParams{
148155
NumRecords: 5,
149156
MovementDirection: driver2.All,
150157
},
151-
expectedSql: "(status != $1)",
152-
expectedArgs: []common2.Param{3},
158+
expectedSql: "(status <> $1)",
159+
expectedArgs: []any{driver2.Deleted},
153160
},
154161
{
155162
name: "Only enrollment ids",
156163
params: driver2.QueryMovementsParams{
157164
EnrollmentIDs: []string{"eid1", "eid2", "eid3"},
158165
MovementDirection: driver2.All,
159166
},
160-
expectedSql: "((enrollment_id) IN (($1), ($2), ($3))) AND (status != $4)",
161-
expectedArgs: []common2.Param{"eid1", "eid2", "eid3", 3},
167+
expectedSql: "(enrollment_id IN ($1,$2,$3) AND status <> $4)",
168+
expectedArgs: []any{"eid1", "eid2", "eid3", driver2.Deleted},
162169
},
163170
{
164171
name: "Only confirmed",
165172
params: driver2.QueryMovementsParams{
166173
TxStatuses: []driver2.TxStatus{driver2.Confirmed},
167174
MovementDirection: driver2.All,
168175
},
169-
expectedSql: "(status = $1)",
170-
expectedArgs: []common2.Param{driver2.Confirmed},
176+
expectedSql: "(status IN ($1))",
177+
expectedArgs: []any{driver2.Confirmed},
171178
},
172179
{
173180
name: "Pending and deleted",
174181
params: driver2.QueryMovementsParams{
175182
TxStatuses: []driver2.TxStatus{driver2.Pending, driver2.Deleted},
176183
MovementDirection: driver2.All,
177184
},
178-
expectedSql: "((status) IN (($1), ($2)))",
179-
expectedArgs: []common2.Param{driver2.Pending, driver2.Deleted},
185+
expectedSql: "(status IN ($1,$2))",
186+
expectedArgs: []any{driver2.Pending, driver2.Deleted},
180187
},
181188
{
182189
name: "Confirmed from alice",
@@ -185,8 +192,8 @@ func TestMovementConditions(t *testing.T) {
185192
TxStatuses: []driver2.TxStatus{driver2.Confirmed},
186193
MovementDirection: driver2.All,
187194
},
188-
expectedSql: "(enrollment_id = $1) AND (status = $2)",
189-
expectedArgs: []common2.Param{"alice", driver2.Confirmed},
195+
expectedSql: "(enrollment_id IN ($1) AND status IN ($2))",
196+
expectedArgs: []any{"alice", driver2.Confirmed},
190197
},
191198
{
192199
name: "Confirmed ABC and XYZ from alice",
@@ -196,8 +203,8 @@ func TestMovementConditions(t *testing.T) {
196203
TokenTypes: []token.Type{"ABC", "XYZ"},
197204
MovementDirection: driver2.All,
198205
},
199-
expectedSql: "(enrollment_id = $1) AND ((token_type) IN (($2), ($3))) AND (status = $4)",
200-
expectedArgs: []common2.Param{"alice", "ABC", "XYZ", driver2.Confirmed},
206+
expectedSql: "(enrollment_id IN ($1) AND token_type IN ($2,$3) AND status IN ($4))",
207+
expectedArgs: []any{"alice", token.Type("ABC"), token.Type("XYZ"), driver2.Confirmed},
201208
},
202209
{
203210
name: "Max 5 confirmed ABC and XYZ from alice",
@@ -208,8 +215,8 @@ func TestMovementConditions(t *testing.T) {
208215
NumRecords: 5,
209216
MovementDirection: driver2.All,
210217
},
211-
expectedSql: "(enrollment_id = $1) AND ((token_type) IN (($2), ($3))) AND (status = $4)",
212-
expectedArgs: []common2.Param{"alice", "ABC", "XYZ", driver2.Confirmed},
218+
expectedSql: "(enrollment_id IN ($1) AND token_type IN ($2,$3) AND status IN ($4))",
219+
expectedArgs: []any{"alice", token.Type("ABC"), token.Type("XYZ"), driver2.Confirmed},
213220
},
214221
{
215222
name: "Sent XYZ from alice",
@@ -218,8 +225,8 @@ func TestMovementConditions(t *testing.T) {
218225
TokenTypes: []token.Type{"XYZ"},
219226
MovementDirection: driver2.Sent,
220227
},
221-
expectedSql: "(enrollment_id = $1) AND (token_type = $2) AND (status != $3) AND (amount < $4)",
222-
expectedArgs: []common2.Param{"alice", "XYZ", 3, 0},
228+
expectedSql: "(enrollment_id IN ($1) AND token_type IN ($2) AND status <> $3 AND amount < $4)",
229+
expectedArgs: []any{"alice", token.Type("XYZ"), driver2.Deleted, 0},
223230
},
224231
{
225232
name: "2 last pending received",
@@ -229,14 +236,15 @@ func TestMovementConditions(t *testing.T) {
229236
MovementDirection: driver2.Received,
230237
NumRecords: 2,
231238
},
232-
expectedSql: "(status = $1) AND (amount > $2)",
233-
expectedArgs: []common2.Param{driver2.Pending, 0},
239+
expectedSql: "(status IN ($1) AND amount > $2)",
240+
expectedArgs: []any{driver2.Pending, 0},
234241
},
235242
}
236243

237244
for _, tc := range testCases {
238245
t.Run(tc.name, func(t *testing.T) {
239-
actualSql, actualArgs := evalCondition(HasMovementsParams(tc.params))
246+
actualSql, actualArgs, err := evalSqlizer(HasMovementsParams(tc.params))
247+
assert.NoError(t, err)
240248
assert.Equal(t, tc.expectedSql, actualSql)
241249
compareArgs(t, tc.expectedArgs, actualArgs)
242250
})
@@ -334,24 +342,6 @@ func TestTokenSql(t *testing.T) {
334342
assert.Len(t, args, 6)
335343
}
336344

337-
func evalCondition(condition cond.Condition) (string, []common2.Param) {
338-
sb := common2.NewBuilder()
339-
condition.WriteString(sqlite.NewConditionInterpreter(), sb)
340-
actualSql, actualArgs := sb.Build()
341-
342-
return actualSql, actualArgs
343-
}
344-
345-
// evalSqlizer converts a sq.Sqlizer to (sql, args) using Dollar placeholder.
346-
func evalSqlizer(s sq.Sqlizer) (string, []any, error) {
347-
sql, args, err := s.ToSql()
348-
if err != nil {
349-
return "", nil, err
350-
}
351-
sql, err = sq.Dollar.ReplacePlaceholders(sql)
352-
return sql, args, err
353-
}
354-
355345
func TestTokenSqlNoJoin(t *testing.T) {
356346
testCases := []struct {
357347
name string
@@ -452,12 +442,36 @@ func TestIn(t *testing.T) {
452442
assert.Equal(t, []any{"eid1", "eid2", "eid3"}, args)
453443
}
454444

445+
func evalCondition(condition cond.Condition) (string, []common2.Param) {
446+
sb := common2.NewBuilder()
447+
condition.WriteString(sqlite.NewConditionInterpreter(), sb)
448+
actualSql, actualArgs := sb.Build()
449+
450+
return actualSql, actualArgs
451+
}
452+
453+
// evalSqlizer converts a sq.Sqlizer to (sql, args) using Dollar placeholder.
454+
func evalSqlizer(s sq.Sqlizer) (string, []any, error) {
455+
if s == nil {
456+
return "", []any{}, nil
457+
}
458+
sql, args, err := s.ToSql()
459+
if err != nil {
460+
return "", nil, err
461+
}
462+
sql, err = sq.Dollar.ReplacePlaceholders(sql)
463+
464+
return sql, args, err
465+
}
466+
455467
func compareArgs(t *testing.T, expected, actual []any) {
456468
t.Helper()
457469
assert.Len(t, actual, len(expected))
458-
// assert.Equal(t, tc.expectedArgs, actualArgs)
459470

460471
for i := range expected {
472+
if i >= len(actual) {
473+
break
474+
}
461475
switch expected[i].(type) {
462476
case *time.Time:
463477
exp, _ := expected[i].(*time.Time)

0 commit comments

Comments
 (0)