Skip to content

Commit

Permalink
Do not allow Cassandra reserved words to be used as field names etc. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
phliar authored Dec 12, 2019
1 parent 3183aad commit 9b7dd30
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 41 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog

## v3.4.13 (unreleased)
- Nothing changed yet.
- Do not allow Cassandra reserved words to be used as field names etc.

## v3.4.12 (2019-12-06)
- Improve some CLI error messages
Expand Down
36 changes: 32 additions & 4 deletions names.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import (
)

// This module does some sanity checking of names used in DOSA. A name must have a leading letter,
// followed by a string of letters and digits. A name can have up to 32 chars.
// followed by a string of letters and digits. A name can have up to 32 chars. Reserved words are
// not allowed as a name.
//
// A special kind of name is the name-prefix. A name-prefix has the same restrictions as a name,
// except that a name-prefix can also contain the "." character.
Expand All @@ -40,10 +41,34 @@ const (
var (
namePrefixRegex = regexp.MustCompile("^[a-z_][a-z0-9_.]{0,31}$")
nameRegex = regexp.MustCompile("^[a-z_][a-z0-9_]{0,31}$")

// Reserved words
reserved map[string]struct{}
)

// DosaNamingRule DOSA Name Rule
const DosaNamingRule = "DOSA valid names must start with a letter or underscore, and may contain letters, digits, and underscores, and must not be longer than 32 characters."
func init() {
// Cassandra's reserved words
cassandraRsvd := []string{"add", "aggregate", "all", "allow", "alter", "and", "any", "apply", "as",
"asc", "ascii", "authorize", "batch", "begin", "bigint", "blob", "boolean", "by", "clustering",
"columnfamily", "compact", "consistency", "count", "counter", "create", "custom", "decimal",
"delete", "desc", "distinct", "double", "drop", "each_quorum", "entries", "exists", "filtering",
"float", "from", "frozen", "full", "grant", "if", "in", "index", "inet", "infinity", "insert",
"int", "into", "key", "keyspace", "keyspaces", "level", "limit", "list", "local_one",
"local_quorum", "map", "materialized", "modify", "nan", "norecursive", "nosuperuser", "not",
"of", "on", "one", "order", "partition", "password", "per", "permission", "permissions",
"primary", "quorum", "rename", "revoke", "schema", "select", "set", "static", "storage",
"superuser", "table", "text", "time", "timestamp", "timeuuid", "three", "to", "token",
"truncate", "ttl", "tuple", "two", "type", "unlogged", "update", "use", "user", "users",
"using", "uuid", "values", "varchar", "varint", "view", "where", "with", "writetime"}
reserved = make(map[string]struct{})
for _, n := range cassandraRsvd {
reserved[n] = struct{}{}
}
}

// DosaNamingRule is the error message for invalid names.
const DosaNamingRule = "DOSA valid names must start with a letter or underscore, and may contain letters, " +
"digits, and underscores, and must not be longer than 32 characters."

// IsValidNamePrefix checks if a name prefix is valid.
func IsValidNamePrefix(namePrefix string) error {
Expand All @@ -59,7 +84,10 @@ func IsValidName(name string) error {
if !nameRegex.MatchString(name) {
return errors.Errorf("invalid name '%s': %s", name, DosaNamingRule)
}
return nil
if _, ok := reserved[name]; !ok {
return nil
}
return errors.Errorf("%s is a reserved word", name)
}

// NormalizeName normalizes a name to a canonical representation.
Expand Down
78 changes: 42 additions & 36 deletions names_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,58 +29,66 @@ import (

func TestIsValidName(t *testing.T) {
dataProvider := []struct {
arg string
allowed bool
arg string
err string
}{
{
arg: "has_underscore",
allowed: true,
arg: "has_underscore",
},
{
arg: "mixeDCase",
allowed: false,
arg: "mixeDCase",
err: "invalid name",
},
{
arg: "md5",
allowed: true,
arg: "md5",
},
{
arg: "_name",
allowed: true,
arg: "_name",
},
{
arg: "_alreadynormalized9",
allowed: true,
arg: "_alreadynormalized9",
},
{
arg: "123numberprefix",
allowed: false,
arg: "123numberprefix",
err: "invalid name",
},
{
arg: "",
allowed: false,
arg: "",
err: "invalid name",
},
{
arg: "longname012345678901234567890123456789",
allowed: false,
arg: "longname012345678901234567890123456789",
err: "invalid name",
},
{
arg: "世界",
allowed: false,
arg: "世界",
err: "invalid name",
},
{
arg: "an apple",
allowed: false,
arg: "an apple",
err: "invalid name",
},
{
arg: "token",
err: "reserved word",
},
{
arg: "keyspaces",
err: "reserved word",
},
{
arg: "schema",
err: "reserved word",
},
}

for _, testData := range dataProvider {
err := IsValidName(testData.arg)
if testData.allowed {
assert.NoError(t, err, fmt.Sprintf("got error while expecting no error for %s", testData.arg))
if testData.err == "" {
assert.NoError(t, err, testData.arg)
} else {
assert.Error(t, err, fmt.Sprintf("expect error but got no error for %s", testData.arg))
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Error(t, err, testData.arg)
assert.Contains(t, err.Error(), testData.err)
}
}
}
Expand Down Expand Up @@ -123,12 +131,10 @@ func TestNormalizeName(t *testing.T) {
for _, testData := range dataProvider {
name, err := NormalizeName(testData.arg)
if testData.allowed {
assert.NoError(t, err, fmt.Sprintf("got error while expecting no error for %s", testData.arg))
assert.Equal(t, testData.expected, name,
fmt.Sprintf("unexpected normalized name for %s", testData.arg))
assert.NoError(t, err, testData.arg)
assert.Equal(t, testData.expected, name, testData.arg)
} else {
assert.Error(t, err, fmt.Sprintf("expect error but got no error for %s", testData.arg))
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Error(t, err, testData.arg)
}
}
}
Expand All @@ -142,19 +148,19 @@ func TestIsValidNamePrefix(t *testing.T) {

err = IsValidNamePrefix("")
assert.Error(t, err)
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Contains(t, err.Error(), "invalid name")

err = IsValidNamePrefix("service.an entity")
assert.Error(t, err)
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Contains(t, err.Error(), "invalid name")

err = IsValidNamePrefix("germanRush.über")
assert.Error(t, err)
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Contains(t, err.Error(), "invalid name")

err = IsValidNamePrefix("this.prefix.has.more.than.thrity.two.characters.in.it")
assert.Error(t, err)
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Contains(t, err.Error(), "invalid name")
}

func TestNormalizeNamePrefix(t *testing.T) {
Expand Down Expand Up @@ -205,7 +211,7 @@ func TestNormalizeNamePrefix(t *testing.T) {
name, err := NormalizeNamePrefix(tc.arg)
if tc.bogus {
assert.Error(t, err)
assert.Contains(t, err.Error(), DosaNamingRule)
assert.Contains(t, err.Error(), "invalid name")
} else {
assert.NoError(t, err)
assert.Equal(t, tc.expected, name)
Expand Down

0 comments on commit 9b7dd30

Please sign in to comment.