Add support for database json types.

This commit is contained in:
zer0sub 2019-04-04 15:25:45 +02:00
parent 37b0a6445b
commit d84deb8745
6 changed files with 94 additions and 5 deletions

View file

@ -88,6 +88,8 @@ func (c ColumnInfo) GoBaseType() string {
return "float64"
case "uuid":
return "uuid.UUID"
case "json", "jsonb":
return "types.JSONText"
default:
fmt.Println("Unknown go map type: " + c.DataType + ", " + c.EnumName + ", using string instead.")
return "string"

View file

@ -26,6 +26,8 @@ func (t TableInfo) GetImports() []string {
imports["time.Time"] = "time"
case "uuid.UUID":
imports["uuid.UUID"] = "github.com/google/uuid"
case "types.JSONText":
imports["types.JSONText"] = "github.com/sub0Zero/go-sqlbuilder/types"
}
}

View file

@ -51,10 +51,15 @@ func (a *{{.ToGoStructName}}) As(alias string) *{{.ToGoStructName}} {
var DataModelTemplate = `package model
{{range .GetImports}}
import "{{.}}"
{{ if .GetImports }}
import (
{{- range .GetImports}}
"{{.}}"
{{- end}}
)
{{end}}
type {{.ToGoModelStructName}} struct {
{{- range .Columns}}
{{.ToGoDMFieldName}} {{.ToGoType}} {{if .IsUnique}}` + "`sql:\"unique\"`" + ` {{end}}

View file

@ -600,7 +600,7 @@ func newScanType(columnType *sql.ColumnType) reflect.Type {
return nullInt32Type
case "INT8":
return nullInt64Type
case "VARCHAR", "TEXT", "", "_TEXT", "TSVECTOR", "BPCHAR", "BYTEA", "UUID":
case "VARCHAR", "TEXT", "", "_TEXT", "TSVECTOR", "BPCHAR", "BYTEA", "UUID", "JSON", "JSONB":
return nullStringType
case "FLOAT4":
return nullFloatType

View file

@ -18,8 +18,7 @@ func TestUUIDType(t *testing.T) {
assert.NilError(t, err)
fmt.Println(queryStr)
assert.Equal(t, queryStr, `SELECT all_types.character AS "all_types.character", all_types.character_varying AS "all_types.character_varying", all_types.text AS "all_types.text", all_types.bytea AS "all_types.bytea", all_types.timestamp_without_time_zone AS "all_types.timestamp_without_time_zone", all_types.timestamp_with_time_zone AS "all_types.timestamp_with_time_zone", all_types.uuid AS "all_types.uuid" FROM test_sample.all_types WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'`)
//assert.Equal(t, queryStr, `SELECT all_types.character AS "all_types.character", all_types.character_varying AS "all_types.character_varying", all_types.text AS "all_types.text", all_types.bytea AS "all_types.bytea", all_types.timestamp_without_time_zone AS "all_types.timestamp_without_time_zone", all_types.timestamp_with_time_zone AS "all_types.timestamp_with_time_zone", all_types.uuid AS "all_types.uuid", all_types.json AS "all_types.json", all_types.jsonb AS "all_types.jsonb" FROM test_sample.all_types WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11`)
result := model.AllTypes{}
err = query.Execute(db, &result)

81
types/types.go Normal file
View file

@ -0,0 +1,81 @@
package types
import (
"database/sql/driver"
"encoding/json"
"errors"
)
// JSONText is a json.RawMessage, which is a []byte underneath.
// Value() validates the json format in the source, and returns an error if
// the json is not valid. Scan does no validation. JSONText additionally
// implements `Unmarshal`, which unmarshals the json within to an interface{}
type JSONText json.RawMessage
var emptyJSON = JSONText("{}")
// MarshalJSON returns the *j as the JSON encoding of j.
func (j JSONText) MarshalJSON() ([]byte, error) {
if len(j) == 0 {
return emptyJSON, nil
}
return j, nil
}
// UnmarshalJSON sets *j to a copy of data
func (j *JSONText) UnmarshalJSON(data []byte) error {
if j == nil {
return errors.New("JSONText: UnmarshalJSON on nil pointer")
}
*j = append((*j)[0:0], data...)
return nil
}
// Value returns j as a value. This does a validating unmarshal into another
// RawMessage. If j is invalid json, it returns an error.
func (j JSONText) Value() (driver.Value, error) {
var m json.RawMessage
var err = j.Unmarshal(&m)
if err != nil {
return []byte{}, err
}
return []byte(j), nil
}
// Scan stores the src in *j. No validation is done.
func (j *JSONText) Scan(src interface{}) error {
if j == nil {
return errors.New("JSONText: Scan on nil pointer")
}
var source []byte
switch t := src.(type) {
case string:
source = []byte(t)
case []byte:
if len(t) == 0 {
source = emptyJSON
} else {
source = t
}
case nil:
*j = emptyJSON
default:
return errors.New("Incompatible type for JSONText")
}
*j = JSONText(append((*j)[0:0], source...))
return nil
}
// Unmarshal unmarshal's the json in j to v, as in json.Unmarshal.
func (j *JSONText) Unmarshal(v interface{}) error {
if len(*j) == 0 {
*j = emptyJSON
}
return json.Unmarshal([]byte(*j), v)
}
// String supports pretty printing for JSONText types.
func (j JSONText) String() string {
return string(j)
}