Fix Statement Where clone array corruption in v2
Method-chaining in gorm is predicated on a `Clause`'s `MergeClause`
method ensuring that the two clauses are disconnected in terms of
pointers (at least in the Wherec case).
However, the original Where implementation used `append`, which
only returns a new instance if the backing array needs to be resized.
In some cases, this is true. Practically, go doubles the size of the
slice once it gets full, so the following slice `append` calls would
result in a new slice:
* 0 -> 1
* 1 -> 2
* 2 -> 4
* 4 -> 8
* and so on.
So, when the number of "where" conditions was 0, 1, 2, or 4, method-chaining
would work as expected. However, when it was 3, 5, 6, or 7, modifying the
copy would modify the original.
This also updates the "order by", "group by" and "set" clauses.
2020-06-07 23:41:54 +03:00
|
|
|
package gorm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
2020-06-08 04:10:27 +03:00
|
|
|
|
Fix Statement Where clone array corruption in v2
Method-chaining in gorm is predicated on a `Clause`'s `MergeClause`
method ensuring that the two clauses are disconnected in terms of
pointers (at least in the Wherec case).
However, the original Where implementation used `append`, which
only returns a new instance if the backing array needs to be resized.
In some cases, this is true. Practically, go doubles the size of the
slice once it gets full, so the following slice `append` calls would
result in a new slice:
* 0 -> 1
* 1 -> 2
* 2 -> 4
* 4 -> 8
* and so on.
So, when the number of "where" conditions was 0, 1, 2, or 4, method-chaining
would work as expected. However, when it was 3, 5, 6, or 7, modifying the
copy would modify the original.
This also updates the "order by", "group by" and "set" clauses.
2020-06-07 23:41:54 +03:00
|
|
|
"gorm.io/gorm/clause"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestWhereCloneCorruption(t *testing.T) {
|
|
|
|
for whereCount := 1; whereCount <= 8; whereCount++ {
|
|
|
|
t.Run(fmt.Sprintf("w=%d", whereCount), func(t *testing.T) {
|
|
|
|
s := new(Statement)
|
|
|
|
for w := 0; w < whereCount; w++ {
|
|
|
|
s = s.clone()
|
|
|
|
s.AddClause(clause.Where{
|
2020-06-08 06:38:51 +03:00
|
|
|
Exprs: s.BuildCondition(fmt.Sprintf("where%d", w)),
|
Fix Statement Where clone array corruption in v2
Method-chaining in gorm is predicated on a `Clause`'s `MergeClause`
method ensuring that the two clauses are disconnected in terms of
pointers (at least in the Wherec case).
However, the original Where implementation used `append`, which
only returns a new instance if the backing array needs to be resized.
In some cases, this is true. Practically, go doubles the size of the
slice once it gets full, so the following slice `append` calls would
result in a new slice:
* 0 -> 1
* 1 -> 2
* 2 -> 4
* 4 -> 8
* and so on.
So, when the number of "where" conditions was 0, 1, 2, or 4, method-chaining
would work as expected. However, when it was 3, 5, 6, or 7, modifying the
copy would modify the original.
This also updates the "order by", "group by" and "set" clauses.
2020-06-07 23:41:54 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
s1 := s.clone()
|
|
|
|
s1.AddClause(clause.Where{
|
2020-06-08 06:38:51 +03:00
|
|
|
Exprs: s.BuildCondition("FINAL1"),
|
Fix Statement Where clone array corruption in v2
Method-chaining in gorm is predicated on a `Clause`'s `MergeClause`
method ensuring that the two clauses are disconnected in terms of
pointers (at least in the Wherec case).
However, the original Where implementation used `append`, which
only returns a new instance if the backing array needs to be resized.
In some cases, this is true. Practically, go doubles the size of the
slice once it gets full, so the following slice `append` calls would
result in a new slice:
* 0 -> 1
* 1 -> 2
* 2 -> 4
* 4 -> 8
* and so on.
So, when the number of "where" conditions was 0, 1, 2, or 4, method-chaining
would work as expected. However, when it was 3, 5, 6, or 7, modifying the
copy would modify the original.
This also updates the "order by", "group by" and "set" clauses.
2020-06-07 23:41:54 +03:00
|
|
|
})
|
|
|
|
s2 := s.clone()
|
|
|
|
s2.AddClause(clause.Where{
|
2020-06-08 06:38:51 +03:00
|
|
|
Exprs: s.BuildCondition("FINAL2"),
|
Fix Statement Where clone array corruption in v2
Method-chaining in gorm is predicated on a `Clause`'s `MergeClause`
method ensuring that the two clauses are disconnected in terms of
pointers (at least in the Wherec case).
However, the original Where implementation used `append`, which
only returns a new instance if the backing array needs to be resized.
In some cases, this is true. Practically, go doubles the size of the
slice once it gets full, so the following slice `append` calls would
result in a new slice:
* 0 -> 1
* 1 -> 2
* 2 -> 4
* 4 -> 8
* and so on.
So, when the number of "where" conditions was 0, 1, 2, or 4, method-chaining
would work as expected. However, when it was 3, 5, 6, or 7, modifying the
copy would modify the original.
This also updates the "order by", "group by" and "set" clauses.
2020-06-07 23:41:54 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
if reflect.DeepEqual(s1.Clauses["WHERE"], s2.Clauses["WHERE"]) {
|
|
|
|
t.Errorf("Where conditions should be different")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-10-08 12:51:27 +03:00
|
|
|
|
2023-02-02 12:54:51 +03:00
|
|
|
func TestNilCondition(t *testing.T) {
|
|
|
|
s := new(Statement)
|
|
|
|
if len(s.BuildCondition(nil)) != 0 {
|
|
|
|
t.Errorf("Nil condition should be empty")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-08 12:51:27 +03:00
|
|
|
func TestNameMatcher(t *testing.T) {
|
2022-07-13 13:02:11 +03:00
|
|
|
for k, v := range map[string][]string{
|
2022-08-15 05:47:26 +03:00
|
|
|
"table.name": {"table", "name"},
|
|
|
|
"`table`.`name`": {"table", "name"},
|
|
|
|
"'table'.'name'": {"table", "name"},
|
|
|
|
"'table'.name": {"table", "name"},
|
|
|
|
"table1.name_23": {"table1", "name_23"},
|
|
|
|
"`table_1`.`name23`": {"table_1", "name23"},
|
|
|
|
"'table23'.'name_1'": {"table23", "name_1"},
|
|
|
|
"'table23'.name1": {"table23", "name1"},
|
|
|
|
"'name1'": {"", "name1"},
|
|
|
|
"`name_1`": {"", "name_1"},
|
|
|
|
"`Name_1`": {"", "Name_1"},
|
|
|
|
"`Table`.`nAme`": {"Table", "nAme"},
|
2023-12-23 16:19:41 +03:00
|
|
|
"my_table.*": {"my_table", "*"},
|
|
|
|
"`my_table`.*": {"my_table", "*"},
|
|
|
|
"User__Company.*": {"User__Company", "*"},
|
|
|
|
"`User__Company`.*": {"User__Company", "*"},
|
|
|
|
`"User__Company".*`: {"User__Company", "*"},
|
|
|
|
`"table"."*"`: {"", ""},
|
2021-10-08 12:51:27 +03:00
|
|
|
} {
|
2023-12-23 16:19:41 +03:00
|
|
|
if table, column := matchName(k); table != v[0] || column != v[1] {
|
|
|
|
t.Errorf("failed to match value: %v, got %v, expect: %v", k, []string{table, column}, v)
|
2021-10-08 12:51:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|