func DeleteDevice(ctx context.Context, db sqlx.Execer, devEUI lorawan.EUI64) error {
	res, err := db.Exec("delete from device where dev_eui = $1", devEUI[:])
	if err != nil {
		return handlePSQLError(err, "delete error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return handlePSQLError(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}
	log.WithFields(log.Fields{
		"dev_eui": devEUI,
		"ctx_id":  ctx.Value(logging.ContextIDKey),
	}).Info("device deleted")
	return nil
}
DeleteDevice deletes the device matching the given DevEUI.
package storage
import (
	"context"
	"time"
	"github.com/gofrs/uuid"
	"github.com/jmoiron/sqlx"
	log "github.com/sirupsen/logrus"
	"github.com/brocaar/chirpstack-network-server/v3/internal/logging"
	"github.com/brocaar/lorawan"
)
type DeviceMode string
const (
	DeviceModeA DeviceMode = "A"
	DeviceModeB DeviceMode = "B"
	DeviceModeC DeviceMode = "C"
)
type Device struct {
	DevEUI            lorawan.EUI64 `db:"dev_eui"`
	CreatedAt         time.Time     `db:"created_at"`
	UpdatedAt         time.Time     `db:"updated_at"`
	DeviceProfileID   uuid.UUID     `db:"device_profile_id"`
	ServiceProfileID  uuid.UUID     `db:"service_profile_id"`
	RoutingProfileID  uuid.UUID     `db:"routing_profile_id"`
	SkipFCntCheck     bool          `db:"skip_fcnt_check"`
	ReferenceAltitude float64       `db:"reference_altitude"`
	Mode              DeviceMode    `db:"mode"`
	IsDisabled        bool          `db:"is_disabled"`
}
type DeviceActivation struct {
	ID          int64             `db:"id"`
	CreatedAt   time.Time         `db:"created_at"`
	DevEUI      lorawan.EUI64     `db:"dev_eui"`
	JoinEUI     lorawan.EUI64     `db:"join_eui"`
	DevAddr     lorawan.DevAddr   `db:"dev_addr"`
	FNwkSIntKey lorawan.AES128Key `db:"f_nwk_s_int_key"`
	SNwkSIntKey lorawan.AES128Key `db:"s_nwk_s_int_key"`
	NwkSEncKey  lorawan.AES128Key `db:"nwk_s_enc_key"`
	DevNonce    lorawan.DevNonce  `db:"dev_nonce"`
	JoinReqType lorawan.JoinType  `db:"join_req_type"`
}
func CreateDevice(ctx context.Context, db sqlx.Execer, d *Device) error {
	now := time.Now()
	d.CreatedAt = now
	d.UpdatedAt = now
	_, err := db.Exec(`
		insert into device (
			dev_eui,
			created_at,
			updated_at,
			device_profile_id,
			service_profile_id,
			routing_profile_id,
			skip_fcnt_check,
			reference_altitude,
			mode,
			is_disabled
		) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
		d.DevEUI[:],
		d.CreatedAt,
		d.UpdatedAt,
		d.DeviceProfileID,
		d.ServiceProfileID,
		d.RoutingProfileID,
		d.SkipFCntCheck,
		d.ReferenceAltitude,
		d.Mode,
		d.IsDisabled,
	)
	if err != nil {
		return handlePSQLError(err, "insert error")
	}
	log.WithFields(log.Fields{
		"dev_eui": d.DevEUI,
		"ctx_id":  ctx.Value(logging.ContextIDKey),
	}).Info("device created")
	return nil
}
func GetDevice(ctx context.Context, db sqlx.Queryer, devEUI lorawan.EUI64, forUpdate bool) (Device, error) {
	var d Device
	var fu string
	if forUpdate {
		fu = " for update"
	}
	err := sqlx.Get(db, &d, "select * from device where dev_eui = $1"+fu, devEUI[:])
	if err != nil {
		return d, handlePSQLError(err, "select error")
	}
	return d, nil
}
func UpdateDevice(ctx context.Context, db sqlx.Execer, d *Device) error {
	d.UpdatedAt = time.Now()
	res, err := db.Exec(`
		update device set
			updated_at = $2,
			device_profile_id = $3,
			service_profile_id = $4,
			routing_profile_id = $5,
			skip_fcnt_check = $6,
			reference_altitude = $7,
			mode = $8,
			is_disabled = $9
		where
			dev_eui = $1`,
		d.DevEUI[:],
		d.UpdatedAt,
		d.DeviceProfileID,
		d.ServiceProfileID,
		d.RoutingProfileID,
		d.SkipFCntCheck,
		d.ReferenceAltitude,
		d.Mode,
		d.IsDisabled,
	)
	if err != nil {
		return handlePSQLError(err, "update error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return handlePSQLError(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}
	log.WithFields(log.Fields{
		"dev_eui": d.DevEUI,
		"ctx_id":  ctx.Value(logging.ContextIDKey),
	}).Info("device updated")
	return nil
}