mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
248 lines
7.4 KiB
Go
248 lines
7.4 KiB
Go
// Copyright 2017 Pilosa Corp.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package pilosa
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/pilosa/pilosa/internal"
|
|
)
|
|
|
|
// System errors.
|
|
var (
|
|
ErrHostRequired = errors.New("host required")
|
|
|
|
ErrIndexRequired = errors.New("index required")
|
|
ErrIndexExists = errors.New("index already exists")
|
|
ErrIndexNotFound = errors.New("index not found")
|
|
|
|
// ErrFrameRequired is returned when no frame is specified.
|
|
ErrFrameRequired = errors.New("frame required")
|
|
ErrFrameExists = errors.New("frame already exists")
|
|
ErrFrameNotFound = errors.New("frame not found")
|
|
ErrFrameInverseDisabled = errors.New("frame inverse disabled")
|
|
ErrColumnRowLabelEqual = errors.New("column and row labels cannot be equal")
|
|
|
|
ErrInputDefinitionExists = errors.New("input-definition already exists")
|
|
ErrInputDefinitionHasPrimaryKey = errors.New("input-definition must contain one PrimaryKey")
|
|
ErrInputDefinitionDupePrimaryKey = errors.New("input-definition can only contain one PrimaryKey")
|
|
ErrInputDefinitionColumnLabel = errors.New("PrimaryKey field name does not match columnLabel")
|
|
ErrInputDefinitionNameRequired = errors.New("input-definition name required")
|
|
ErrInputDefinitionAttrsRequired = errors.New("frames and fields are required")
|
|
ErrInputDefinitionValueMap = errors.New("valueMap required for map")
|
|
ErrInputDefinitionActionRequired = errors.New("field definitions require an action")
|
|
ErrInputDefinitionNotFound = errors.New("input-definition not found")
|
|
|
|
ErrFieldNotFound = errors.New("field not found")
|
|
ErrFieldExists = errors.New("field already exists")
|
|
ErrFieldNameRequired = errors.New("field name required")
|
|
ErrInvalidFieldType = errors.New("invalid field type")
|
|
ErrInvalidFieldRange = errors.New("invalid field range")
|
|
ErrInverseRangeNotAllowed = errors.New("inverse range not allowed")
|
|
ErrRangeCacheNotAllowed = errors.New("range cache not allowed")
|
|
ErrFrameFieldsNotAllowed = errors.New("frame fields not allowed")
|
|
ErrInvalidFieldValueType = errors.New("invalid field value type")
|
|
ErrFieldValueTooLow = errors.New("field value too low")
|
|
ErrFieldValueTooHigh = errors.New("field value too high")
|
|
ErrInvalidRangeOperation = errors.New("invalid range operation")
|
|
ErrInvalidBetweenValue = errors.New("invalid value for between operation")
|
|
|
|
ErrInvalidView = errors.New("invalid view")
|
|
ErrInvalidCacheType = errors.New("invalid cache type")
|
|
|
|
ErrName = errors.New("invalid index or frame's name, must match [a-z0-9_-]")
|
|
ErrLabel = errors.New("invalid row or column label, must match [A-Za-z0-9_-]")
|
|
|
|
// ErrFragmentNotFound is returned when a fragment does not exist.
|
|
ErrFragmentNotFound = errors.New("fragment not found")
|
|
ErrQueryRequired = errors.New("query required")
|
|
ErrTooManyWrites = errors.New("too many write commands")
|
|
|
|
ErrConfigClusterEnabledHosts = errors.New("providing hosts to a non-disabled cluster is not allowed")
|
|
ErrConfigClusterTypeInvalid = errors.New("invalid cluster type")
|
|
ErrConfigHostsMissing = errors.New("missing bind address in cluster hosts")
|
|
|
|
ErrClusterDoesNotOwnSlice = errors.New("cluster does not own slice")
|
|
)
|
|
|
|
// Regular expression to validate index and frame names.
|
|
var nameRegexp = regexp.MustCompile(`^[a-z][a-z0-9_-]{0,63}$`)
|
|
|
|
// Regular expression to validate row and column labels.
|
|
var labelRegexp = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_-]{0,63}$`)
|
|
|
|
// ColumnAttrSet represents a set of attributes for a vertical column in an index.
|
|
// Can have a set of attributes attached to it.
|
|
type ColumnAttrSet struct {
|
|
ID uint64 `json:"id"`
|
|
Attrs map[string]interface{} `json:"attrs,omitempty"`
|
|
}
|
|
|
|
// encodeColumnAttrSets converts a into its internal representation.
|
|
func encodeColumnAttrSets(a []*ColumnAttrSet) []*internal.ColumnAttrSet {
|
|
other := make([]*internal.ColumnAttrSet, len(a))
|
|
for i := range a {
|
|
other[i] = encodeColumnAttrSet(a[i])
|
|
}
|
|
return other
|
|
}
|
|
|
|
// decodeColumnAttrSets converts a from its internal representation.
|
|
func decodeColumnAttrSets(a []*internal.ColumnAttrSet) []*ColumnAttrSet {
|
|
other := make([]*ColumnAttrSet, len(a))
|
|
for i := range a {
|
|
other[i] = decodeColumnAttrSet(a[i])
|
|
}
|
|
return other
|
|
}
|
|
|
|
// encodeColumnAttrSet converts set into its internal representation.
|
|
func encodeColumnAttrSet(set *ColumnAttrSet) *internal.ColumnAttrSet {
|
|
return &internal.ColumnAttrSet{
|
|
ID: set.ID,
|
|
Attrs: encodeAttrs(set.Attrs),
|
|
}
|
|
}
|
|
|
|
// decodeColumnAttrSet converts b from its internal representation.
|
|
func decodeColumnAttrSet(pb *internal.ColumnAttrSet) *ColumnAttrSet {
|
|
set := &ColumnAttrSet{
|
|
ID: pb.ID,
|
|
}
|
|
|
|
if len(pb.Attrs) > 0 {
|
|
set.Attrs = make(map[string]interface{}, len(pb.Attrs))
|
|
for _, attr := range pb.Attrs {
|
|
k, v := decodeAttr(attr)
|
|
set.Attrs[k] = v
|
|
}
|
|
}
|
|
|
|
return set
|
|
}
|
|
|
|
// TimeFormat is the go-style time format used to parse string dates.
|
|
const TimeFormat = "2006-01-02T15:04"
|
|
|
|
// ValidateName ensures that the name is a valid format.
|
|
func ValidateName(name string) error {
|
|
if nameRegexp.Match([]byte(name)) == false {
|
|
return ErrName
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateLabel ensures that the label is a valid format.
|
|
func ValidateLabel(label string) error {
|
|
if labelRegexp.Match([]byte(label)) == false {
|
|
return ErrLabel
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StringInSlice checks for substring a in the slice.
|
|
func StringInSlice(a string, list []string) bool {
|
|
for _, b := range list {
|
|
if b == a {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// StringSlicesAreEqual determines if two string slices are equal.
|
|
func StringSlicesAreEqual(a, b []string) bool {
|
|
|
|
if a == nil && b == nil {
|
|
return true
|
|
}
|
|
|
|
if a == nil || b == nil {
|
|
return false
|
|
}
|
|
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
|
|
for i := range a {
|
|
if a[i] != b[i] {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// SliceDiff returns the difference between two uint64 slices.
|
|
func SliceDiff(a, b []uint64) []uint64 {
|
|
m := make(map[uint64]uint64)
|
|
|
|
for _, y := range b {
|
|
m[y]++
|
|
}
|
|
|
|
var ret []uint64
|
|
for _, x := range a {
|
|
if m[x] > 0 {
|
|
m[x]--
|
|
continue
|
|
}
|
|
ret = append(ret, x)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// ContainsSubstring checks to see if substring a is contained in any string in the slice.
|
|
func ContainsSubstring(a string, list []string) bool {
|
|
for _, b := range list {
|
|
if strings.Contains(b, a) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HostToIP converts host to an IP4 address based on net.LookupIP().
|
|
func HostToIP(host string) string {
|
|
// if host is not an IP addr, check net.LookupIP()
|
|
if net.ParseIP(host) == nil {
|
|
hosts, err := net.LookupIP(host)
|
|
if err != nil {
|
|
return host
|
|
}
|
|
for _, h := range hosts {
|
|
// this restricts pilosa to IP4
|
|
if h.To4() != nil {
|
|
return h.String()
|
|
}
|
|
}
|
|
}
|
|
return host
|
|
}
|
|
|
|
// AddressWithDefaults converts addr into a valid address,
|
|
// using defaults when necessary.
|
|
func AddressWithDefaults(addr string) (*URI, error) {
|
|
if addr == "" {
|
|
return DefaultURI(), nil
|
|
} else {
|
|
return NewURIFromAddress(addr)
|
|
}
|
|
}
|