featurebase/dax/table_test.go
Vengata Krishnan 6777e3dc07
FB-1968 timestamp data type related fixes and enhancements (#2256)
* Removed support for EPOCH column constraint from TIMESTAMP SQL data type.
* Implicit conversion of integers to timestamp will treat the integer value as seconds since unix epoch.
* Add new ToTimeStamp(num, timeunit) SQL scalar function to help convert integer values to timestamp.
2023-02-24 14:57:17 -05:00

403 lines
9.2 KiB
Go

package dax_test
import (
"encoding/json"
"fmt"
"sort"
"strings"
"testing"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/stretchr/testify/assert"
)
func TestTable(t *testing.T) {
tableName := dax.TableName("foo")
t.Run("StringKeys", func(t *testing.T) {
// No PrimaryKeys.
{
tbl := &dax.Table{
Name: tableName,
}
assert.False(t, tbl.StringKeys())
}
// One PrimaryKey (string).
{
tbl := &dax.Table{
Name: tableName,
Fields: []*dax.Field{
{
Name: dax.PrimaryKeyFieldName,
Type: dax.BaseTypeString,
},
{
Name: "stringField2",
Type: dax.BaseTypeString,
},
},
}
assert.True(t, tbl.StringKeys())
}
// One PrimaryKey (non-string).
{
tbl := &dax.Table{
Name: tableName,
Fields: []*dax.Field{
{
Name: dax.PrimaryKeyFieldName,
Type: dax.BaseTypeID,
},
},
}
assert.False(t, tbl.StringKeys())
}
})
t.Run("Tables", func(t *testing.T) {
tblA := &dax.Table{
Name: "a",
}
tblZ := &dax.Table{
Name: "z",
}
tables := []*dax.Table{tblZ, tblA}
// Ensure tables starts out of order.
assert.Equal(t, tblZ.Name, tables[0].Name)
assert.Equal(t, tblA.Name, tables[1].Name)
sort.Sort(dax.Tables(tables))
// Ensure tables is ordered by name.
assert.Equal(t, tblA.Name, tables[0].Name)
assert.Equal(t, tblZ.Name, tables[1].Name)
})
t.Run("CreateSQL", func(t *testing.T) {
tests := []struct {
tbl dax.Table
expSQL string
}{
{
tbl: dax.Table{
Name: "just_a_table",
Fields: []*dax.Field{
{
Name: "_id",
Type: "id",
},
},
},
expSQL: "CREATE TABLE just_a_table (_id id) KEYPARTITIONS 0",
},
{
tbl: dax.Table{
Name: "all_field_types",
Fields: []*dax.Field{
{
Name: "_id",
Type: "string",
},
{
Name: "an_id",
Type: "id",
},
{
Name: "a_string",
Type: "string",
},
{
Name: "an_id_set",
Type: "idset",
},
{
Name: "a_string_set",
Type: "stringset",
},
{
Name: "an_int",
Type: "int",
},
{
Name: "a_decimal",
Type: "decimal",
},
{
Name: "a_timestamp",
Type: "timestamp",
},
},
},
expSQL: "CREATE TABLE all_field_types (_id string, an_id id, a_string string, an_id_set idset, a_string_set stringset, an_int int MIN 0 MAX 0, a_decimal decimal, a_timestamp timestamp) KEYPARTITIONS 0",
},
{
tbl: dax.Table{
Name: "all_field_types_with_options",
Fields: []*dax.Field{
{
Name: "_id",
Type: "string",
},
{
Name: "an_id",
Type: "id",
Options: dax.FieldOptions{
CacheType: "ranked",
CacheSize: 500,
},
},
{
Name: "a_string",
Type: "string",
Options: dax.FieldOptions{
CacheType: "ranked",
CacheSize: 500,
},
},
{
Name: "an_id_set",
Type: "idset",
Options: dax.FieldOptions{
CacheType: "ranked",
CacheSize: 500,
},
},
{
Name: "a_string_set",
Type: "stringset",
Options: dax.FieldOptions{
CacheType: "ranked",
CacheSize: 500,
},
},
{
Name: "an_int",
Type: "int",
Options: dax.FieldOptions{
Min: pql.NewDecimal(-100, 0),
Max: pql.NewDecimal(200, 0),
},
},
{
Name: "a_decimal",
Type: "decimal",
Options: dax.FieldOptions{},
},
{
Name: "a_timestamp",
Type: "timestamp",
Options: dax.FieldOptions{
TimeUnit: "s",
},
},
},
},
expSQL: "CREATE TABLE all_field_types_with_options (_id string, an_id id CACHETYPE ranked SIZE 500, a_string string CACHETYPE ranked SIZE 500, an_id_set idset CACHETYPE ranked SIZE 500, a_string_set stringset CACHETYPE ranked SIZE 500, an_int int MIN -100 MAX 200, a_decimal decimal, a_timestamp timestamp TIMEUNIT 's') KEYPARTITIONS 0",
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
assert.Equal(t, test.expSQL, test.tbl.CreateSQL())
})
}
})
t.Run("Table", func(t *testing.T) {
// CleanStub makes sure that the portion of the dax.TableName that we
// use in the pilosa.Index.Name is actually valid.
t.Run("CleanStub", func(t *testing.T) {
tests := []struct {
name dax.TableName
expStub string
}{
{
name: "",
expStub: "",
},
{
name: "foo",
expStub: "foo",
},
{
name: "AbCdEfG1234",
expStub: "abcdefg123",
},
{
name: "foo_bar_",
expStub: "foobar",
},
{
name: "!!!!!!!",
expStub: "",
},
{
name: "long1234567890",
expStub: "long123456",
},
{
name: "&&&&&&&&&&&&&&valid_stuff",
expStub: "validstuff",
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
n := dax.NewTable(test.name)
assert.Empty(t, n.ID)
n.CreateID()
parts := strings.Split(string(n.ID), "_")
assert.Equal(t, test.expStub, parts[0])
})
}
})
t.Run("RandomID", func(t *testing.T) {
n := dax.NewTable(tableName)
assert.Empty(t, n.ID)
n.CreateID()
assert.NotEmpty(t, n.ID)
assert.Equal(t, tableName, n.Name)
})
t.Run("ToJSON", func(t *testing.T) {
t.Run("WithID", func(t *testing.T) {
n := dax.NewTable(tableName)
n.CreateID()
id := n.ID
assert.NotEmpty(t, id)
b, err := json.Marshal(n)
assert.NoError(t, err)
exp := fmt.Sprintf("{\"id\":\"%s\",\"name\":\"%s\",\"fields\":[],\"partitionN\":0}", id, tableName)
assert.JSONEq(t, exp, string(b))
})
t.Run("WithoutID", func(t *testing.T) {
n := dax.Table{
Name: tableName,
}
id := n.ID
assert.Empty(t, id)
b, err := json.Marshal(n)
assert.NoError(t, err)
exp := fmt.Sprintf("{\"name\":\"%s\",\"fields\":null,\"partitionN\":0}", tableName)
assert.JSONEq(t, exp, string(b))
})
})
t.Run("FromJSON", func(t *testing.T) {
tid := dax.TableID("0000000000abc123") // 11256099
j := fmt.Sprintf("{\"id\":\"%s\",\"name\":\"foo\",\"fields\":[],\"partitionN\":0}", tid)
tbl := &dax.Table{}
err := json.Unmarshal([]byte(j), tbl)
assert.NoError(t, err)
assert.Equal(t, tid, tbl.ID)
})
})
t.Run("QualifiedTable", func(t *testing.T) {
orgID := dax.OrganizationID("acme")
dbID := dax.DatabaseID("db1")
t.Run("New", func(t *testing.T) {
tbl := dax.NewTable(tableName)
tbl.CreateID()
qdbid := dax.QualifiedDatabaseID{
OrganizationID: orgID,
DatabaseID: dbID,
}
qtbl := dax.NewQualifiedTable(qdbid, tbl)
assert.NotEmpty(t, qtbl.ID)
assert.Equal(t, tbl.ID, qtbl.ID)
tq := qtbl.Qualifier()
assert.Equal(t, qdbid.OrganizationID, tq.OrganizationID)
assert.Equal(t, qdbid.DatabaseID, tq.DatabaseID)
wrappedTable := qtbl.Table
assert.Equal(t, tbl.Name, wrappedTable.Name)
assert.Equal(t, tbl.ID, wrappedTable.ID)
// Key.
exp := dax.TableKey(fmt.Sprintf("%s%s%s%s%s%s%s",
dax.PrefixTable,
dax.TableKeyDelimiter,
orgID,
dax.TableKeyDelimiter,
dbID,
dax.TableKeyDelimiter,
tbl.ID,
))
assert.Equal(t, exp, qtbl.Key())
})
t.Run("ToJSON", func(t *testing.T) {
tbl := dax.NewTable(tableName)
tbl.CreateID()
qdbid := dax.QualifiedDatabaseID{
OrganizationID: orgID,
DatabaseID: dbID,
}
qtbl := dax.NewQualifiedTable(qdbid, tbl)
id := qtbl.ID
b, err := json.Marshal(qtbl)
assert.NoError(t, err)
exp := fmt.Sprintf("{\"org-id\":\"%s\",\"db-id\":\"%s\",\"id\":\"%s\",\"name\":\"%s\",\"fields\":[],\"partitionN\":0}", orgID, dbID, id, tableName)
assert.JSONEq(t, exp, string(b))
})
t.Run("FromJSON", func(t *testing.T) {
tid := dax.TableID("0000000000abc123") // 11256099
j := fmt.Sprintf("{\"org-id\":\"%s\",\"db-id\":\"%s\",\"id\":\"%s\",\"name\":\"foo\",\"fields\":[],\"partitionN\":0}", orgID, dbID, tid)
qtbl := &dax.QualifiedTable{}
err := json.Unmarshal([]byte(j), qtbl)
assert.NoError(t, err)
assert.Equal(t, tid, qtbl.ID)
assert.Equal(t, orgID, qtbl.Qualifier().OrganizationID)
assert.Equal(t, dbID, qtbl.Qualifier().DatabaseID)
})
})
}
func TestDatabase(t *testing.T) {
databaseName := dax.DatabaseName("db1")
t.Run("Options", func(t *testing.T) {
{
db := &dax.Database{
Name: databaseName,
}
assert.Zero(t, db.Options.WorkersMin)
assert.Zero(t, db.Options.WorkersMax)
// Set WorkersMin to 5.
assert.NoError(t, db.Options.Set(dax.DatabaseOptionWorkersMin, "5"))
assert.Equal(t, 5, db.Options.WorkersMin)
assert.Equal(t, 5, db.Options.WorkersMax)
// Set WorkersMin back to 0.
assert.NoError(t, db.Options.Set(dax.DatabaseOptionWorkersMin, "0"))
assert.Zero(t, db.Options.WorkersMin)
assert.Zero(t, db.Options.WorkersMax)
// Try setting WorkersMin to an invalid value.
assert.Error(t, db.Options.Set(dax.DatabaseOptionWorkersMin, "abc"))
// Try setting an unsupported option.
assert.Error(t, db.Options.Set("invalid-option", ""))
}
})
}