Skip to content

Commit 3695ee3

Browse files
committed
SQLite: Coerce jsonb columns to json before returning to Go code
This one follows up the discussion in #3953 to try and make the `jsonb` data type in SQLite usable (see discussion there, but I believe that it's currently not). According the SQLite docs on JSONB [1], it's considered a format that's internal to the database itself, and no attempt should be made to parse it elsewhere: > JSONB is not intended as an external format to be used by > applications. JSONB is designed for internal use by SQLite only. > Programmers do not need to understand the JSONB format in order to use > it effectively. Applications should access JSONB only through the JSON > SQL functions, not by looking at individual bytes of the BLOB. Currently, when trying to use a `jsonb` column in SQLite, sqlc ends up returning the internal binary data, which ends up being unparsable in Go: riverdrivertest.go:3030: Error Trace: /Users/brandur/Documents/projects/river/internal/riverinternaltest/riverdrivertest/riverdrivertest.go:3030 Error: Not equal: expected: []byte{0x7b, 0x22, 0x66, 0x6f, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x62, 0x61, 0x72, 0x22, 0x7d} actual : []byte{0x8c, 0x37, 0x66, 0x6f, 0x6f, 0x37, 0x62, 0x61, 0x72} Diff: --- Expected +++ Actual @@ -1,3 +1,3 @@ -([]uint8) (len=14) { - 00000000 7b 22 66 6f 6f 22 3a 20 22 62 61 72 22 7d |{"foo": "bar"}| +([]uint8) (len=9) { + 00000000 8c 37 66 6f 6f 37 62 61 72 |.7foo7bar| } Test: TestDriverRiverSQLite/QueueCreateOrSetUpdatedAt/InsertsANewQueueWithDefaultUpdatedAt The fix is that we should make sure to coerce `jsonb` columns back to `json` before returning. That's what this pull request does, intercepting `SELECT *` and wrapping `jsonb` columns with a `json(...)` invocation. I also assign `json` and `jsonb` a `[]byte` data type by default so they don't end up as `any`, which isn't very useful. `[]byte` is consistent with the default for `pgx/v5`. [1] https://sqlite.org/jsonb.html
1 parent 34f8c1b commit 3695ee3

File tree

14 files changed

+271
-0
lines changed

14 files changed

+271
-0
lines changed

internal/codegen/golang/sqlite_type.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ func sqliteType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.
5656
}
5757
return "sql.NullTime"
5858

59+
case "json", "jsonb":
60+
return "[]byte"
61+
5962
case "any":
6063
return "interface{}"
6164

internal/compiler/expand.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
"github.com/sqlc-dev/sqlc/internal/config"
9+
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
910
"github.com/sqlc-dev/sqlc/internal/source"
1011
"github.com/sqlc-dev/sqlc/internal/sql/ast"
1112
"github.com/sqlc-dev/sqlc/internal/sql/astutils"
@@ -149,6 +150,17 @@ func (c *Compiler) expandStmt(qc *QueryCatalog, raw *ast.RawStmt, node ast.Node)
149150
if counts[cname] > 1 {
150151
cname = tableName + "." + cname
151152
}
153+
// Under SQLite, neither json nor jsonb are real data types, and
154+
// rather just of type blob, so database drivers just return
155+
// whatever raw binary is stored as values. This is a problem
156+
// for jsonb, which is considered an internal format to SQLite
157+
// and no attempt should be made to parse it outside of the
158+
// database itself. For jsonb columns in SQLite, wrap returned
159+
// columns in `json(col)` to coerce the internal binary format
160+
// to JSON parsable by the user-space application.
161+
if _, ok := c.parser.(*sqlite.Parser); ok && column.DataType == "jsonb" {
162+
cname = "json(" + cname + ")"
163+
}
152164
cols = append(cols, cname)
153165
}
154166
}

internal/endtoend/testdata/jsonb/pgx/go/db.go

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/jsonb/pgx/go/models.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/jsonb/pgx/go/query.sql.go

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- name: InsertFoo :exec
2+
INSERT INTO foo (
3+
a,
4+
b,
5+
c,
6+
d
7+
) VALUES (
8+
@a,
9+
@b,
10+
@c,
11+
@d
12+
) RETURNING *;
13+
14+
-- name: SelectFoo :exec
15+
SELECT * FROM foo;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE foo (
2+
a json not null,
3+
b jsonb not null,
4+
c json,
5+
d jsonb
6+
);
7+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": "1",
3+
"packages": [
4+
{
5+
"path": "go",
6+
"engine": "postgresql",
7+
"sql_package": "pgx/v5",
8+
"name": "querytest",
9+
"schema": "schema.sql",
10+
"queries": "query.sql"
11+
}
12+
]
13+
}

internal/endtoend/testdata/jsonb/sqlite/go/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/jsonb/sqlite/go/models.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/jsonb/sqlite/go/query.sql.go

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- name: InsertFoo :exec
2+
INSERT INTO foo (
3+
a,
4+
b,
5+
c,
6+
d
7+
) VALUES (
8+
@a,
9+
@b,
10+
@c,
11+
@d
12+
) RETURNING *;
13+
14+
-- name: SelectFoo :exec
15+
SELECT * FROM foo;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE foo (
2+
a json not null,
3+
b jsonb not null,
4+
c json,
5+
d jsonb
6+
);
7+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "1",
3+
"packages": [
4+
{
5+
"path": "go",
6+
"engine": "sqlite",
7+
"name": "querytest",
8+
"schema": "schema.sql",
9+
"queries": "query.sql"
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)