netmap: Support non-ascii attributes in SELECT IN
#273
13 changed files with 112 additions and 45 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -23,7 +23,7 @@ coverage.txt
|
||||||
coverage.html
|
coverage.html
|
||||||
|
|
||||||
# antlr tool jar
|
# antlr tool jar
|
||||||
antlr-*.jar
|
antlr*.jar
|
||||||
|
|
||||||
# tempfiles
|
# tempfiles
|
||||||
.cache
|
.cache
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
ANTLR_VERSION="4.13.0"
|
ANTLR_VERSION=4.13.1
|
||||||
TMP_DIR := .cache
|
TMP_DIR := .cache
|
||||||
LINT_VERSION ?= 1.60.1
|
LINT_VERSION ?= 1.60.1
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.6
|
TRUECLOUDLAB_LINT_VERSION ?= 0.0.6
|
||||||
|
@ -53,7 +53,8 @@ format:
|
||||||
|
|
||||||
policy:
|
policy:
|
||||||
@wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar
|
@wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar
|
||||||
@java -Xmx500M -cp "`pwd`/antlr4-tool.jar" "org.antlr.v4.Tool" -o `pwd`/netmap/parser/ -Dlanguage=Go -no-listener -visitor `pwd`/netmap/parser/Query.g4 `pwd`/netmap/parser/QueryLexer.g4
|
@java -Xmx500M -cp antlr4-tool.jar org.antlr.v4.Tool -Dlanguage=Go \
|
||||||
|
-no-listener -visitor netmap/parser/Query.g4 netmap/parser/QueryLexer.g4
|
||||||
|
|
||||||
# Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java)
|
# Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java)
|
||||||
docker/%:
|
docker/%:
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -7,7 +7,7 @@ require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1
|
git.frostfs.info/TrueCloudLab/hrw v1.2.1
|
||||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0
|
github.com/antlr4-go/antlr/v4 v4.13.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||||
github.com/klauspost/reedsolomon v1.12.1
|
github.com/klauspost/reedsolomon v1.12.1
|
||||||
|
@ -40,7 +40,7 @@ require (
|
||||||
go.etcd.io/bbolt v1.3.9 // indirect
|
go.etcd.io/bbolt v1.3.9 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
golang.org/x/crypto v0.24.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/net v0.26.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
|
|
BIN
go.sum
BIN
go.sum
Binary file not shown.
|
@ -19,10 +19,10 @@ repStmt:
|
||||||
cbfStmt: CBF BackupFactor = NUMBER1; // container backup factor
|
cbfStmt: CBF BackupFactor = NUMBER1; // container backup factor
|
||||||
|
|
||||||
selectStmt:
|
selectStmt:
|
||||||
SELECT Count = NUMBER1 // number of nodes to select without container backup factor *)
|
SELECT Count = NUMBER1 // number of nodes to select without container backup factor *)
|
||||||
(IN clause? Bucket = ident)? // bucket name
|
(IN clause? Bucket = filterKey)? // bucket name
|
||||||
FROM Filter = identWC // filter reference or whole netmap
|
FROM Filter = identWC // filter reference or whole netmap
|
||||||
(AS Name = ident)? // optional selector name
|
(AS Name = ident)? // optional selector name
|
||||||
;
|
;
|
||||||
|
|
||||||
clause: CLAUSE_SAME | CLAUSE_DISTINCT; // nodes from distinct buckets
|
clause: CLAUSE_SAME | CLAUSE_DISTINCT; // nodes from distinct buckets
|
||||||
|
|
Binary file not shown.
|
@ -1,4 +1,5 @@
|
||||||
package parser
|
package parser
|
||||||
|
|
||||||
// ANTLR can be downloaded from https://www.antlr.org/download/antlr-4.13.0-complete.jar
|
// You can download ANTLR from https://www.antlr.org/download/antlr-4.13.1-complete.jar,
|
||||||
//go:generate java -Xmx500M -cp "./antlr-4.13.0-complete.jar:$CLASSPATH" org.antlr.v4.Tool -Dlanguage=Go -no-listener -visitor QueryLexer.g4 Query.g4
|
// then run generate or simply run the dedicated Makefile target like this `make policy`.
|
||||||
|
//go:generate java -Xmx500M -cp "./antlr-4.13.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool -Dlanguage=Go -no-listener -visitor QueryLexer.g4 Query.g4
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated from /repo/frostfs/sdk-go/netmap/parser/Query.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
// Code generated from netmap/parser/Query.g4 by ANTLR 4.13.1. DO NOT EDIT.
|
||||||
|
|
||||||
package parser // Query
|
package parser // Query
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated from /repo/frostfs/sdk-go/netmap/parser/QueryLexer.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
// Code generated from netmap/parser/QueryLexer.g4 by ANTLR 4.13.1. DO NOT EDIT.
|
||||||
|
|
||||||
package parser
|
package parser
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated from /repo/frostfs/sdk-go/netmap/parser/Query.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
// Code generated from netmap/parser/Query.g4 by ANTLR 4.13.1. DO NOT EDIT.
|
||||||
|
|
||||||
package parser // Query
|
package parser // Query
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ func queryParserInit() {
|
||||||
85, 1, 0, 0, 0, 85, 7, 1, 0, 0, 0, 86, 87, 5, 10, 0, 0, 87, 88, 5, 22,
|
85, 1, 0, 0, 0, 85, 7, 1, 0, 0, 0, 86, 87, 5, 10, 0, 0, 87, 88, 5, 22,
|
||||||
0, 0, 88, 9, 1, 0, 0, 0, 89, 90, 5, 11, 0, 0, 90, 96, 5, 22, 0, 0, 91,
|
0, 0, 88, 9, 1, 0, 0, 0, 89, 90, 5, 11, 0, 0, 90, 96, 5, 22, 0, 0, 91,
|
||||||
93, 5, 8, 0, 0, 92, 94, 3, 12, 6, 0, 93, 92, 1, 0, 0, 0, 93, 94, 1, 0,
|
93, 5, 8, 0, 0, 92, 94, 3, 12, 6, 0, 93, 92, 1, 0, 0, 0, 93, 94, 1, 0,
|
||||||
0, 0, 94, 95, 1, 0, 0, 0, 95, 97, 3, 28, 14, 0, 96, 91, 1, 0, 0, 0, 96,
|
0, 0, 94, 95, 1, 0, 0, 0, 95, 97, 3, 20, 10, 0, 96, 91, 1, 0, 0, 0, 96,
|
||||||
97, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 5, 12, 0, 0, 99, 102, 3, 30,
|
97, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 5, 12, 0, 0, 99, 102, 3, 30,
|
||||||
15, 0, 100, 101, 5, 9, 0, 0, 101, 103, 3, 28, 14, 0, 102, 100, 1, 0, 0,
|
15, 0, 100, 101, 5, 9, 0, 0, 101, 103, 3, 28, 14, 0, 102, 100, 1, 0, 0,
|
||||||
0, 102, 103, 1, 0, 0, 0, 103, 11, 1, 0, 0, 0, 104, 105, 7, 0, 0, 0, 105,
|
0, 102, 103, 1, 0, 0, 0, 103, 11, 1, 0, 0, 0, 104, 105, 7, 0, 0, 0, 105,
|
||||||
|
@ -1364,7 +1364,7 @@ type ISelectStmtContext interface {
|
||||||
SetCount(antlr.Token)
|
SetCount(antlr.Token)
|
||||||
|
|
||||||
// GetBucket returns the Bucket rule contexts.
|
// GetBucket returns the Bucket rule contexts.
|
||||||
GetBucket() IIdentContext
|
GetBucket() IFilterKeyContext
|
||||||
|
|
||||||
// GetFilter returns the Filter rule contexts.
|
// GetFilter returns the Filter rule contexts.
|
||||||
GetFilter() IIdentWCContext
|
GetFilter() IIdentWCContext
|
||||||
|
@ -1373,7 +1373,7 @@ type ISelectStmtContext interface {
|
||||||
GetName() IIdentContext
|
GetName() IIdentContext
|
||||||
|
|
||||||
// SetBucket sets the Bucket rule contexts.
|
// SetBucket sets the Bucket rule contexts.
|
||||||
SetBucket(IIdentContext)
|
SetBucket(IFilterKeyContext)
|
||||||
|
|
||||||
// SetFilter sets the Filter rule contexts.
|
// SetFilter sets the Filter rule contexts.
|
||||||
SetFilter(IIdentWCContext)
|
SetFilter(IIdentWCContext)
|
||||||
|
@ -1388,8 +1388,8 @@ type ISelectStmtContext interface {
|
||||||
IdentWC() IIdentWCContext
|
IdentWC() IIdentWCContext
|
||||||
IN() antlr.TerminalNode
|
IN() antlr.TerminalNode
|
||||||
AS() antlr.TerminalNode
|
AS() antlr.TerminalNode
|
||||||
AllIdent() []IIdentContext
|
FilterKey() IFilterKeyContext
|
||||||
Ident(i int) IIdentContext
|
Ident() IIdentContext
|
||||||
Clause() IClauseContext
|
Clause() IClauseContext
|
||||||
|
|
||||||
// IsSelectStmtContext differentiates from other interfaces.
|
// IsSelectStmtContext differentiates from other interfaces.
|
||||||
|
@ -1400,7 +1400,7 @@ type SelectStmtContext struct {
|
||||||
antlr.BaseParserRuleContext
|
antlr.BaseParserRuleContext
|
||||||
parser antlr.Parser
|
parser antlr.Parser
|
||||||
Count antlr.Token
|
Count antlr.Token
|
||||||
Bucket IIdentContext
|
Bucket IFilterKeyContext
|
||||||
Filter IIdentWCContext
|
Filter IIdentWCContext
|
||||||
Name IIdentContext
|
Name IIdentContext
|
||||||
}
|
}
|
||||||
|
@ -1436,13 +1436,13 @@ func (s *SelectStmtContext) GetCount() antlr.Token { return s.Count }
|
||||||
|
|
||||||
func (s *SelectStmtContext) SetCount(v antlr.Token) { s.Count = v }
|
func (s *SelectStmtContext) SetCount(v antlr.Token) { s.Count = v }
|
||||||
|
|
||||||
func (s *SelectStmtContext) GetBucket() IIdentContext { return s.Bucket }
|
func (s *SelectStmtContext) GetBucket() IFilterKeyContext { return s.Bucket }
|
||||||
|
|
||||||
func (s *SelectStmtContext) GetFilter() IIdentWCContext { return s.Filter }
|
func (s *SelectStmtContext) GetFilter() IIdentWCContext { return s.Filter }
|
||||||
|
|
||||||
func (s *SelectStmtContext) GetName() IIdentContext { return s.Name }
|
func (s *SelectStmtContext) GetName() IIdentContext { return s.Name }
|
||||||
|
|
||||||
func (s *SelectStmtContext) SetBucket(v IIdentContext) { s.Bucket = v }
|
func (s *SelectStmtContext) SetBucket(v IFilterKeyContext) { s.Bucket = v }
|
||||||
|
|
||||||
func (s *SelectStmtContext) SetFilter(v IIdentWCContext) { s.Filter = v }
|
func (s *SelectStmtContext) SetFilter(v IIdentWCContext) { s.Filter = v }
|
||||||
|
|
||||||
|
@ -1484,37 +1484,28 @@ func (s *SelectStmtContext) AS() antlr.TerminalNode {
|
||||||
return s.GetToken(QueryAS, 0)
|
return s.GetToken(QueryAS, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SelectStmtContext) AllIdent() []IIdentContext {
|
func (s *SelectStmtContext) FilterKey() IFilterKeyContext {
|
||||||
children := s.GetChildren()
|
var t antlr.RuleContext
|
||||||
len := 0
|
for _, ctx := range s.GetChildren() {
|
||||||
for _, ctx := range children {
|
if _, ok := ctx.(IFilterKeyContext); ok {
|
||||||
if _, ok := ctx.(IIdentContext); ok {
|
t = ctx.(antlr.RuleContext)
|
||||||
len++
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tst := make([]IIdentContext, len)
|
if t == nil {
|
||||||
i := 0
|
return nil
|
||||||
for _, ctx := range children {
|
|
||||||
if t, ok := ctx.(IIdentContext); ok {
|
|
||||||
tst[i] = t.(IIdentContext)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tst
|
return t.(IFilterKeyContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SelectStmtContext) Ident(i int) IIdentContext {
|
func (s *SelectStmtContext) Ident() IIdentContext {
|
||||||
var t antlr.RuleContext
|
var t antlr.RuleContext
|
||||||
j := 0
|
|
||||||
for _, ctx := range s.GetChildren() {
|
for _, ctx := range s.GetChildren() {
|
||||||
if _, ok := ctx.(IIdentContext); ok {
|
if _, ok := ctx.(IIdentContext); ok {
|
||||||
if j == i {
|
t = ctx.(antlr.RuleContext)
|
||||||
t = ctx.(antlr.RuleContext)
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
j++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1617,7 +1608,7 @@ func (p *Query) SelectStmt() (localctx ISelectStmtContext) {
|
||||||
{
|
{
|
||||||
p.SetState(95)
|
p.SetState(95)
|
||||||
|
|
||||||
var _x = p.Ident()
|
var _x = p.FilterKey()
|
||||||
|
|
||||||
localctx.(*SelectStmtContext).Bucket = _x
|
localctx.(*SelectStmtContext).Bucket = _x
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated from /repo/frostfs/sdk-go/netmap/parser/Query.g4 by ANTLR 4.13.0. DO NOT EDIT.
|
// Code generated from netmap/parser/Query.g4 by ANTLR 4.13.1. DO NOT EDIT.
|
||||||
|
|
||||||
package parser // Query
|
package parser // Query
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,13 @@ CBF 1
|
||||||
SELECT 1 FROM Color
|
SELECT 1 FROM Color
|
||||||
FILTER (Color EQ Red OR Color EQ Blue OR Color EQ Yellow) AND Color NE Green AS Color`,
|
FILTER (Color EQ Red OR Color EQ Blue OR Color EQ Yellow) AND Color NE Green AS Color`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "non-ascii attributes in SELECT IN",
|
||||||
|
input: `REP 1
|
||||||
|
CBF 1
|
||||||
|
SELECT 1 IN SAME 'Цвет' FROM Colorful
|
||||||
|
FILTER 'Цвет' EQ 'Красный' OR 'Цвет' EQ 'Синий' AS Colorful`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -127,6 +134,11 @@ func TestDecodeSelectFilterExpr(t *testing.T) {
|
||||||
SELECT 1 FROM R
|
SELECT 1 FROM R
|
||||||
FILTER Color LIKE 'R' AS R
|
FILTER Color LIKE 'R' AS R
|
||||||
`,
|
`,
|
||||||
|
`
|
||||||
|
CBF 1
|
||||||
|
SELECT 1 IN SAME 'Цвет' FROM Colorful
|
||||||
|
FILTER 'Цвет' EQ 'Красный' OR 'Цвет' EQ 'Синий' AS Colorful
|
||||||
|
`,
|
||||||
} {
|
} {
|
||||||
_, err := DecodeSelectFilterString(s)
|
_, err := DecodeSelectFilterString(s)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/hrw"
|
"git.frostfs.info/TrueCloudLab/hrw"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -542,6 +544,66 @@ func TestPlacementPolicy_ProcessSelectorsExceptForNodes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlacementPolicy_NonAsciiAttributes(t *testing.T) {
|
||||||
|
p := newPlacementPolicy(
|
||||||
|
1,
|
||||||
|
[]ReplicaDescriptor{
|
||||||
|
newReplica(2, "Nodes"),
|
||||||
|
newReplica(2, "Nodes"),
|
||||||
|
},
|
||||||
|
[]Selector{
|
||||||
|
newSelector("Nodes", "Цвет", 2, "Colorful", (*Selector).SelectSame),
|
||||||
|
},
|
||||||
|
[]Filter{
|
||||||
|
newFilter("Colorful", "", "", netmap.OR,
|
||||||
|
newFilter("", "Цвет", "Красный", netmap.EQ),
|
||||||
|
newFilter("", "Цвет", "Синий", netmap.EQ),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
p.SetUnique(true)
|
||||||
|
|
||||||
|
nodes := []NodeInfo{
|
||||||
|
nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Треугольник"),
|
||||||
|
nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Круг"),
|
||||||
|
nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Треугольник"),
|
||||||
|
nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Круг"),
|
||||||
|
nodeInfoFromAttributes("Свойство", "Мягкий", "Форма", "Треугольник"),
|
||||||
|
nodeInfoFromAttributes("Свойство", "Теплый", "Форма", "Круг"),
|
||||||
|
}
|
||||||
|
for i := range nodes {
|
||||||
|
nodes[i].SetPublicKey([]byte{byte(i)})
|
||||||
|
}
|
||||||
|
|
||||||
|
redNodes := nodes[:2]
|
||||||
|
blueNodes := nodes[2:4]
|
||||||
|
|
||||||
|
var nm NetMap
|
||||||
|
nm.SetNodes(nodes)
|
||||||
|
|
||||||
|
pivot := make([]byte, 42)
|
||||||
|
_, _ = rand.Read(pivot)
|
||||||
|
|
||||||
|
nodesPerReplica, err := nm.ContainerNodes(p, pivot)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, nodesPerReplica, 2)
|
||||||
|
|
||||||
|
for i := range nodesPerReplica {
|
||||||
|
slices.SortFunc(nodesPerReplica[i], func(n1, n2 NodeInfo) int {
|
||||||
|
pk1, pk2 := string(n1.PublicKey()), string(n2.PublicKey())
|
||||||
|
return cmp.Compare(pk1, pk2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
redMatchFirst := reflect.DeepEqual(redNodes, nodesPerReplica[0])
|
||||||
|
blueMatchFirst := reflect.DeepEqual(blueNodes, nodesPerReplica[0])
|
||||||
|
|
||||||
|
redMatchSecond := reflect.DeepEqual(redNodes, nodesPerReplica[1])
|
||||||
|
blueMatchSecond := reflect.DeepEqual(blueNodes, nodesPerReplica[1])
|
||||||
|
|
||||||
|
assert.True(t, redMatchFirst && blueMatchSecond || blueMatchFirst && redMatchSecond)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSelector_SetName(t *testing.T) {
|
func TestSelector_SetName(t *testing.T) {
|
||||||
const name = "some name"
|
const name = "some name"
|
||||||
var s Selector
|
var s Selector
|
||||||
|
|
Loading…
Reference in a new issue