Skip to content

Commit

Permalink
Add database/sql driver (eatonphil#21)
Browse files Browse the repository at this point in the history
* Add driver and example

* Single backend for all connections

* Fixes for lint

* Add database/sql driver to readme
  • Loading branch information
eatonphil authored May 10, 2020
1 parent b893626 commit 0d0aa61
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 0 deletions.
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,67 @@ ok
ok
```

## Using the database/sql driver

See cmd/sqlexample/main.go:

```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](./cmd/main.go)
Expand Down
2 changes: 2 additions & 0 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type TableMetadata struct {

type Backend interface {
CreateTable(*CreateTableStatement) error
DropTable(*DropTableStatement) error
CreateIndex(*CreateIndexStatement) error
Insert(*InsertStatement) error
Select(*SelectStatement) (*Results, error)
GetTables() []TableMetadata
Expand Down
52 changes: 52 additions & 0 deletions cmd/sqlexample/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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)
}
}
149 changes: 149 additions & 0 deletions driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package gosql

import (
"database/sql"
"database/sql/driver"
"fmt"
"io"
)

type Rows struct {
columns []ResultColumn
index uint64
rows [][]Cell
}

func (r *Rows) Columns() []string {
columns := []string{}
for _, c := range r.columns {
columns = append(columns, c.Name)
}

return columns
}

func (r *Rows) Close() error {
r.index = uint64(len(r.rows))
return nil
}

func (r *Rows) Next(dest []driver.Value) error {
if r.index >= uint64(len(r.rows)) {
return io.EOF
}

row := r.rows[r.index]

for idx, cell := range row {
typ := r.columns[idx].Type
switch typ {
case IntType:
i := cell.AsInt()
if i == nil {
dest[idx] = i
} else {
dest[idx] = *i
}
case TextType:
s := cell.AsText()
if s == nil {
dest[idx] = s
} else {
dest[idx] = *s
}
case BoolType:
b := cell.AsBool()
if b == nil {
dest[idx] = b
} else {
dest[idx] = b
}
}
}

r.index++
return nil
}

type Conn struct {
bkd Backend
}

func (dc *Conn) doSelect(slct *SelectStatement) (driver.Rows, error) {
results, err := dc.bkd.Select(slct)
if err != nil {
return nil, err
}

return &Rows{
rows: results.Rows,
columns: results.Columns,
index: 0,
}, nil
}

func (dc *Conn) Query(query string, args []driver.Value) (driver.Rows, error) {
if len(args) > 0 {
// TODO: support parameterization
panic("Parameterization not supported")
}

parser := Parser{}
ast, err := parser.Parse(query)
if err != nil {
return nil, fmt.Errorf("Error while parsing: %s", err)
}

// NOTE: ignorning all but the first statement
stmt := ast.Statements[0]
switch stmt.Kind {
case CreateIndexKind:
err = dc.bkd.CreateIndex(stmt.CreateIndexStatement)
if err != nil {
return nil, fmt.Errorf("Error adding index on table: %s", err)
}
case CreateTableKind:
err = dc.bkd.CreateTable(stmt.CreateTableStatement)
if err != nil {
return nil, fmt.Errorf("Error creating table: %s", err)
}
case DropTableKind:
err = dc.bkd.DropTable(stmt.DropTableStatement)
if err != nil {
return nil, fmt.Errorf("Error dropping table: %s", err)
}
case InsertKind:
err = dc.bkd.Insert(stmt.InsertStatement)
if err != nil {
return nil, fmt.Errorf("Error inserting values: %s", err)
}
case SelectKind:
return dc.doSelect(stmt.SelectStatement)
}

return nil, nil
}

func (dc *Conn) Prepare(query string) (driver.Stmt, error) {
panic("Prepare not implemented")
}

func (dc *Conn) Begin() (driver.Tx, error) {
panic("Begin not implemented")
}

func (dc *Conn) Close() error {
return nil
}

type Driver struct {
bkd Backend
}

func (d *Driver) Open(name string) (driver.Conn, error) {
return &Conn{d.bkd}, nil
}

func init() {
sql.Register("postgres", &Driver{NewMemoryBackend()})
}

0 comments on commit 0d0aa61

Please sign in to comment.