Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PostgreSQL JSONB data type-new code. #69

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/models/db_postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var pgTypes = map[fieldtype.Type]string{
fieldtype.Selection: "character varying",
fieldtype.Many2One: "integer",
fieldtype.One2One: "integer",
fieldtype.JSON: "jsonb",
}

// connectionString returns the connection string for the given parameters
Expand Down
42 changes: 42 additions & 0 deletions src/models/fields_json_defs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

package models

import (

"github.com/hexya-erp/hexya/src/models/fieldtype"
"github.com/hexya-erp/hexya/src/models/types"
)

// A JSONField is a field for storing jsonb.
type JSONField struct {
JSON string
String string
Help string
Stored bool
Required bool
ReadOnly bool
RequiredFunc func(Environment) (bool, Conditioner)
ReadOnlyFunc func(Environment) (bool, Conditioner)
InvisibleFunc func(Environment) (bool, Conditioner)
Unique bool
Index bool
Compute Methoder
Depends []string
Related string
NoCopy bool
GoType interface{}
Translate bool
OnChange Methoder
Constraint Methoder
Inverse Methoder
Contexts FieldContexts
Default func(Environment) interface{}
}

// DeclareField creates a html field for the given FieldsCollection with the given name.
func (jsonf JSONField) DeclareField(fc *FieldsCollection, name string) *Field {
fInfo := genericDeclareField(fc, &jsonf, name, fieldtype.JSON, new(types.JSONText))
return fInfo
}


4 changes: 4 additions & 0 deletions src/models/fieldtype/fieldtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package fieldtype
import (
"reflect"

"github.com/hexya-erp/hexya/src/models/types"
"github.com/hexya-erp/hexya/src/models/types/dates"
)

Expand All @@ -31,6 +32,7 @@ const (
Reference Type = "reference"
Selection Type = "selection"
Text Type = "text"
JSON Type = "json"
)

// IsRelationType returns true if this type is a relation.
Expand Down Expand Up @@ -87,6 +89,8 @@ func (t Type) DefaultGoType() reflect.Type {
return reflect.TypeOf(*new(dates.Date))
case DateTime:
return reflect.TypeOf(*new(dates.DateTime))
case JSON:
return reflect.TypeOf(*new(types.JSONText))
case Float:
return reflect.TypeOf(*new(float64))
case Integer, Many2One, One2One, Rev2One:
Expand Down
118 changes: 118 additions & 0 deletions src/models/types/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package types

import (
"database/sql/driver"
"encoding/json"
"errors"
)

// Source: https://github.com/jmoiron/sqlx/blob/master/types/types.go
//
// For HEXYA: In db.sanitizeQuery() function, call to sqlx.Rebind(sqlx.BindType(db.DriverName()), q)
// causes invalid SQL statement with json.RawMessage type. So, I change JSONText to string type

//type JSONText json.RawMessage
type JSONText string

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 "{}", err
}
return string(j), nil
}

// Scan stores the src in *j. No validation is done.
func (j *JSONText) Scan(src interface{}) error {
if src == nil {
*j = emptyJSON
} else {
*j = JSONText(string(src.([]uint8)))
}
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)
}

// NullJSONText represents a JSONText that may be null.
// NullJSONText implements the scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullJSONText struct {
JSONText
Valid bool // Valid is true if JSONText is not NULL
}

// Scan implements the Scanner interface.
func (n *NullJSONText) Scan(value interface{}) error {
if value == nil {
n.JSONText, n.Valid = emptyJSON, false
return nil
}
n.Valid = true
return n.JSONText.Scan(value)
}

// Value implements the driver Valuer interface.
func (n NullJSONText) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.JSONText.Value()
}

// BitBool is an implementation of a bool for the MySQL type BIT(1).
// This type allows you to avoid wasting an entire byte for MySQL's boolean type TINYINT.
type BitBool bool

// Value implements the driver.Valuer interface,
// and turns the BitBool into a bitfield (BIT(1)) for MySQL storage.
func (b BitBool) Value() (driver.Value, error) {
if b {
return []byte{1}, nil
}
return []byte{0}, nil
}

// Scan implements the sql.Scanner interface,
// and turns the bitfield incoming from MySQL into a BitBool
func (b *BitBool) Scan(src interface{}) error {
v, ok := src.([]byte)
if !ok {
return errors.New("bad []byte type assertion")
}
*b = v[0] == 1
return nil
}