Skip to content

Commit 5b9f89a

Browse files
committed
Merged fork/feature/duckdb branch
1 parent 698de4b commit 5b9f89a

25 files changed

Lines changed: 2604 additions & 782 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ bin/
44
.idea/
55
/notes.md
66
test/*.db
7+
test/*.db.wal
78

89
# Created by https://www.toptal.com/developers/gitignore/api/go,macos,linux,windows
910
# Edit at https://www.toptal.com/developers/gitignore?templates=go,macos,linux,windows

README.md

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
> I recently started a [discussion](https://github.com/proofrock/ws4sql/discussions/44) over the future direction for this project. Take a look, and chip in if you want!
44
5-
**`ws4sql`** is a server application that, applied to one or more sqlite files, allows to perform SQL queries and statements on them via REST (or better, JSON over HTTP).
5+
**`ws4sql`** is a server application that, applied to one or more database files, allows to perform SQL queries and statements on them via REST (or better, JSON over HTTP). It supports SQLite and DuckDB.
66

7-
Possible use cases are the ones where remote access to a sqlite db is useful/needed, for example a data layer for a remote application, possibly serverless or even called from a web page (*after security considerations* of course).
7+
Possible use cases are the ones where remote access to an embedded db is useful/needed, for example a data layer for a remote application, possibly serverless or even called from a web page (*after security considerations* of course).
88

9-
Client libraries are available, that will abstract the "raw" JSON-based communication. See
9+
Client libraries are available, that will abstract the "raw" JSON-based communication. See
1010
[here](https://github.com/proofrock/ws4sqlite-client-jvm) for Java/JVM, [here](https://github.com/proofrock/ws4sqlite-client-go) for Go(lang); others will follow.
1111

12-
As a quick example, after launching
12+
As a quick example, after launching
1313

1414
```bash
1515
ws4sql --db mydatabase.db
@@ -56,7 +56,7 @@ Obtaining an answer of
5656

5757
[Docs](https://germ.gitbook.io/ws4sql/), a [Tutorial](https://germ.gitbook.io/ws4sql/tutorial), a [Discord](https://discord.gg/nBCcq2VQPu).
5858

59-
- Aligned to [**SQLite 3.45.1**](https://sqlite.org/releaselog/3_45_1.html);
59+
- Aligned to [**SQLite 3.46.1**](https://sqlite.org/releaselog/3_46_1.html) and [**DuckDB 1.1.3**](https://github.com/duckdb/duckdb/releases/tag/v1.1.3);
6060
- A [**single executable file**](https://germ.gitbook.io/ws4sql/documentation/installation) (written in Go);
6161
- HTTP/JSON access, with [**client libraries**](https://germ.gitbook.io/ws4sql/client-libraries) for convenience;
6262
- Directly call `ws4sql` on a database (as above), many options available using a YAML companion file;
@@ -72,7 +72,7 @@ Obtaining an answer of
7272
- [**Scheduled tasks**](https://germ.gitbook.io/ws4sql/documentation/sched_tasks), cron-like and/or at startup, also configurable per-db;
7373
- Scheduled tasks can be: backup (with rotation), vacuum and/or a set of SQL statements;
7474
- Provide [**initialization statements**](https://germ.gitbook.io/ws4sql/documentation/configuration-file#initstatements) to execute when a DB is created;
75-
- [**WAL**](https://sqlite.org/wal.html) mode enabled by default, can be [disabled](https://germ.gitbook.io/ws4sql/documentation/configuration-file#disablewalmode);
75+
- (for SQLite) [**WAL**](https://sqlite.org/wal.html) mode enabled by default, can be [disabled](https://germ.gitbook.io/ws4sql/documentation/configuration-file#disablewalmode);
7676
- [**Quite fast**](features/performances.md)!
7777
- [**Embedded web server**](https://germ.gitbook.io/ws4sql/documentation/web-server) to directly serve web pages that can access ws4sql without CORS;
7878
- Compact codebase;
@@ -82,22 +82,22 @@ Obtaining an answer of
8282

8383
# Security Features
8484

85-
* [**Authentication**](documentation/security.md#authentication) can be configured
86-
* on the client, either using HTTP Basic Authentication or specifying the credentials in the request;
87-
* on the server, either by specifying credentials (also with hashed passwords) or providing a query to look them up in the db itself;
88-
* customizable `Not Authorized` error code (if 401 is not optimal)
89-
* A database can be opened in [**read-only mode**](documentation/security.md#read-only-databases) (only queries will be allowed);
90-
* It's possible to enforce using [**only stored statements**](documentation/security.md#stored-statements-to-prevent-sql-injection), to avoid some forms of SQL injection and receiving SQL from the client altogether;
91-
* [**CORS Allowed Origin**](documentation/security.md#cors-allowed-origin) can be configured and enforced;
92-
* It's possible to [**bind**](documentation/security.md#binding-to-a-network-interface) to a network interface, to limit access.
85+
- [**Authentication**](documentation/security.md#authentication) can be configured
86+
- on the client, either using HTTP Basic Authentication or specifying the credentials in the request;
87+
- on the server, either by specifying credentials (also with hashed passwords) or providing a query to look them up in the db itself;
88+
- customizable `Not Authorized` error code (if 401 is not optimal)
89+
- A database can be opened in [**read-only mode**](documentation/security.md#read-only-databases) (only queries will be allowed);
90+
- It's possible to enforce using [**only stored statements**](documentation/security.md#stored-statements-to-prevent-sql-injection), to avoid some forms of SQL injection and receiving SQL from the client altogether;
91+
- [**CORS Allowed Origin**](documentation/security.md#cors-allowed-origin) can be configured and enforced;
92+
- It's possible to [**bind**](documentation/security.md#binding-to-a-network-interface) to a network interface, to limit access.
9393

9494
# Design Choices
9595

9696
Some design choices:
9797

98-
* Very thin layer over SQLite. Errors and type translation, for example, are those provided by the SQLite driver;
99-
* Doesn't include HTTPS, as this can be done easily (and much more securely) with a [reverse proxy](documentation/security.md#use-a-reverse-proxy-if-going-on-the-internet);
100-
* Doesn't support SQLite extensions, to improve portability.
98+
- Very thin layer over the database. Errors and type translation, for example, are those provided by the db driver;
99+
- Doesn't include HTTPS, as this can be done easily (and much more securely) with a [reverse proxy](documentation/security.md#use-a-reverse-proxy-if-going-on-the-internet);
100+
- Doesn't support SQLite/DuckDB extensions, to improve portability.
101101

102102
# Contacts and Support
103103

@@ -112,7 +112,8 @@ Many thanks and all the credits to these awesome projects:
112112
- [gofiber's fiber](https://github.com/gofiber/fiber) (MIT License);
113113
- [klauspost's compress](https://github.com/klauspost/compress) (3-Clause BSD license);
114114
- [mitchellh's go-homedir](https://github.com/mitchellh/go-homedir) (MIT License);
115-
- [modernc.org's sqlite](https://gitlab.com/cznic/sqlite) (3-Clause BSD License);
115+
- [mattn's go-sqlite3](https://github.com/mattn/go-sqlite3) (MIT License);
116+
- [modernc.org's go-duckdb](https://github.com/marcboeker/go-duckdb) (MIT License);
116117
- [wI2L's jettison](https://github.com/wI2L/jettison) (MIT License)
117118
- and of course, [Google Go](https://go.dev).
118119

@@ -124,4 +125,4 @@ Import my [key](https://public.germanorizzo.it/4BD993A579025E4932C0110DC368E8BA7
124125

125126
```bash
126127
curl -sSL https://public.germanorizzo.it/4BD993A579025E4932C0110DC368E8BA7D4453F6.gpgkey | gpg --import -
127-
```
128+
```

ROAD_TO_WS4SQL.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,35 @@ The version in this branch is a work in progress to slowly add features and (unf
55
# Changes
66

77
- SQLite is embedded via [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) and CGO. Should be way faster.
8+
- Support for DuckDB (see below).
89
- Target platforms (because of CGO) are now 6 (`win/amd64`, `macos/amd64`, `macos/arm64`, `linux/amd64`, `linux/arm64`, `linux/arm6`).
9-
- [**BREAKING**] When running the app, the config files must be specified on the command line, the file paths cannot be used anymore (there). This is described in the "Migration" section below. The file path is in the config file.
10+
- [**BREAKING CHANGE**] When running the app, the config files must be specified on the command line, the file paths cannot be used anymore (there). This is described in the "Migration" section below. The file path is in the config file.
1011
- The only exception is a "simple case" to serve a file path without any config. This can be done with the new `--quick-db` parameter.
12+
- Fail fast if the request is empty, don't even attempt to authenticate
1113

1214
# Migration
1315

1416
- For any `--db` and `--mem-db` switch that was used, an explicit YAML config file must be created. The format is the same, but there is a new section at the beginning:
17+
1518
```yaml
1619
database:
17-
type: SQLITE # Only SQLITE for now. If omitted, defaults to SQLITE
18-
inMemory: false # If type = SQLITE. The db is a memory one? If omitted, defaults to false
19-
path: ".../test.db" # If type = SQLITE. The db file path.
20-
id: test # If omitted and !inMemory, calculates it from the file name (if type = SQLITE)
20+
type: SQLITE # SQLITE or DUCKDB. If omitted, defaults to SQLITE
21+
inMemory: false # If type = SQLITE|DUCKDB. The db is a memory one? If omitted, defaults to false
22+
path: ".../test.db" # If type = SQLITE|DUCKDB. The db file path.
23+
id: test # If omitted and !inMemory, calculates it from the file name (if type = SQLITE|DUCKDB)
2124
disableWALMode: false # If type = SQLITE. Same as before, but moved here.
2225
readOnly: false # Same as before, but moved here.
2326
```
2427
28+
# Specific to DuckDB
29+
30+
- `noFail` is not supported.
31+
- Placeholders for named parameters are `$VAL`, not `:VAL` as in SQLite.
32+
- As DuckDB does not support read-only transactions, when `readOnly` is specified the requests won't be processed in a transaction.
33+
2534
# Roadmap
2635

2736
1. Support mariadb/mysql
28-
1. Support duckdb (and iron out all the incompatibilities)
2937
1. Support postgresql
3038
1. ...
31-
1. Profit!
39+
1. Profit!

src/authentication.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
"strings"
2828

2929
mllog "github.com/proofrock/go-mylittlelogger"
30+
"github.com/proofrock/ws4sql/structs"
31+
"github.com/proofrock/ws4sql/utils"
3032
)
3133

3234
const (
@@ -38,11 +40,11 @@ const (
3840
// Version with explicit credentials, called by the authentication
3941
// middleware and by the "other" auth function, that accepts
4042
// a request.
41-
func applyAuthCreds(db *db, user, password string) error {
43+
func applyAuthCreds(db *structs.Db, user, password string) error {
4244
if db.Auth.ByQuery != "" {
4345
// Auth via query. Looks into the database for the credentials;
4446
// needs a query that is correctly parametrized.
45-
nameds := vals2nameds(map[string]interface{}{"user": user, "password": password})
47+
nameds := utils.Vals2nameds(map[string]interface{}{"user": user, "password": password})
4648
row := db.DbConn.QueryRowContext(context.Background(), db.Auth.ByQuery, nameds...)
4749
var foo interface{}
4850
if err := row.Scan(&foo); err == sql.ErrNoRows {
@@ -65,7 +67,7 @@ func applyAuthCreds(db *db, user, password string) error {
6567
// Checks auth. If auth is granted, returns nil, if not an error.
6668
// Version with request, extracts the credentials from the request
6769
// (when authmode = INLINE) and delegates to applyAuthCreds()
68-
func applyAuth(db *db, req *request) error {
70+
func applyAuth(db *structs.Db, req *structs.Request) error {
6971
if req.Credentials == nil {
7072
return errors.New("missing auth credentials")
7173
}
@@ -74,7 +76,7 @@ func applyAuth(db *db, req *request) error {
7476

7577
// Parses the authentication configurations. Builds a few structures,
7678
// should be pretty straightforward to read.
77-
func parseAuth(db *db) {
79+
func parseAuth(db *structs.Db) {
7880
auth := *db.Auth
7981
if strings.ToUpper(auth.Mode) != authModeInline && strings.ToUpper(auth.Mode) != authModeHttp {
8082
mllog.Fatal("Auth Mode must be INLINE or HTTP")

0 commit comments

Comments
 (0)