You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by li...@apache.org on 2026/07/21 10:58:26 UTC
(skywalking-banyandb) 01/01: Allow `*` in the BydbQL identifier
This is an automated email from the ASF dual-hosted git repository.
mrproliu pushed a commit to branch ql-star-support
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git
commit 05438286ab1e1709127e11cb345c3e57db970cca
Author: mrproliu <74...@qq.com>
AuthorDate: Tue Jul 21 18:58:10 2026 +0800
Allow `*` in the BydbQL identifier
---
CHANGES.md | 1 +
docs/interacting/bydbql.md | 28 +++++++++++++++++----
pkg/bydbql/bydbql_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++
pkg/bydbql/parser.go | 7 +++++-
4 files changed, 93 insertions(+), 6 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 95dbdbf6d..147d9ba83 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -138,6 +138,7 @@ Release Notes.
- Deleting one TopN aggregation no longer tears down sibling aggregations on the same source measure.
- Purge a deleted group's resource, index-rule and binding cache entries to avoid dangling references.
- Clear a trace subject's index when its last index rule or binding is removed.
+- Allow `*` as a non-initial character in BydbQL identifiers, so resources named with `*` become queryable, not just writable.
### Document
diff --git a/docs/interacting/bydbql.md b/docs/interacting/bydbql.md
index a2ab16166..02cc21436 100644
--- a/docs/interacting/bydbql.md
+++ b/docs/interacting/bydbql.md
@@ -98,6 +98,24 @@ FROM STREAM mystream in group1 -- refers to stream "mystream"
**Best Practice**: Use uppercase for reserved words and consistent casing for identifiers to maintain readability.
+#### Identifier Character Set
+
+An identifier starts with a letter or underscore and may continue with letters, digits, underscores, hyphens (`-`), and asterisks (`*`):
+
+```
+identifier ::= [a-zA-Z_][a-zA-Z0-9_*-]*
+```
+
+Hyphens and asterisks are allowed only after the first character. This matters because the schema layer accepts such names at creation time — a TopN rule named `endpoint_avg-cluster-excludes-*`, for instance — and a resource that can be created must remain referenceable from a query:
+
+```sql
+SHOW TOP 10 FROM MEASURE endpoint_avg-cluster-excludes-* IN default;
+```
+
+Confining `*` to non-initial positions is what keeps the star projection unambiguous: a bare `*` cannot begin an identifier, so `SELECT *` is always a projection.
+
+**Caveat**: an identifier that begins with a reserved word followed by a non-word character is rejected by the lexer — `IN group-a` fails because `group` matches the keyword rule first. Avoid naming resources with a reserved-word prefix.
+
## 2.5. Timestamp Formats
BydbQL supports flexible timestamp specifications in TIME clauses, accommodating both absolute and relative time formats:
@@ -392,7 +410,7 @@ timestamp ::= string_literal | integer_literal
/* timestamp supports both absolute and relative time formats:
- Absolute: RFC3339 format like "2006-01-02T15:04:05Z07:00"
- Relative: duration strings like "-30m", "2h", "1d" (relative to current time) */
-identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
+identifier ::= [a-zA-Z_][a-zA-Z0-9_*-]*
string_literal ::= "'" [^']* "'" | "\"" [^\"]* "\""
integer_literal ::= [0-9]+
```
@@ -530,7 +548,7 @@ timestamp ::= string_literal | integer_literal
/* timestamp supports both absolute and relative time formats:
- Absolute: RFC3339 format like "2006-01-02T15:04:05Z07:00"
- Relative: duration strings like "-30m", "2h", "1d" (relative to current time) */
-identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
+identifier ::= [a-zA-Z_][a-zA-Z0-9_*-]*
string_literal ::= "'" [^']* "'" | "\"" [^\"]* "\""
integer_literal ::= [0-9]+
```
@@ -705,7 +723,7 @@ timestamp ::= string_literal | integer_literal
/* timestamp supports both absolute and relative time formats:
- Absolute: RFC3339 format like "2006-01-02T15:04:05Z07:00"
- Relative: duration strings like "-30m", "2h", "1d" (relative to current time) */
-identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
+identifier ::= [a-zA-Z_][a-zA-Z0-9_*-]*
string_literal ::= "'" [^']* "'" | "\"" [^\"]* "\""
integer_literal ::= [0-9]+
```
@@ -825,7 +843,7 @@ condition ::= identifier binary_op (value | value_list) | "ID" binary_
binary_op ::= "=" | "!=" | ">" | "<" | ">=" | "<=" | "IN" | "NOT IN"
value ::= string_literal | integer_literal | "NULL"
value_list ::= "(" value ("," value)* ")"
-identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
+identifier ::= [a-zA-Z_][a-zA-Z0-9_*-]*
string_literal ::= "'" [^']* "'" | "\"" [^\"]* "\""
integer_literal ::= [0-9]+
```
@@ -912,7 +930,7 @@ timestamp ::= string_literal | integer_literal
/* timestamp supports both absolute and relative time formats:
- Absolute: RFC3339 format like "2006-01-02T15:04:05Z07:00"
- Relative: duration strings like "-30m", "2h", "1d" (relative to current time) */
-identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
+identifier ::= [a-zA-Z_][a-zA-Z0-9_*-]*
string_literal ::= "'" [^']* "'" | "\"" [^\"]* "\""
integer_literal ::= [0-9]+
```
diff --git a/pkg/bydbql/bydbql_test.go b/pkg/bydbql/bydbql_test.go
index 9578e0cf5..e29b5559c 100644
--- a/pkg/bydbql/bydbql_test.go
+++ b/pkg/bydbql/bydbql_test.go
@@ -3794,6 +3794,69 @@ var _ = Describe("Parser", func() {
})
})
})
+
+ Describe("Identifier Character Set", func() {
+ // The schema layer accepts resource names containing `-` and `*` at creation time, so
+ // the query layer must be able to name them; otherwise a resource can be written but
+ // never read. `*` is confined to non-initial positions to keep `SELECT *` unambiguous.
+ It("parses a measure name containing a star", func() {
+ grammar, err := ParseQuery("SHOW TOP 5 FROM MEASURE endpoint_avg-cluster-excludes-* IN default")
+ Expect(err).To(BeNil())
+ Expect(grammar).NotTo(BeNil())
+
+ stmt := grammar.TopN
+ Expect(stmt.From.ResourceName).To(Equal("endpoint_avg-cluster-excludes-*"))
+ })
+
+ It("parses a stream name containing a star", func() {
+ grammar, err := ParseQuery("SELECT * FROM STREAM sw-excludes-* IN default")
+ Expect(err).To(BeNil())
+
+ stmt := grammar.Select
+ Expect(stmt.From.ResourceName).To(Equal("sw-excludes-*"))
+ })
+
+ // A group name that begins with a reserved word (e.g. `group-a`) is rejected by the
+ // keyword rule regardless of the star, so this uses a non-reserved prefix.
+ It("parses a group name containing a star", func() {
+ grammar, err := ParseQuery("SELECT * FROM MEASURE m IN mygroup-*")
+ Expect(err).To(BeNil())
+
+ stmt := grammar.Select
+ Expect(stmt.From.In.Groups).To(Equal([]string{"mygroup-*"}))
+ })
+
+ It("keeps the star projection working alongside a starred resource name", func() {
+ grammar, err := ParseQuery("SELECT * FROM MEASURE endpoint_avg-cluster-excludes-* IN default")
+ Expect(err).To(BeNil())
+
+ stmt := grammar.Select
+ Expect(stmt.Projection.All).To(BeTrue())
+ Expect(stmt.From.ResourceName).To(Equal("endpoint_avg-cluster-excludes-*"))
+ })
+
+ It("still parses the bare star projection as a projection, not an identifier", func() {
+ grammar, err := ParseQuery("SELECT * FROM STREAM sw IN default")
+ Expect(err).To(BeNil())
+
+ stmt := grammar.Select
+ Expect(stmt.Projection.All).To(BeTrue())
+ Expect(stmt.Projection.Columns).To(BeEmpty())
+ })
+
+ It("rejects an identifier starting with a star", func() {
+ _, err := ParseQuery("SELECT * FROM MEASURE *bad IN default")
+ Expect(err).NotTo(BeNil())
+ })
+
+ It("parses a starred column reference in a condition", func() {
+ grammar, err := ParseQuery("SELECT * FROM MEASURE m IN default WHERE col-* = 'v'")
+ Expect(err).To(BeNil())
+
+ stmt := grammar.Select
+ Expect(stmt.Where).NotTo(BeNil())
+ })
+ })
})
func BenchmarkParser(b *testing.B) {
diff --git a/pkg/bydbql/parser.go b/pkg/bydbql/parser.go
index 35cbc8e15..66892e088 100644
--- a/pkg/bydbql/parser.go
+++ b/pkg/bydbql/parser.go
@@ -48,7 +48,12 @@ func init() {
Name: "Keyword",
Pattern: fmt.Sprintf(`(?i)(%s)\b`, keywordStr),
},
- {Name: "Ident", Pattern: `[a-zA-Z_][a-zA-Z0-9_-]*`},
+ // `-` and `*` are accepted as continuation characters, never as the first one, so that
+ // resource names the schema layer already accepts at creation time — e.g. a TopN rule
+ // named `endpoint_avg-cluster-excludes-*` — stay referenceable from a query. Confining
+ // `*` to non-initial positions is what keeps the `SELECT *` projection unambiguous: a
+ // bare `*` cannot begin an identifier, so it still lexes as an operator.
+ {Name: "Ident", Pattern: `[a-zA-Z_][a-zA-Z0-9_*-]*`},
{Name: "Int", Pattern: `[-+]?\d+`},
{Name: "String", Pattern: `'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"`},
{Name: "QuotedIdent", Pattern: `"[a-zA-Z_][a-zA-Z0-9_.]*"|'[a-zA-Z_][a-zA-Z0-9_.]*'`},