summaryrefslogtreecommitdiffstats
path: root/pkg/types
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkg/types/acknowledgement_state.go61
-rw-r--r--pkg/types/binary.go137
-rw-r--r--pkg/types/bool.go105
-rw-r--r--pkg/types/comment_type.go79
-rw-r--r--pkg/types/float.go68
-rw-r--r--pkg/types/int.go68
-rw-r--r--pkg/types/notification_states.go78
-rw-r--r--pkg/types/notification_type.go68
-rw-r--r--pkg/types/notification_types.go81
-rw-r--r--pkg/types/state_type.go65
-rw-r--r--pkg/types/string.go74
-rw-r--r--pkg/types/unix_milli.go95
-rw-r--r--pkg/types/uuid.go24
13 files changed, 1003 insertions, 0 deletions
diff --git a/pkg/types/acknowledgement_state.go b/pkg/types/acknowledgement_state.go
new file mode 100644
index 0000000..5bff613
--- /dev/null
+++ b/pkg/types/acknowledgement_state.go
@@ -0,0 +1,61 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+)
+
+// AcknowledgementState specifies an acknowledgement state (yes, no, sticky).
+type AcknowledgementState uint8
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (as *AcknowledgementState) UnmarshalText(text []byte) error {
+ return as.UnmarshalJSON(text)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (as *AcknowledgementState) UnmarshalJSON(data []byte) error {
+ var i uint8
+ if err := internal.UnmarshalJSON(data, &i); err != nil {
+ return err
+ }
+
+ a := AcknowledgementState(i)
+ if _, ok := acknowledgementStates[a]; !ok {
+ return badAcknowledgementState(data)
+ }
+
+ *as = a
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (as AcknowledgementState) Value() (driver.Value, error) {
+ if v, ok := acknowledgementStates[as]; ok {
+ return v, nil
+ } else {
+ return nil, badAcknowledgementState(as)
+ }
+}
+
+// badAcknowledgementState returns an error about a syntactically, but not semantically valid AcknowledgementState.
+func badAcknowledgementState(s interface{}) error {
+ return errors.Errorf("bad acknowledgement state: %#v", s)
+}
+
+// acknowledgementStates maps all valid AcknowledgementState values to their SQL representation.
+var acknowledgementStates = map[AcknowledgementState]string{
+ 0: "n",
+ 1: "y",
+ 2: "sticky",
+}
+
+// Assert interface compliance.
+var (
+ _ encoding.TextUnmarshaler = (*AcknowledgementState)(nil)
+ _ json.Unmarshaler = (*AcknowledgementState)(nil)
+ _ driver.Valuer = AcknowledgementState(0)
+)
diff --git a/pkg/types/binary.go b/pkg/types/binary.go
new file mode 100644
index 0000000..00a5417
--- /dev/null
+++ b/pkg/types/binary.go
@@ -0,0 +1,137 @@
+package types
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding"
+ "encoding/hex"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/icinga/icingadb/pkg/contracts"
+ "github.com/pkg/errors"
+)
+
+// Binary nullable byte string. Hex as JSON.
+type Binary []byte
+
+// nullBinary for validating whether a Binary is valid.
+var nullBinary Binary
+
+// Equal returns whether the binaries are the same length and
+// contain the same bytes.
+func (binary Binary) Equal(equaler contracts.Equaler) bool {
+ b, ok := equaler.(Binary)
+ if !ok {
+ panic("bad Binary type assertion")
+ }
+
+ return bytes.Equal(binary, b)
+}
+
+// Valid returns whether the Binary is valid.
+func (binary Binary) Valid() bool {
+ return !bytes.Equal(binary, nullBinary)
+}
+
+// String returns the hex string representation form of the Binary.
+func (binary Binary) String() string {
+ return hex.EncodeToString(binary)
+}
+
+// MarshalText implements a custom marhsal function to encode
+// the Binary as hex. MarshalText implements the
+// encoding.TextMarshaler interface.
+func (binary Binary) MarshalText() ([]byte, error) {
+ return []byte(binary.String()), nil
+}
+
+// UnmarshalText implements a custom unmarshal function to decode
+// hex into a Binary. UnmarshalText implements the
+// encoding.TextUnmarshaler interface.
+func (binary *Binary) UnmarshalText(text []byte) error {
+ b := make([]byte, hex.DecodedLen(len(text)))
+ _, err := hex.Decode(b, text)
+ if err != nil {
+ return internal.CantDecodeHex(err, string(text))
+ }
+ *binary = b
+
+ return nil
+}
+
+// MarshalJSON implements a custom marshal function to encode the Binary
+// as a hex string. MarshalJSON implements the json.Marshaler interface.
+// Supports JSON null.
+func (binary Binary) MarshalJSON() ([]byte, error) {
+ if !binary.Valid() {
+ return nil, nil
+ }
+
+ return internal.MarshalJSON(binary.String())
+}
+
+// UnmarshalJSON implements a custom unmarshal function to decode
+// a JSON hex string into a Binary. UnmarshalJSON implements the
+// json.Unmarshaler interface. Supports JSON null.
+func (binary *Binary) UnmarshalJSON(data []byte) error {
+ if string(data) == "null" || len(data) == 0 {
+ return nil
+ }
+
+ var s string
+ if err := internal.UnmarshalJSON(data, &s); err != nil {
+ return err
+ }
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ return internal.CantDecodeHex(err, s)
+ }
+ *binary = b
+
+ return nil
+}
+
+// Scan implements the sql.Scanner interface.
+// Supports SQL NULL.
+func (binary *Binary) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case nil:
+ return nil
+
+ case []byte:
+ if len(src) == 0 {
+ return nil
+ }
+
+ b := make([]byte, len(src))
+ copy(b, src)
+ *binary = b
+
+ default:
+ return errors.Errorf("unable to scan type %T into Binary", src)
+ }
+
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+// Supports SQL NULL.
+func (binary Binary) Value() (driver.Value, error) {
+ if !binary.Valid() {
+ return nil, nil
+ }
+
+ return []byte(binary), nil
+}
+
+// Assert interface compliance.
+var (
+ _ contracts.ID = (*Binary)(nil)
+ _ encoding.TextMarshaler = (*Binary)(nil)
+ _ encoding.TextUnmarshaler = (*Binary)(nil)
+ _ json.Marshaler = (*Binary)(nil)
+ _ json.Unmarshaler = (*Binary)(nil)
+ _ sql.Scanner = (*Binary)(nil)
+ _ driver.Valuer = (*Binary)(nil)
+)
diff --git a/pkg/types/bool.go b/pkg/types/bool.go
new file mode 100644
index 0000000..4330264
--- /dev/null
+++ b/pkg/types/bool.go
@@ -0,0 +1,105 @@
+package types
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+ "strconv"
+)
+
+var (
+ enum = map[bool]string{
+ true: "y",
+ false: "n",
+ }
+)
+
+// Bool represents a bool for ENUM ('y', 'n'), which can be NULL.
+type Bool struct {
+ Bool bool
+ Valid bool // Valid is true if Bool is not NULL
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (b Bool) MarshalJSON() ([]byte, error) {
+ if !b.Valid {
+ return nil, nil
+ }
+
+ return internal.MarshalJSON(b.Bool)
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (b *Bool) UnmarshalText(text []byte) error {
+ parsed, err := strconv.ParseUint(string(text), 10, 64)
+ if err != nil {
+ return internal.CantParseUint64(err, string(text))
+ }
+
+ *b = Bool{parsed != 0, true}
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (b *Bool) UnmarshalJSON(data []byte) error {
+ if string(data) == "null" || len(data) == 0 {
+ return nil
+ }
+
+ if err := internal.UnmarshalJSON(data, &b.Bool); err != nil {
+ return err
+ }
+
+ b.Valid = true
+
+ return nil
+}
+
+// Scan implements the sql.Scanner interface.
+// Supports SQL NULL.
+func (b *Bool) Scan(src interface{}) error {
+ if src == nil {
+ b.Bool, b.Valid = false, false
+ return nil
+ }
+
+ v, ok := src.([]byte)
+ if !ok {
+ return errors.Errorf("bad []byte type assertion from %#v", src)
+ }
+
+ switch string(v) {
+ case "y":
+ b.Bool = true
+ case "n":
+ b.Bool = false
+ default:
+ return errors.Errorf("bad bool %#v", v)
+ }
+
+ b.Valid = true
+
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+// Supports SQL NULL.
+func (b Bool) Value() (driver.Value, error) {
+ if !b.Valid {
+ return nil, nil
+ }
+
+ return enum[b.Bool], nil
+}
+
+// Assert interface compliance.
+var (
+ _ json.Marshaler = (*Bool)(nil)
+ _ encoding.TextUnmarshaler = (*Bool)(nil)
+ _ json.Unmarshaler = (*Bool)(nil)
+ _ sql.Scanner = (*Bool)(nil)
+ _ driver.Valuer = (*Bool)(nil)
+)
diff --git a/pkg/types/comment_type.go b/pkg/types/comment_type.go
new file mode 100644
index 0000000..8aed475
--- /dev/null
+++ b/pkg/types/comment_type.go
@@ -0,0 +1,79 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+ "strconv"
+)
+
+// CommentType specifies a comment's origin's kind.
+type CommentType uint8
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (ct *CommentType) UnmarshalJSON(data []byte) error {
+ var i uint8
+ if err := internal.UnmarshalJSON(data, &i); err != nil {
+ return err
+ }
+
+ c := CommentType(i)
+ if _, ok := commentTypes[c]; !ok {
+ return badCommentType(data)
+ }
+
+ *ct = c
+ return nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (ct *CommentType) UnmarshalText(text []byte) error {
+ s := string(text)
+
+ i, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return internal.CantParseUint64(err, s)
+ }
+
+ c := CommentType(i)
+ if uint64(c) != i {
+ // Truncated due to above cast, obviously too high
+ return badCommentType(s)
+ }
+
+ if _, ok := commentTypes[c]; !ok {
+ return badCommentType(s)
+ }
+
+ *ct = c
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (ct CommentType) Value() (driver.Value, error) {
+ if v, ok := commentTypes[ct]; ok {
+ return v, nil
+ } else {
+ return nil, badCommentType(ct)
+ }
+}
+
+// badCommentType returns an error about a syntactically, but not semantically valid CommentType.
+func badCommentType(t interface{}) error {
+ return errors.Errorf("bad comment type: %#v", t)
+}
+
+// commentTypes maps all valid CommentType values to their SQL representation.
+var commentTypes = map[CommentType]string{
+ 1: "comment",
+ 4: "ack",
+}
+
+// Assert interface compliance.
+var (
+ _ json.Unmarshaler = (*CommentType)(nil)
+ _ encoding.TextUnmarshaler = (*CommentType)(nil)
+ _ driver.Valuer = CommentType(0)
+)
diff --git a/pkg/types/float.go b/pkg/types/float.go
new file mode 100644
index 0000000..a4aedd6
--- /dev/null
+++ b/pkg/types/float.go
@@ -0,0 +1,68 @@
+package types
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "strconv"
+)
+
+// Float adds JSON support to sql.NullFloat64.
+type Float struct {
+ sql.NullFloat64
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+// Supports JSON null.
+func (f Float) MarshalJSON() ([]byte, error) {
+ var v interface{}
+ if f.Valid {
+ v = f.Float64
+ }
+
+ return internal.MarshalJSON(v)
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (f *Float) UnmarshalText(text []byte) error {
+ parsed, err := strconv.ParseFloat(string(text), 64)
+ if err != nil {
+ return internal.CantParseFloat64(err, string(text))
+ }
+
+ *f = Float{sql.NullFloat64{
+ Float64: parsed,
+ Valid: true,
+ }}
+
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+// Supports JSON null.
+func (f *Float) UnmarshalJSON(data []byte) error {
+ // Ignore null, like in the main JSON package.
+ if bytes.HasPrefix(data, []byte{'n'}) {
+ return nil
+ }
+
+ if err := internal.UnmarshalJSON(data, &f.Float64); err != nil {
+ return err
+ }
+
+ f.Valid = true
+
+ return nil
+}
+
+// Assert interface compliance.
+var (
+ _ json.Marshaler = Float{}
+ _ encoding.TextUnmarshaler = (*Float)(nil)
+ _ json.Unmarshaler = (*Float)(nil)
+ _ driver.Valuer = Float{}
+ _ sql.Scanner = (*Float)(nil)
+)
diff --git a/pkg/types/int.go b/pkg/types/int.go
new file mode 100644
index 0000000..0e51f21
--- /dev/null
+++ b/pkg/types/int.go
@@ -0,0 +1,68 @@
+package types
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "strconv"
+)
+
+// Int adds JSON support to sql.NullInt64.
+type Int struct {
+ sql.NullInt64
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+// Supports JSON null.
+func (i Int) MarshalJSON() ([]byte, error) {
+ var v interface{}
+ if i.Valid {
+ v = i.Int64
+ }
+
+ return internal.MarshalJSON(v)
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (i *Int) UnmarshalText(text []byte) error {
+ parsed, err := strconv.ParseInt(string(text), 10, 64)
+ if err != nil {
+ return internal.CantParseInt64(err, string(text))
+ }
+
+ *i = Int{sql.NullInt64{
+ Int64: parsed,
+ Valid: true,
+ }}
+
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+// Supports JSON null.
+func (i *Int) UnmarshalJSON(data []byte) error {
+ // Ignore null, like in the main JSON package.
+ if bytes.HasPrefix(data, []byte{'n'}) {
+ return nil
+ }
+
+ if err := internal.UnmarshalJSON(data, &i.Int64); err != nil {
+ return err
+ }
+
+ i.Valid = true
+
+ return nil
+}
+
+// Assert interface compliance.
+var (
+ _ json.Marshaler = Int{}
+ _ json.Unmarshaler = (*Int)(nil)
+ _ encoding.TextUnmarshaler = (*Int)(nil)
+ _ driver.Valuer = Int{}
+ _ sql.Scanner = (*Int)(nil)
+)
diff --git a/pkg/types/notification_states.go b/pkg/types/notification_states.go
new file mode 100644
index 0000000..ff5760a
--- /dev/null
+++ b/pkg/types/notification_states.go
@@ -0,0 +1,78 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+)
+
+// NotificationStates specifies the set of states a notification may be sent for.
+type NotificationStates uint8
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (nst *NotificationStates) UnmarshalJSON(data []byte) error {
+ var states []string
+ if err := internal.UnmarshalJSON(data, &states); err != nil {
+ return err
+ }
+
+ var n NotificationStates
+ for _, state := range states {
+ if v, ok := notificationStateNames[state]; ok {
+ n |= v
+ } else {
+ return badNotificationStates(states)
+ }
+ }
+
+ *nst = n
+ return nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (nst *NotificationStates) UnmarshalText(text []byte) error {
+ return nst.UnmarshalJSON(text)
+}
+
+// Value implements the driver.Valuer interface.
+func (nst NotificationStates) Value() (driver.Value, error) {
+ if nst&^allNotificationStates == 0 {
+ return int64(nst), nil
+ } else {
+ return nil, badNotificationStates(nst)
+ }
+}
+
+// badNotificationStates returns an error about syntactically, but not semantically valid NotificationStates.
+func badNotificationStates(s interface{}) error {
+ return errors.Errorf("bad notification states: %#v", s)
+}
+
+// notificationStateNames maps all valid NotificationStates values to their SQL representation.
+var notificationStateNames = map[string]NotificationStates{
+ "OK": 1,
+ "Warning": 2,
+ "Critical": 4,
+ "Unknown": 8,
+ "Up": 16,
+ "Down": 32,
+}
+
+// allNotificationStates is the largest valid NotificationStates value.
+var allNotificationStates = func() NotificationStates {
+ var nt NotificationStates
+ for _, v := range notificationStateNames {
+ nt |= v
+ }
+
+ return nt
+}()
+
+// Assert interface compliance.
+var (
+ _ json.Unmarshaler = (*NotificationStates)(nil)
+ _ encoding.TextUnmarshaler = (*NotificationStates)(nil)
+ _ driver.Valuer = NotificationStates(0)
+)
diff --git a/pkg/types/notification_type.go b/pkg/types/notification_type.go
new file mode 100644
index 0000000..f2980f4
--- /dev/null
+++ b/pkg/types/notification_type.go
@@ -0,0 +1,68 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+ "strconv"
+)
+
+// NotificationType specifies the reason of a sent notification.
+type NotificationType uint16
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (nt *NotificationType) UnmarshalText(text []byte) error {
+ s := string(text)
+
+ i, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return internal.CantParseUint64(err, s)
+ }
+
+ n := NotificationType(i)
+ if uint64(n) != i {
+ // Truncated due to above cast, obviously too high
+ return badNotificationType(s)
+ }
+
+ if _, ok := notificationTypes[n]; !ok {
+ return badNotificationType(s)
+ }
+
+ *nt = n
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (nt NotificationType) Value() (driver.Value, error) {
+ if v, ok := notificationTypes[nt]; ok {
+ return v, nil
+ } else {
+ return nil, badNotificationType(nt)
+ }
+}
+
+// badNotificationType returns an error about a syntactically, but not semantically valid NotificationType.
+func badNotificationType(t interface{}) error {
+ return errors.Errorf("bad notification type: %#v", t)
+}
+
+// notificationTypes maps all valid NotificationType values to their SQL representation.
+var notificationTypes = map[NotificationType]string{
+ 1: "downtime_start",
+ 2: "downtime_end",
+ 4: "downtime_removed",
+ 8: "custom",
+ 16: "acknowledgement",
+ 32: "problem",
+ 64: "recovery",
+ 128: "flapping_start",
+ 256: "flapping_end",
+}
+
+// Assert interface compliance.
+var (
+ _ encoding.TextUnmarshaler = (*NotificationType)(nil)
+ _ driver.Valuer = NotificationType(0)
+)
diff --git a/pkg/types/notification_types.go b/pkg/types/notification_types.go
new file mode 100644
index 0000000..832a515
--- /dev/null
+++ b/pkg/types/notification_types.go
@@ -0,0 +1,81 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+)
+
+// NotificationTypes specifies the set of reasons a notification may be sent for.
+type NotificationTypes uint16
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (nt *NotificationTypes) UnmarshalJSON(data []byte) error {
+ var types []string
+ if err := internal.UnmarshalJSON(data, &types); err != nil {
+ return err
+ }
+
+ var n NotificationTypes
+ for _, typ := range types {
+ if v, ok := notificationTypeNames[typ]; ok {
+ n |= v
+ } else {
+ return badNotificationTypes(nt)
+ }
+ }
+
+ *nt = n
+ return nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (nt *NotificationTypes) UnmarshalText(text []byte) error {
+ return nt.UnmarshalJSON(text)
+}
+
+// Value implements the driver.Valuer interface.
+func (nt NotificationTypes) Value() (driver.Value, error) {
+ if nt&^allNotificationTypes == 0 {
+ return int64(nt), nil
+ } else {
+ return nil, badNotificationTypes(nt)
+ }
+}
+
+// badNotificationTypes returns an error about syntactically, but not semantically valid NotificationTypes.
+func badNotificationTypes(t interface{}) error {
+ return errors.Errorf("bad notification types: %#v", t)
+}
+
+// notificationTypeNames maps all valid NotificationTypes values to their SQL representation.
+var notificationTypeNames = map[string]NotificationTypes{
+ "DowntimeStart": 1,
+ "DowntimeEnd": 2,
+ "DowntimeRemoved": 4,
+ "Custom": 8,
+ "Acknowledgement": 16,
+ "Problem": 32,
+ "Recovery": 64,
+ "FlappingStart": 128,
+ "FlappingEnd": 256,
+}
+
+// allNotificationTypes is the largest valid NotificationTypes value.
+var allNotificationTypes = func() NotificationTypes {
+ var nt NotificationTypes
+ for _, v := range notificationTypeNames {
+ nt |= v
+ }
+
+ return nt
+}()
+
+// Assert interface compliance.
+var (
+ _ json.Unmarshaler = (*NotificationTypes)(nil)
+ _ encoding.TextUnmarshaler = (*NotificationTypes)(nil)
+ _ driver.Valuer = NotificationTypes(0)
+)
diff --git a/pkg/types/state_type.go b/pkg/types/state_type.go
new file mode 100644
index 0000000..f0cc69a
--- /dev/null
+++ b/pkg/types/state_type.go
@@ -0,0 +1,65 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/pkg/errors"
+)
+
+// StateType specifies a state's hardness.
+type StateType uint8
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (st *StateType) UnmarshalText(text []byte) error {
+ return st.UnmarshalJSON(text)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (st *StateType) UnmarshalJSON(data []byte) error {
+ var i uint8
+ if err := internal.UnmarshalJSON(data, &i); err != nil {
+ return err
+ }
+
+ s := StateType(i)
+ if _, ok := stateTypes[s]; !ok {
+ return badStateType(data)
+ }
+
+ *st = s
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (st StateType) Value() (driver.Value, error) {
+ if v, ok := stateTypes[st]; ok {
+ return v, nil
+ } else {
+ return nil, badStateType(st)
+ }
+}
+
+// badStateType returns and error about a syntactically, but not semantically valid StateType.
+func badStateType(t interface{}) error {
+ return errors.Errorf("bad state type: %#v", t)
+}
+
+const (
+ StateSoft = StateType(0)
+ StateHard = StateType(1)
+)
+
+// stateTypes maps all valid StateType values to their SQL representation.
+var stateTypes = map[StateType]string{
+ StateSoft: "soft",
+ StateHard: "hard",
+}
+
+// Assert interface compliance.
+var (
+ _ encoding.TextUnmarshaler = (*StateType)(nil)
+ _ json.Unmarshaler = (*StateType)(nil)
+ _ driver.Valuer = StateType(0)
+)
diff --git a/pkg/types/string.go b/pkg/types/string.go
new file mode 100644
index 0000000..f8ead45
--- /dev/null
+++ b/pkg/types/string.go
@@ -0,0 +1,74 @@
+package types
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "strings"
+)
+
+// String adds JSON support to sql.NullString.
+type String struct {
+ sql.NullString
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+// Supports JSON null.
+func (s String) MarshalJSON() ([]byte, error) {
+ var v interface{}
+ if s.Valid {
+ v = s.String
+ }
+
+ return internal.MarshalJSON(v)
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (s *String) UnmarshalText(text []byte) error {
+ *s = String{sql.NullString{
+ String: string(text),
+ Valid: true,
+ }}
+
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+// Supports JSON null.
+func (s *String) UnmarshalJSON(data []byte) error {
+ // Ignore null, like in the main JSON package.
+ if bytes.HasPrefix(data, []byte{'n'}) {
+ return nil
+ }
+
+ if err := internal.UnmarshalJSON(data, &s.String); err != nil {
+ return err
+ }
+
+ s.Valid = true
+
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+// Supports SQL NULL.
+func (s String) Value() (driver.Value, error) {
+ if !s.Valid {
+ return nil, nil
+ }
+
+ // PostgreSQL does not allow null bytes in varchar, char and text fields.
+ return strings.ReplaceAll(s.String, "\x00", ""), nil
+}
+
+// Assert interface compliance.
+var (
+ _ json.Marshaler = String{}
+ _ encoding.TextUnmarshaler = (*String)(nil)
+ _ json.Unmarshaler = (*String)(nil)
+ _ driver.Valuer = String{}
+ _ sql.Scanner = (*String)(nil)
+)
diff --git a/pkg/types/unix_milli.go b/pkg/types/unix_milli.go
new file mode 100644
index 0000000..203cdf6
--- /dev/null
+++ b/pkg/types/unix_milli.go
@@ -0,0 +1,95 @@
+package types
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "encoding"
+ "encoding/json"
+ "github.com/icinga/icingadb/internal"
+ "github.com/icinga/icingadb/pkg/utils"
+ "github.com/pkg/errors"
+ "strconv"
+ "time"
+)
+
+// UnixMilli is a nullable millisecond UNIX timestamp in databases and JSON.
+type UnixMilli time.Time
+
+// Time returns the time.Time conversion of UnixMilli.
+func (t UnixMilli) Time() time.Time {
+ return time.Time(t)
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+// Marshals to milliseconds. Supports JSON null.
+func (t UnixMilli) MarshalJSON() ([]byte, error) {
+ if time.Time(t).IsZero() {
+ return nil, nil
+ }
+
+ return []byte(strconv.FormatInt(time.Time(t).UnixMilli(), 10)), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (t *UnixMilli) UnmarshalText(text []byte) error {
+ parsed, err := strconv.ParseFloat(string(text), 64)
+ if err != nil {
+ return internal.CantParseFloat64(err, string(text))
+ }
+
+ *t = UnixMilli(utils.FromUnixMilli(int64(parsed)))
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+// Unmarshals from milliseconds. Supports JSON null.
+func (t *UnixMilli) UnmarshalJSON(data []byte) error {
+ if string(data) == "null" || len(data) == 0 {
+ return nil
+ }
+
+ ms, err := strconv.ParseFloat(string(data), 64)
+ if err != nil {
+ return internal.CantParseFloat64(err, string(data))
+ }
+ tt := utils.FromUnixMilli(int64(ms))
+ *t = UnixMilli(tt)
+
+ return nil
+}
+
+// Scan implements the sql.Scanner interface.
+// Scans from milliseconds. Supports SQL NULL.
+func (t *UnixMilli) Scan(src interface{}) error {
+ if src == nil {
+ return nil
+ }
+
+ v, ok := src.(int64)
+ if !ok {
+ return errors.Errorf("bad int64 type assertion from %#v", src)
+ }
+ tt := utils.FromUnixMilli(v)
+ *t = UnixMilli(tt)
+
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+// Returns milliseconds. Supports SQL NULL.
+func (t UnixMilli) Value() (driver.Value, error) {
+ if t.Time().IsZero() {
+ return nil, nil
+ }
+
+ return t.Time().UnixMilli(), nil
+}
+
+// Assert interface compliance.
+var (
+ _ json.Marshaler = (*UnixMilli)(nil)
+ _ encoding.TextUnmarshaler = (*UnixMilli)(nil)
+ _ json.Unmarshaler = (*UnixMilli)(nil)
+ _ sql.Scanner = (*UnixMilli)(nil)
+ _ driver.Valuer = (*UnixMilli)(nil)
+)
diff --git a/pkg/types/uuid.go b/pkg/types/uuid.go
new file mode 100644
index 0000000..02acbcd
--- /dev/null
+++ b/pkg/types/uuid.go
@@ -0,0 +1,24 @@
+package types
+
+import (
+ "database/sql/driver"
+ "encoding"
+ "github.com/google/uuid"
+)
+
+// UUID is like uuid.UUID, but marshals itself binarily (not like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) in SQL context.
+type UUID struct {
+ uuid.UUID
+}
+
+// Value implements driver.Valuer.
+func (uuid UUID) Value() (driver.Value, error) {
+ return uuid.UUID[:], nil
+}
+
+// Assert interface compliance.
+var (
+ _ encoding.TextUnmarshaler = (*UUID)(nil)
+ _ driver.Valuer = UUID{}
+ _ driver.Valuer = (*UUID)(nil)
+)