gocqlmem

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 26, 2026 License: MIT Imports: 30 Imported by: 0

README

logo gocqlmem
coveralls Go Reference MIT licensed

Rationale: I have a project that uses gocql (Cassandra gocql driver) and I want to create unit tests that exercise pieces that actually call gocql. Couldn't find a library that simulates Cassandra access via gocql, so created my own.

gocqlmem implements in-memory Cassandra engine and provides access to it via gocql-like interface. This is a spin-off from the original gocql-using project Capillaries.

Unfortunately, gocql exports objects, not interfaces. So, I had to create a set of interfaces that mimic gocql Session, Query, Iter and declare them in gocqlmem/gocqlshims. If gocql developers ever decide to expose interfaces (see proposed interfaces in gocqlshims/gocql_shims.go, gocqlmem/gocqlshims will go away.

In your code, instead of gocql objects Iter, Query and Session, use shim interfaces instead:

  • gocqlshims.Session
  • gocqlshims.Query
  • gocqlshims.Iter

So the caller should not know either original gocql implementation is called, or gocqlmem implementation.

Below is the sample code creating a gocqlshims.Session depending on the configuration (test/prod). For a complete sample use, see code in https://github.com/capillariesio/capillaries/blob/main/pkg/db/cassandra.go and the test in https://github.com/capillariesio/capillaries/blob/main/pkg/api/api_test.go.

package main

import (
	gocql "github.com/apache/cassandra-gocql-driver/v2"
	"github.com/capillariesio/gocqlmem"
	"github.com/capillariesio/gocqlmem/gocqlshims"
)

func NewSession(cfg *SomeConfig) (gocqlshims.Session, error) {
	if cfg.IsTest {
        return gocqlmem.NewGocqlmemSession(), nil
	}

	dataCluster := gocql.NewCluster(cfg.Hosts...)
	dataCluster.Port = cfg.Port
    ...
    cassandraSession, err := dataCluster.CreateSession()
    gocqlshimsSession := gocqlshims.NewGocqlSession(cassandraSession)
    ...
    return gocqlshimsSession, nil
}

// Use gocqlshims.Session, gocqlshims.Query, gocqlshims.Iter

Expression evaluation engine (see ./eval directory) is Go AST-based, it was borrowed from Capillaries, it is well-tested and seems to do the job. CQL parser was written from scratch as I could not make good use of generic parser packages like antlr4 or lex/yacc. gocql-specific code in gocqlmem often mimics gocql implementation, but it does not really have to.

At the moment, gocqlmem provides basic functionality. Some things like batches, complex Cassandra data types. A lot of TODOs across the code. Test code coverage is not stellar, run test_coverage.sh to try it.

(C) 2026 KH (kleines.hertz[at]protonmail.com)

Documentation

Index

Constants

View Source
const BeginningOfTimeMicro = int64(-62135596800000000) // time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC).UnixMicro()

Variables

View Source
var GocqlmemEvalConstants = map[string]any{
	"TRUE":      true,
	"FALSE":     false,
	"NULL":      nil,
	"ASCII":     DataTypeAscii,
	"BIGINT":    DataTypeBigint,
	"BLOB":      DataTypeBlob,
	"BOOLEAN":   DataTypeBoolean,
	"COUNTER":   DataTypeCounter,
	"DATE":      DataTypeDate,
	"DECIMAL":   DataTypeDecimal,
	"DOUBLE":    DataTypeDouble,
	"DURATION":  DataTypeDuration,
	"FLOAT":     DataTypeFloat,
	"INET":      DataTypeInet,
	"INT":       DataTypeInt,
	"SMALLINT":  DataTypeSmallint,
	"TEXT":      DataTypeText,
	"TIME":      DataTypeTime,
	"TIMESTAMP": DataTypeTimestamp,
	"TIMEUUID":  DataTypeTimeuuid,
	"TINYINT":   DataTypeTinyint,
	"UUID":      DataTypeUuid,
	"VARCHAR":   DataTypeVarchar,
	"VARINT":    DataTypeVarint,
}
View Source
var GocqlmemEvalFunctions = map[string]eval.EvalFunction{
	"cast":              callCast,
	"cqlin":             callCqlIn,
	"cqlnotin":          callCqlNotIn,
	"token":             callToken,
	"current_timestamp": callCurrentTimestamp,
	"current_date":      callCurrentDate,
	"current_time":      callCurrentTime,
	"abs":               callAbs,
	"exp":               callExp,
	"log":               callLog,
	"log10":             callLog10,
	"round":             callRound,
	"now":               callNow,
	"totimestamp":       callToTimestamp,
}

Assuming that all CQL functions are lowercase

Functions

func NewGocqlmemSession

func NewGocqlmemSession() gocqlshims.Session

Types

type ClusteringOrderCaseSensitivity

type ClusteringOrderCaseSensitivity int
const (
	ClusteringOrderCaseSensitivityUnknown ClusteringOrderCaseSensitivity = iota
	ClusteringOrderCaseSensitive
	ClusteringOrderIgnoreCase
)

type ClusteringOrderType

type ClusteringOrderType int
const (
	ClusteringOrderNone ClusteringOrderType = iota
	ClusteringOrderAsc
	ClusteringOrderDesc
)

type ColumnSetExp

type ColumnSetExp struct {
	Name      string
	ExpLexems []*Lexem
}

type Command

type Command interface {
	GetCtxKeyspace() string
	SetCtxKeyspace(string)
}

func ParseCommands

func ParseCommands(s string, preparedQueryParams []any) ([]Command, error)

type CommandCreateKeyspace

type CommandCreateKeyspace struct {
	IfNotExists     bool
	KeyspaceName    string
	WithReplication []*KeyValuePair
}

func (*CommandCreateKeyspace) GetCtxKeyspace

func (c *CommandCreateKeyspace) GetCtxKeyspace() string

func (*CommandCreateKeyspace) SetCtxKeyspace

func (c *CommandCreateKeyspace) SetCtxKeyspace(_ string)

type CommandCreateTable

type CommandCreateTable struct {
	CtxKeyspace          string
	IfNotExists          bool
	TableName            string
	ColumnDefs           []*CreateTableColumnDef
	PartitionKeyColumns  []string
	ClusteringKeyColumns []string
	ClusteringOrderBy    []*OrderByField
}

func (*CommandCreateTable) GetCtxKeyspace

func (c *CommandCreateTable) GetCtxKeyspace() string

func (*CommandCreateTable) SetCtxKeyspace

func (c *CommandCreateTable) SetCtxKeyspace(keyspace string)

type CommandDelete

type CommandDelete struct {
	CtxKeyspace     string
	TableName       string
	ColumnsToDelete []string
	WhereExpLexems  []*Lexem
	IfExists        bool
	WhereExpAst     ast.Expr
}

func (*CommandDelete) GetCtxKeyspace

func (c *CommandDelete) GetCtxKeyspace() string

func (*CommandDelete) SetCtxKeyspace

func (c *CommandDelete) SetCtxKeyspace(keyspace string)

type CommandDropKeyspace

type CommandDropKeyspace struct {
	IfExists     bool
	KeyspaceName string
}

func (*CommandDropKeyspace) GetCtxKeyspace

func (c *CommandDropKeyspace) GetCtxKeyspace() string

func (*CommandDropKeyspace) SetCtxKeyspace

func (c *CommandDropKeyspace) SetCtxKeyspace(_ string)

type CommandDropTable

type CommandDropTable struct {
	CtxKeyspace string
	IfExists    bool
	TableName   string
}

func (*CommandDropTable) GetCtxKeyspace

func (c *CommandDropTable) GetCtxKeyspace() string

func (*CommandDropTable) SetCtxKeyspace

func (c *CommandDropTable) SetCtxKeyspace(keyspace string)

type CommandInsert

type CommandInsert struct {
	CtxKeyspace string
	TableName   string

	ColumnNames []string

	ColumnValueLexems [][]*Lexem
	// ColumnValueExpAsts []ast.Expr
	ColumnValues []any

	IfNotExists bool
}

func (*CommandInsert) GetCtxKeyspace

func (c *CommandInsert) GetCtxKeyspace() string

func (*CommandInsert) SetCtxKeyspace

func (c *CommandInsert) SetCtxKeyspace(keyspace string)

type CommandSelect

type CommandSelect struct {
	CtxKeyspace     string
	Distinct        bool
	SelectExpLexems [][]*Lexem
	TableName       string
	WhereExpLexems  []*Lexem
	OrderByFields   []*OrderByField
	Limit           *Lexem
	SelectExpAsts   []ast.Expr
	SelectExpNames  []string
	WhereExpAst     ast.Expr
}

ORDER BY: The partition key must be defined in the WHERE clause and then the ORDER BY clause defines one or more clustering columns to use for ordering. The order of the specified columns must match the order of the clustering columns in the PRIMARY KEY definition

func (*CommandSelect) GetCtxKeyspace

func (c *CommandSelect) GetCtxKeyspace() string

func (*CommandSelect) SetCtxKeyspace

func (c *CommandSelect) SetCtxKeyspace(keyspace string)

type CommandTruncateTable

type CommandTruncateTable struct {
	CtxKeyspace string
	TableName   string
}

func (*CommandTruncateTable) GetCtxKeyspace

func (c *CommandTruncateTable) GetCtxKeyspace() string

func (*CommandTruncateTable) SetCtxKeyspace

func (c *CommandTruncateTable) SetCtxKeyspace(keyspace string)

type CommandUpdate

type CommandUpdate struct {
	CtxKeyspace string
	TableName   string

	ColumnSetExpressions []*ColumnSetExp
	ColumnSetExpAsts     []ast.Expr

	WhereExpLexems []*Lexem
	WhereExpAst    ast.Expr

	IfExists    bool
	IfExpLexems []*Lexem
	IfExpAst    ast.Expr
}

func (*CommandUpdate) GetCtxKeyspace

func (c *CommandUpdate) GetCtxKeyspace() string

func (*CommandUpdate) SetCtxKeyspace

func (c *CommandUpdate) SetCtxKeyspace(keyspace string)

type CommandUseKeyspace

type CommandUseKeyspace struct {
	KeyspaceName string
}

func (*CommandUseKeyspace) GetCtxKeyspace

func (c *CommandUseKeyspace) GetCtxKeyspace() string

func (*CommandUseKeyspace) SetCtxKeyspace

func (c *CommandUseKeyspace) SetCtxKeyspace(_ string)

type CqlDataType

type CqlDataType string
const (
	DataTypeAscii     CqlDataType = "ascii"
	DataTypeBigint    CqlDataType = "bigint"
	DataTypeBlob      CqlDataType = "blob"
	DataTypeBoolean   CqlDataType = "boolean"
	DataTypeCounter   CqlDataType = "counter"
	DataTypeDate      CqlDataType = "date"
	DataTypeDecimal   CqlDataType = "decimal"
	DataTypeDouble    CqlDataType = "double"
	DataTypeDuration  CqlDataType = "duration"
	DataTypeFloat     CqlDataType = "float"
	DataTypeInet      CqlDataType = "inet"
	DataTypeInt       CqlDataType = "int"
	DataTypeSmallint  CqlDataType = "smallint"
	DataTypeText      CqlDataType = "text"
	DataTypeTime      CqlDataType = "time"
	DataTypeTimestamp CqlDataType = "timestamp"
	DataTypeTimeuuid  CqlDataType = "timeuuid"
	DataTypeTinyint   CqlDataType = "tinyint"
	DataTypeUuid      CqlDataType = "uuid"
	DataTypeVarchar   CqlDataType = "varchar"
	DataTypeVarint    CqlDataType = "varint"
	DataTypeUnknown   CqlDataType = "unknown"
)

type CreateTableColumnDef

type CreateTableColumnDef struct {
	Name       string
	ColumnType gocql.Type
}

type Iter

type Iter interface {
	Host() *gocql.HostInfo
	Columns() []gocql.ColumnInfo
	Attempts() int
	Latency() int64
	Keyspace() string
	Table() string
	Scanner() gocql.Scanner
	Scan(dest ...any) bool
	GetCustomPayload() map[string][]byte
	Warnings() []string
	Close() error
	WillSwitchPage() bool
	PageState() []byte
	NumRows() int
	RowData() (gocql.RowData, error)
	SliceMap() ([]map[string]any, error)
	MapScan(m map[string]any) bool

	// These methods are not in gocql, but it's worth adding them:
	Err() error       // Called by tests and Query wrappers around Iter (why do we need them, btw?): MapScan, Scan, ScanCAS, MapScanCAS
	SetErr(err error) // Called by Iter.Scan
}

type KeyValuePair

type KeyValuePair struct {
	K string
	V *Lexem
}

type Keyspace

type Keyspace struct {
	TableMap         map[string]*tableStore
	WithReplication  []*KeyValuePair
	Lock             sync.RWMutex
	TableMetadataMap map[string]*gocql.TableMetadata
}

type Lexem

type Lexem struct {
	T LexemType
	V string
}

type LexemType

type LexemType int
const (
	LexemStringLiteral LexemType = iota
	LexemNumberLiteral
	LexemBoolLiteral
	LexemIdent
	LexemPointedIdent
	LexemKeyword
	LexemComma
	LexemSemicolon
	LexemArithmeticOp
	LexemLogicalOp
	LexemCqlOp
	LexemParenthesis
	LexemAsterisk
	LexemPointedAsterisk
	LexemNull
	LexemQuestionMark
	LexemAs
)

type OrderByField

type OrderByField struct {
	FieldName       string
	ClusteringOrder ClusteringOrderType
	CaseSensitivity ClusteringOrderCaseSensitivity
}

type PrimaryKeyType

type PrimaryKeyType int
const (
	PrimaryKeyPartition PrimaryKeyType = iota
	PrimaryKeyClustering
	PrimaryKeyNone
)

type Query

type Query interface {
	Consistency(c gocql.Consistency) Query
	GetConsistency() gocql.Consistency
	CustomPayload(customPayload map[string][]byte) Query
	Trace(trace gocql.Tracer) Query
	Observer(observer gocql.QueryObserver) Query
	PageSize(n int) Query
	DefaultTimestamp(enable bool) Query
	WithTimestamp(timestamp int64) Query
	RoutingKey(routingKey []byte) Query
	Keyspace() string
	Prefetch(p float64) Query
	RetryPolicy(r gocql.RetryPolicy) Query
	SetSpeculativeExecutionPolicy(sp gocql.SpeculativeExecutionPolicy) Query
	IsIdempotent() bool
	Idempotent(value bool) Query
	Bind(v ...any) Query
	SerialConsistency(cons gocql.Consistency) Query
	PageState(state []byte) Query
	NoSkipMetadata() Query
	Exec() error
	ExecContext(ctx context.Context) error
	Iter() Iter
	IterContext(ctx context.Context) Iter
	MapScan(m map[string]any) error
	MapScanContext(ctx context.Context, m map[string]any) error
	Scan(dest ...any) error
	ScanContext(ctx context.Context, dest ...any) error
	ScanCAS(dest ...any) (applied bool, err error)
	ScanCASContext(ctx context.Context, dest ...any) (applied bool, err error)
	MapScanCAS(dest map[string]any) (applied bool, err error)
	MapScanCASContext(ctx context.Context, dest map[string]any) (applied bool, err error)
	SetHostID(hostID string) Query
	GetHostID() string
	SetKeyspace(keyspace string) Query
	WithNowInSeconds(now int) Query
}

type Session

type Session interface {
	AwaitSchemaAgreement(ctx context.Context) error
	Query(stmt string, values ...any) Query
	Bind(stmt string, b func(q *gocql.QueryInfo) ([]any, error)) Query
	Close()
	Closed() bool
	KeyspaceMetadata(keyspace string) (*gocql.KeyspaceMetadata, error)
	Batch(typ gocql.BatchType) *gocql.Batch
	GetHosts() []*gocql.HostInfo
}

func NewGocqlSession

func NewGocqlSession(cfg gocql.ClusterConfig) (Session, error)

This helper was made public intentionally, this is how users instantiate sessions

Directories

Path Synopsis
Package eval contains base implementation of the AST expression eval engine with aggregation capabilities.
Package eval contains base implementation of the AST expression eval engine with aggregation capabilities.
Package gocqlshims contains interfaces that ideally should have been defined by gocql
Package gocqlshims contains interfaces that ideally should have been defined by gocql

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL