gosql

package module
v0.0.0-...-5297572 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2021 License: MIT Imports: 13 Imported by: 1

README

gosql

An early PostgreSQL implementation in Go.

gosql

Example

$ git clone git@github.com:eatonphil/gosql
$ cd gosql
$ go run cmd/main.go
Welcome to gosql.
# CREATE TABLE users (id INT PRIMARY KEY, name TEXT, age INT);
ok
# \d users
Table "users"
  Column |  Type   | Nullable
---------+---------+-----------
  id     | integer | not null
  name   | text    |
  age    | integer |
Indexes:
        "users_pkey" PRIMARY KEY, rbtree ("id")

# INSERT INTO users VALUES (1, 'Corey', 34);
ok
# INSERT INTO users VALUES (1, 'Max', 29);
Error inserting values: Duplicate key value violates unique constraint
# INSERT INTO users VALUES (2, 'Max', 29);
ok
# SELECT * FROM users WHERE id = 2;
  id | name | age
-----+------+------
   2 | Max  |  29
(1 result)
ok
# SELECT id, name, age + 3 FROM users WHERE id = 2 OR id = 1;
  id | name  | ?column?
-----+-------+-----------
   1 | Corey |       37
   2 | Max   |       32
(2 results)
ok

Using the database/sql driver

See cmd/sqlexample/main.go:

package main

import (
	"database/sql"
	"fmt"

	_ "github.com/eatonphil/gosql"
)

func main() {
	db, err := sql.Open("postgres", "")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	_, err = db.Query("CREATE TABLE users (name TEXT, age INT);")
	if err != nil {
		panic(err)
	}

	_, err = db.Query("INSERT INTO users VALUES ('Terry', 45);")
	if err != nil {
		panic(err)
	}

	_, err = db.Query("INSERT INTO users VALUES ('Anette', 57);")
	if err != nil {
		panic(err)
	}

	rows, err := db.Query("SELECT name, age FROM users;")
	if err != nil {
		panic(err)
	}

	var name string
	var age uint64
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&name, &age)
		if err != nil {
			panic(err)
		}

		fmt.Printf("Name: %s, Age: %d\n", name, age)
	}

	if err = rows.Err(); err != nil {
		panic(err)
	}
}

Parameterization is not currently supported.

Architecture

  • cmd/main.go
    • Contains the REPL and high-level interface to the project
    • Dataflow is: user input -> lexer -> parser -> in-memory backend
  • lexer.go
    • Handles breaking user input into tokens for the parser
  • parser.go
    • Matches a list of tokens into an AST or fails if the user input is not a valid program
  • memory.go
    • An example, in-memory backend supporting the Backend interface (defined in backend.go)

Contributing

  • Add a new operator (such as -, *, etc.)
  • Add a new data type (such as `VARCHAR(n)``)

In each case, you'll probably have to add support in the lexer, parser, and in-memory backend. I recommend going in that order.

In all cases, make sure the code is formatted (make fmt), linted (make lint) and passes tests (make test). New code should have tests.

Blog series

Further reading

Here are some similar projects written in Go.

  • go-mysql-server
    • This is a MySQL frontend (with an in-memory backend for testing only).
  • ramsql
    • This is a WIP PostgreSQL-compatible in-memory database.
  • CockroachDB
    • This is a production-ready PostgreSQL-compatible database.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrTableDoesNotExist         = errors.New("Table does not exist")
	ErrTableAlreadyExists        = errors.New("Table already exists")
	ErrIndexAlreadyExists        = errors.New("Index already exists")
	ErrViolatesUniqueConstraint  = errors.New("Duplicate key value violates unique constraint")
	ErrViolatesNotNullConstraint = errors.New("Value violates not null constraint")
	ErrColumnDoesNotExist        = errors.New("Column does not exist")
	ErrInvalidSelectItem         = errors.New("Select item is not valid")
	ErrInvalidDatatype           = errors.New("Invalid datatype")
	ErrMissingValues             = errors.New("Missing values")
	ErrInvalidCell               = errors.New("Cell is invalid")
	ErrInvalidOperands           = errors.New("Operands are invalid")
	ErrPrimaryKeyAlreadyExists   = errors.New("Primary key already exists")
)

Functions

func RunRepl

func RunRepl(b Backend)

Types

type Ast

type Ast struct {
	Statements []*Statement
}

type AstKind

type AstKind uint
const (
	SelectKind AstKind = iota
	CreateTableKind
	CreateIndexKind
	DropTableKind
	InsertKind
)

type Backend

type Backend interface {
	CreateTable(*CreateTableStatement) error
	DropTable(*DropTableStatement) error
	CreateIndex(*CreateIndexStatement) error
	Insert(*InsertStatement) error
	Select(*SelectStatement) (*Results, error)
	GetTables() []TableMetadata
}

type BinaryExpression

type BinaryExpression struct {
	A  Expression
	B  Expression
	Op Token
}

func (BinaryExpression) GenerateCode

func (be BinaryExpression) GenerateCode() string

type Cell

type Cell interface {
	AsText() *string
	AsInt() *int32
	AsBool() *bool
}

type ColumnDefinition

type ColumnDefinition struct {
	Name       Token
	Datatype   Token
	PrimaryKey bool
}

type ColumnType

type ColumnType uint
const (
	TextType ColumnType = iota
	IntType
	BoolType
)

func (ColumnType) String

func (c ColumnType) String() string

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

func (*Conn) Begin

func (dc *Conn) Begin() (driver.Tx, error)

func (*Conn) Close

func (dc *Conn) Close() error

func (*Conn) Prepare

func (dc *Conn) Prepare(query string) (driver.Stmt, error)

func (*Conn) Query

func (dc *Conn) Query(query string, args []driver.Value) (driver.Rows, error)

type CreateIndexStatement

type CreateIndexStatement struct {
	Name       Token
	Unique     bool
	PrimaryKey bool
	Table      Token
	Exp        Expression
}

func (CreateIndexStatement) GenerateCode

func (cis CreateIndexStatement) GenerateCode() string

type CreateTableStatement

type CreateTableStatement struct {
	Name Token
	Cols *[]*ColumnDefinition
}

func (CreateTableStatement) GenerateCode

func (cts CreateTableStatement) GenerateCode() string

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

func (*Driver) Open

func (d *Driver) Open(name string) (driver.Conn, error)

type DropTableStatement

type DropTableStatement struct {
	Name Token
}

func (DropTableStatement) GenerateCode

func (dts DropTableStatement) GenerateCode() string

type EmptyBackend

type EmptyBackend struct{}

Useful to embed when prototyping new backends

func (EmptyBackend) CreateIndex

func (eb EmptyBackend) CreateIndex(_ *CreateIndexStatement) error

func (EmptyBackend) CreateTable

func (eb EmptyBackend) CreateTable(_ *CreateTableStatement) error

func (EmptyBackend) DropTable

func (eb EmptyBackend) DropTable(_ *DropTableStatement) error

func (EmptyBackend) GetTables

func (eb EmptyBackend) GetTables() []TableMetadata

func (EmptyBackend) Insert

func (eb EmptyBackend) Insert(_ *InsertStatement) error

func (EmptyBackend) Select

func (eb EmptyBackend) Select(_ *SelectStatement) (*Results, error)

type Expression

type Expression struct {
	Literal *Token
	Binary  *BinaryExpression
	Kind    ExpressionKind
}

func (Expression) GenerateCode

func (e Expression) GenerateCode() string

type ExpressionKind

type ExpressionKind uint
const (
	LiteralKind ExpressionKind = iota
	BinaryKind
)

type Index

type Index struct {
	Name       string
	Exp        string
	Type       string
	Unique     bool
	PrimaryKey bool
}

type InsertStatement

type InsertStatement struct {
	Table  Token
	Values *[]*Expression
}

func (InsertStatement) GenerateCode

func (is InsertStatement) GenerateCode() string

type Keyword

type Keyword string

for storing SQL reserved Keywords

const (
	SelectKeyword     Keyword = "select"
	FromKeyword       Keyword = "from"
	AsKeyword         Keyword = "as"
	TableKeyword      Keyword = "table"
	CreateKeyword     Keyword = "create"
	DropKeyword       Keyword = "drop"
	InsertKeyword     Keyword = "insert"
	IntoKeyword       Keyword = "into"
	ValuesKeyword     Keyword = "values"
	IntKeyword        Keyword = "int"
	TextKeyword       Keyword = "text"
	BoolKeyword       Keyword = "boolean"
	WhereKeyword      Keyword = "where"
	AndKeyword        Keyword = "and"
	OrKeyword         Keyword = "or"
	TrueKeyword       Keyword = "true"
	FalseKeyword      Keyword = "false"
	UniqueKeyword     Keyword = "unique"
	IndexKeyword      Keyword = "index"
	OnKeyword         Keyword = "on"
	PrimarykeyKeyword Keyword = "primary key"
	NullKeyword       Keyword = "null"
	LimitKeyword      Keyword = "limit"
	OffsetKeyword     Keyword = "offset"
)

type Location

type Location struct {
	Line uint
	Col  uint
}

location of the token in source code

type MemoryBackend

type MemoryBackend struct {
	// contains filtered or unexported fields
}

func NewMemoryBackend

func NewMemoryBackend() *MemoryBackend

func (*MemoryBackend) CreateIndex

func (mb *MemoryBackend) CreateIndex(ci *CreateIndexStatement) error

func (*MemoryBackend) CreateTable

func (mb *MemoryBackend) CreateTable(crt *CreateTableStatement) error

func (*MemoryBackend) DropTable

func (mb *MemoryBackend) DropTable(dt *DropTableStatement) error

func (*MemoryBackend) GetTables

func (mb *MemoryBackend) GetTables() []TableMetadata

func (*MemoryBackend) Insert

func (mb *MemoryBackend) Insert(inst *InsertStatement) error

func (*MemoryBackend) Select

func (mb *MemoryBackend) Select(slct *SelectStatement) (*Results, error)

type Parser

type Parser struct {
	HelpMessagesDisabled bool
}

func (Parser) Parse

func (p Parser) Parse(source string) (*Ast, error)

type ResultColumn

type ResultColumn struct {
	Type    ColumnType
	Name    string
	NotNull bool
}

type Results

type Results struct {
	Columns []ResultColumn
	Rows    [][]Cell
}

type Rows

type Rows struct {
	// contains filtered or unexported fields
}

func (*Rows) Close

func (r *Rows) Close() error

func (*Rows) Columns

func (r *Rows) Columns() []string

func (*Rows) Next

func (r *Rows) Next(dest []driver.Value) error

type SelectItem

type SelectItem struct {
	Exp      *Expression
	Asterisk bool // for *
	As       *Token
}

type SelectStatement

type SelectStatement struct {
	Item   *[]*SelectItem
	From   *Token
	Where  *Expression
	Limit  *Expression
	Offset *Expression
}

func (SelectStatement) GenerateCode

func (ss SelectStatement) GenerateCode() string

type Statement

type Statement struct {
	SelectStatement      *SelectStatement
	CreateTableStatement *CreateTableStatement
	CreateIndexStatement *CreateIndexStatement
	DropTableStatement   *DropTableStatement
	InsertStatement      *InsertStatement
	Kind                 AstKind
}

func (Statement) GenerateCode

func (s Statement) GenerateCode() string

type Symbol

type Symbol string

for storing SQL syntax

const (
	SemicolonSymbol  Symbol = ";"
	AsteriskSymbol   Symbol = "*"
	CommaSymbol      Symbol = ","
	LeftParenSymbol  Symbol = "("
	RightParenSymbol Symbol = ")"
	EqSymbol         Symbol = "="
	NeqSymbol        Symbol = "<>"
	NeqSymbol2       Symbol = "!="
	ConcatSymbol     Symbol = "||"
	PlusSymbol       Symbol = "+"
	LtSymbol         Symbol = "<"
	LteSymbol        Symbol = "<="
	GtSymbol         Symbol = ">"
	GteSymbol        Symbol = ">="
)

type TableMetadata

type TableMetadata struct {
	Name    string
	Columns []ResultColumn
	Indexes []Index
}

type Token

type Token struct {
	Value string
	Kind  TokenKind
	Loc   Location
}

type TokenKind

type TokenKind uint
const (
	KeywordKind TokenKind = iota
	SymbolKind
	IdentifierKind
	StringKind
	NumericKind
	BoolKind
	NullKind
)

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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