Skip to content

Commit 88024b3

Browse files
committed
Added sqaccess
1 parent d65d437 commit 88024b3

File tree

7 files changed

+230
-49
lines changed

7 files changed

+230
-49
lines changed

etc/server.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ plugins:
33
- build/httpserver.plugin
44
- build/log.plugin
55
- build/sqlite3.plugin
6+
- build/sqaccess.plugin
67
- build/static.plugin
78

89
# HTTP Server parameters
@@ -41,3 +42,8 @@ sqlite3:
4142

4243
# Set max number of connections that can be simultaneously opened
4344
max: 50
45+
46+
sqaccess:
47+
# Set database to store usernames and passwords in
48+
database: main
49+

pkg/sqlite3/conn.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ func OpenPath(path string, flags SQFlag) (*Conn, error) {
8080
conn.SetCap(0)
8181
}
8282

83+
// Set foreign keys
84+
if flags&SQLITE_OPEN_FOREIGNKEYS != 0 {
85+
if err := conn.SetForeignKeyConstraints(true); err != nil {
86+
conn.ConnEx.Close()
87+
return nil, err
88+
}
89+
}
90+
8391
// Finalizer to panic when connection not closed before garbage collection
8492
_, file, line, _ := runtime.Caller(1)
8593
runtime.SetFinalizer(conn, func(conn *Conn) {

pkg/sqobj/sqobjects.go

Lines changed: 84 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,117 @@
11
package sqobj
22

33
import (
4-
5-
// Import Namespaces
64
"context"
75
"fmt"
86
"reflect"
97

8+
// Import Namespaces
109
. "github.com/djthorpe/go-errors"
1110
. "github.com/djthorpe/go-sqlite"
1211

1312
// Packages
14-
"github.com/djthorpe/go-sqlite/pkg/sqlite3"
13+
sqlite3 "github.com/djthorpe/go-sqlite/pkg/sqlite3"
1514
)
1615

1716
///////////////////////////////////////////////////////////////////////////////
1817
// TYPES
1918

2019
type Objects struct {
21-
*sqlite3.Conn
22-
2320
schema string
2421
m map[reflect.Type]*Class
22+
p SQPool
23+
c SQConnection
2524
}
2625

2726
///////////////////////////////////////////////////////////////////////////////
2827
// LIFECYCLE
2928

30-
// With creates an SQObjects with an existing database connection and named schema
31-
func With(conn *sqlite3.Conn, schema string, classes ...SQClass) (*Objects, error) {
29+
// WithPool creates an SQObjects with a connection pool and named schema
30+
func WithPool(pool SQPool, schema string, classes ...SQClass) (*Objects, error) {
31+
objects := new(Objects)
32+
33+
// Check parameters
34+
if pool == nil || len(classes) == 0 {
35+
return nil, ErrBadParameter.With("WithPool")
36+
} else {
37+
objects.p = pool
38+
}
39+
40+
return objects.with(schema, classes...)
41+
}
42+
43+
// With creates an SQObjects with a database connection and named schema
44+
func With(conn SQConnection, schema string, classes ...SQClass) (*Objects, error) {
3245
objects := new(Objects)
3346

3447
// Check parameters
3548
if conn == nil || len(classes) == 0 {
3649
return nil, ErrBadParameter.With("With")
50+
} else {
51+
objects.c = conn
52+
}
53+
54+
return objects.with(schema, classes...)
55+
}
56+
57+
///////////////////////////////////////////////////////////////////////////////
58+
// STRINGIFY
59+
60+
func (obj *Objects) String() string {
61+
str := "<sqobjects"
62+
str += fmt.Sprintf(" schema=%q", obj.schema)
63+
for _, c := range obj.m {
64+
str += " " + c.String()
65+
}
66+
str += fmt.Sprint(" ", obj.SQConnection)
67+
return str + ">"
68+
}
69+
70+
///////////////////////////////////////////////////////////////////////////////
71+
// PUBLIC METHODS
72+
73+
// Write objects (insert or update) to the database
74+
func (obj *Objects) Write(ctx context.Context, v ...interface{}) error {
75+
return obj.c.Do(ctx, SQLITE_NONE, func(txn SQTransaction) error {
76+
for _, v := range v {
77+
rv := ValueOf(v)
78+
class, exists := obj.m[rv.Type()]
79+
if !exists {
80+
return ErrBadParameter.Withf("Write: %v", v)
81+
}
82+
if r, err := class.UpsertKeys(txn, v); err != nil {
83+
return err
84+
} else {
85+
// TODO: Pass rowid and primary keys to next object
86+
fmt.Println(r[0])
87+
}
88+
}
89+
return nil
90+
})
91+
}
92+
93+
///////////////////////////////////////////////////////////////////////////////
94+
// PRIVATE METHODS
95+
96+
func (objects *Objects) conn(ctx context.Context) SQConnection {
97+
98+
}
99+
100+
func (objects *Objects) with(schema string, classes ...SQClass) (*Objects, error) {
101+
if schema == "" {
102+
schema = sqlite3.DefaultSchema
37103
}
104+
105+
// Set connection, classes
106+
objects.m = make(map[reflect.Type]*Class, len(classes))
107+
objects.schema = schema
108+
38109
if schema == "" {
39110
schema = sqlite3.DefaultSchema
40111
}
41112

42113
// Set connection, classes
43-
objects.Conn = conn
114+
objects.c = conn
44115
objects.m = make(map[reflect.Type]*Class, len(classes))
45116
objects.schema = schema
46117

@@ -49,6 +120,11 @@ func With(conn *sqlite3.Conn, schema string, classes ...SQClass) (*Objects, erro
49120
return nil, ErrNotFound.Withf("schema %q", schema)
50121
}
51122

123+
// Error if foreign keys not supported
124+
if !conn.Flags().Is(SQLITE_OPEN_FOREIGNKEYS) {
125+
return nil, ErrBadParameter.With("SQLITE_OPEN_FOREIGNKEYS")
126+
}
127+
52128
// Register classes
53129
for _, class := range classes {
54130
if class, ok := class.(*Class); !ok {
@@ -58,11 +134,6 @@ func With(conn *sqlite3.Conn, schema string, classes ...SQClass) (*Objects, erro
58134
}
59135
}
60136

61-
// Set foreign keys on
62-
if err := conn.SetForeignKeyConstraints(true); err != nil {
63-
return nil, err
64-
}
65-
66137
// Create schema - tables, indexes
67138
if err := conn.Do(context.Background(), SQLITE_TXN_NO_FOREIGNKEY_CONSTRAINTS, func(txn SQTransaction) error {
68139
// Create all classes
@@ -79,38 +150,3 @@ func With(conn *sqlite3.Conn, schema string, classes ...SQClass) (*Objects, erro
79150
// Return success
80151
return objects, nil
81152
}
82-
83-
///////////////////////////////////////////////////////////////////////////////
84-
// STRINGIFY
85-
86-
func (obj *Objects) String() string {
87-
str := "<sqobjects"
88-
str += fmt.Sprintf(" schema=%q", obj.schema)
89-
for _, c := range obj.m {
90-
str += " " + c.String()
91-
}
92-
str += " " + obj.Conn.String()
93-
return str + ">"
94-
}
95-
96-
///////////////////////////////////////////////////////////////////////////////
97-
// WRITE OBJECTS
98-
99-
// Write objects (insert or update) to the database
100-
func (obj *Objects) Write(ctx context.Context, v ...interface{}) error {
101-
return obj.Conn.Do(ctx, SQLITE_NONE, func(txn SQTransaction) error {
102-
for _, v := range v {
103-
rv := ValueOf(v)
104-
class, exists := obj.m[rv.Type()]
105-
if !exists {
106-
return ErrBadParameter.Withf("Write: %v", v)
107-
}
108-
if r, err := class.UpsertKeys(txn, v); err != nil {
109-
return err
110-
} else {
111-
fmt.Println(r[0])
112-
}
113-
}
114-
return nil
115-
})
116-
}

plugin/sqaccess/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# sqaccess API plugin
2+
3+
The API plugin provides authentication (NOTE: not authorization) by username
4+
and password as "middleware" to any requests.
5+
6+
It is a plugin to
7+
the monolithic server [`github.com/djthorpe/go-server`](github.com/djthorpe/go-server)
8+
and as such the server needs to be installed in addition to the plugin. Instructions on how
9+
to install the server and necessary plugins is described below.
10+
11+
This package is part of a wider project, `github.com/djthorpe/go-sqlite`.
12+
Please see the [module documentation](https://github.com/djthorpe/go-sqlite/blob/master/README.md)
13+
for more information
14+

plugin/sqaccess/main.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
// Packages
8+
"github.com/djthorpe/go-sqlite/pkg/sqlite3"
9+
"github.com/djthorpe/go-sqlite/pkg/sqobj"
10+
11+
// Namespace imports
12+
. "github.com/djthorpe/go-server"
13+
. "github.com/djthorpe/go-sqlite"
14+
. "github.com/djthorpe/go-sqlite/pkg/lang"
15+
)
16+
17+
///////////////////////////////////////////////////////////////////////////////
18+
// TYPES
19+
20+
type Config struct {
21+
Database string `yaml:"database"`
22+
}
23+
24+
type User struct {
25+
Username string `sqlite:"user,primary"`
26+
Hash string `sqlite:"hash"`
27+
}
28+
29+
type plugin struct {
30+
Config
31+
SQPool
32+
}
33+
34+
///////////////////////////////////////////////////////////////////////////////
35+
// GLOBALS
36+
37+
var (
38+
clUser = sqobj.MustRegisterClass(N("User"), User{})
39+
)
40+
41+
///////////////////////////////////////////////////////////////////////////////
42+
// LIFECYCLE
43+
44+
// Create the module
45+
func New(ctx context.Context, provider Provider) Plugin {
46+
p := new(plugin)
47+
48+
// Get configuration
49+
cfg := Config{}
50+
if err := provider.GetConfig(ctx, &cfg); err != nil {
51+
provider.Print(ctx, err)
52+
return nil
53+
} else {
54+
p.Config = cfg
55+
}
56+
57+
// Get sqlite3
58+
if pool := provider.GetPlugin(ctx, "sqlite3").(SQPool); pool == nil {
59+
provider.Print(ctx, "no sqlite3 plugin")
60+
return nil
61+
} else {
62+
p.SQPool = pool
63+
}
64+
65+
// Get a connection
66+
conn := p.Get(ctx)
67+
if conn == nil {
68+
provider.Print(ctx, "no sqlite3 connection")
69+
return nil
70+
}
71+
defer p.Put(conn)
72+
73+
// Create the database
74+
if sqobj, err := sqobj.With(conn.(*sqlite3.Conn), p.Database, clUser); err != nil {
75+
provider.Print(ctx, err)
76+
return nil
77+
} else {
78+
fmt.Println(sqobj)
79+
}
80+
81+
// Return success
82+
return p
83+
}
84+
85+
///////////////////////////////////////////////////////////////////////////////
86+
// STRINGIFY
87+
88+
func (p *plugin) String() string {
89+
str := "<sqaccess"
90+
if p.Config.Database != "" {
91+
str += fmt.Sprintf(" database=%q", p.Config.Database)
92+
}
93+
return str + ">"
94+
}
95+
96+
///////////////////////////////////////////////////////////////////////////////
97+
// PUBLIC METHODS
98+
99+
func Name() string {
100+
return "sqaccess"
101+
}
102+
103+
func (p *plugin) Run(ctx context.Context, provider Provider) error {
104+
105+
// Run until cancelled - print any errors from the connection pool
106+
FOR_LOOP:
107+
for {
108+
select {
109+
case <-ctx.Done():
110+
break FOR_LOOP
111+
}
112+
}
113+
114+
// Return success
115+
return nil
116+
}

plugin/sqlite3/handlers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ func (p *plugin) ServeTokenizer(w http.ResponseWriter, req *http.Request) {
311311
// Populate response
312312
response := TokenizerResponse{
313313
Html: html,
314-
Complete: sqlite3.IsComplete(query.Sql),
314+
Complete: tokenizer.IsComplete(query.Sql),
315315
}
316316

317317
// Serve response

sqlite.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ const (
148148
SQLITE_TXN_NO_FOREIGNKEY_CONSTRAINTS SQFlag = (1 << 19) // Drop foreign key constraints on this transaction
149149
SQLITE_OPEN_CACHE SQFlag = (1 << 20) // Cache prepared statements
150150
SQLITE_OPEN_OVERWRITE SQFlag = (1 << 21) // Overwrite objects
151+
SQLITE_OPEN_FOREIGNKEYS SQFlag = (1 << 22) // Enable foreign key support
151152
)
152153

153154
const (

0 commit comments

Comments
 (0)