...

Source file src/github.com/jackc/pgx/v5/pgtype/bool.go

Documentation: github.com/jackc/pgx/v5/pgtype

     1  package pgtype
     2  
     3  import (
     4  	"bytes"
     5  	"database/sql/driver"
     6  	"encoding/json"
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  type BoolScanner interface {
    13  	ScanBool(v Bool) error
    14  }
    15  
    16  type BoolValuer interface {
    17  	BoolValue() (Bool, error)
    18  }
    19  
    20  type Bool struct {
    21  	Bool  bool
    22  	Valid bool
    23  }
    24  
    25  func (b *Bool) ScanBool(v Bool) error {
    26  	*b = v
    27  	return nil
    28  }
    29  
    30  func (b Bool) BoolValue() (Bool, error) {
    31  	return b, nil
    32  }
    33  
    34  // Scan implements the database/sql Scanner interface.
    35  func (dst *Bool) Scan(src any) error {
    36  	if src == nil {
    37  		*dst = Bool{}
    38  		return nil
    39  	}
    40  
    41  	switch src := src.(type) {
    42  	case bool:
    43  		*dst = Bool{Bool: src, Valid: true}
    44  		return nil
    45  	case string:
    46  		b, err := strconv.ParseBool(src)
    47  		if err != nil {
    48  			return err
    49  		}
    50  		*dst = Bool{Bool: b, Valid: true}
    51  		return nil
    52  	case []byte:
    53  		b, err := strconv.ParseBool(string(src))
    54  		if err != nil {
    55  			return err
    56  		}
    57  		*dst = Bool{Bool: b, Valid: true}
    58  		return nil
    59  	}
    60  
    61  	return fmt.Errorf("cannot scan %T", src)
    62  }
    63  
    64  // Value implements the database/sql/driver Valuer interface.
    65  func (src Bool) Value() (driver.Value, error) {
    66  	if !src.Valid {
    67  		return nil, nil
    68  	}
    69  
    70  	return src.Bool, nil
    71  }
    72  
    73  func (src Bool) MarshalJSON() ([]byte, error) {
    74  	if !src.Valid {
    75  		return []byte("null"), nil
    76  	}
    77  
    78  	if src.Bool {
    79  		return []byte("true"), nil
    80  	} else {
    81  		return []byte("false"), nil
    82  	}
    83  }
    84  
    85  func (dst *Bool) UnmarshalJSON(b []byte) error {
    86  	var v *bool
    87  	err := json.Unmarshal(b, &v)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	if v == nil {
    93  		*dst = Bool{}
    94  	} else {
    95  		*dst = Bool{Bool: *v, Valid: true}
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  type BoolCodec struct{}
   102  
   103  func (BoolCodec) FormatSupported(format int16) bool {
   104  	return format == TextFormatCode || format == BinaryFormatCode
   105  }
   106  
   107  func (BoolCodec) PreferredFormat() int16 {
   108  	return BinaryFormatCode
   109  }
   110  
   111  func (BoolCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
   112  	switch format {
   113  	case BinaryFormatCode:
   114  		switch value.(type) {
   115  		case bool:
   116  			return encodePlanBoolCodecBinaryBool{}
   117  		case BoolValuer:
   118  			return encodePlanBoolCodecBinaryBoolValuer{}
   119  		}
   120  	case TextFormatCode:
   121  		switch value.(type) {
   122  		case bool:
   123  			return encodePlanBoolCodecTextBool{}
   124  		case BoolValuer:
   125  			return encodePlanBoolCodecTextBoolValuer{}
   126  		}
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  type encodePlanBoolCodecBinaryBool struct{}
   133  
   134  func (encodePlanBoolCodecBinaryBool) Encode(value any, buf []byte) (newBuf []byte, err error) {
   135  	v := value.(bool)
   136  
   137  	if v {
   138  		buf = append(buf, 1)
   139  	} else {
   140  		buf = append(buf, 0)
   141  	}
   142  
   143  	return buf, nil
   144  }
   145  
   146  type encodePlanBoolCodecTextBoolValuer struct{}
   147  
   148  func (encodePlanBoolCodecTextBoolValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
   149  	b, err := value.(BoolValuer).BoolValue()
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	if !b.Valid {
   155  		return nil, nil
   156  	}
   157  
   158  	if b.Bool {
   159  		buf = append(buf, 't')
   160  	} else {
   161  		buf = append(buf, 'f')
   162  	}
   163  
   164  	return buf, nil
   165  }
   166  
   167  type encodePlanBoolCodecBinaryBoolValuer struct{}
   168  
   169  func (encodePlanBoolCodecBinaryBoolValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
   170  	b, err := value.(BoolValuer).BoolValue()
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	if !b.Valid {
   176  		return nil, nil
   177  	}
   178  
   179  	if b.Bool {
   180  		buf = append(buf, 1)
   181  	} else {
   182  		buf = append(buf, 0)
   183  	}
   184  
   185  	return buf, nil
   186  }
   187  
   188  type encodePlanBoolCodecTextBool struct{}
   189  
   190  func (encodePlanBoolCodecTextBool) Encode(value any, buf []byte) (newBuf []byte, err error) {
   191  	v := value.(bool)
   192  
   193  	if v {
   194  		buf = append(buf, 't')
   195  	} else {
   196  		buf = append(buf, 'f')
   197  	}
   198  
   199  	return buf, nil
   200  }
   201  
   202  func (BoolCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
   203  
   204  	switch format {
   205  	case BinaryFormatCode:
   206  		switch target.(type) {
   207  		case *bool:
   208  			return scanPlanBinaryBoolToBool{}
   209  		case BoolScanner:
   210  			return scanPlanBinaryBoolToBoolScanner{}
   211  		}
   212  	case TextFormatCode:
   213  		switch target.(type) {
   214  		case *bool:
   215  			return scanPlanTextAnyToBool{}
   216  		case BoolScanner:
   217  			return scanPlanTextAnyToBoolScanner{}
   218  		}
   219  	}
   220  
   221  	return nil
   222  }
   223  
   224  func (c BoolCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
   225  	return c.DecodeValue(m, oid, format, src)
   226  }
   227  
   228  func (c BoolCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
   229  	if src == nil {
   230  		return nil, nil
   231  	}
   232  
   233  	var b bool
   234  	err := codecScan(c, m, oid, format, src, &b)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	return b, nil
   239  }
   240  
   241  type scanPlanBinaryBoolToBool struct{}
   242  
   243  func (scanPlanBinaryBoolToBool) Scan(src []byte, dst any) error {
   244  	if src == nil {
   245  		return fmt.Errorf("cannot scan NULL into %T", dst)
   246  	}
   247  
   248  	if len(src) != 1 {
   249  		return fmt.Errorf("invalid length for bool: %v", len(src))
   250  	}
   251  
   252  	p, ok := (dst).(*bool)
   253  	if !ok {
   254  		return ErrScanTargetTypeChanged
   255  	}
   256  
   257  	*p = src[0] == 1
   258  
   259  	return nil
   260  }
   261  
   262  type scanPlanTextAnyToBool struct{}
   263  
   264  func (scanPlanTextAnyToBool) Scan(src []byte, dst any) error {
   265  	if src == nil {
   266  		return fmt.Errorf("cannot scan NULL into %T", dst)
   267  	}
   268  
   269  	if len(src) == 0 {
   270  		return fmt.Errorf("cannot scan empty string into %T", dst)
   271  	}
   272  
   273  	p, ok := (dst).(*bool)
   274  	if !ok {
   275  		return ErrScanTargetTypeChanged
   276  	}
   277  
   278  	v, err := planTextToBool(src)
   279  	if err != nil {
   280  		return err
   281  	}
   282  
   283  	*p = v
   284  
   285  	return nil
   286  }
   287  
   288  type scanPlanBinaryBoolToBoolScanner struct{}
   289  
   290  func (scanPlanBinaryBoolToBoolScanner) Scan(src []byte, dst any) error {
   291  	s, ok := (dst).(BoolScanner)
   292  	if !ok {
   293  		return ErrScanTargetTypeChanged
   294  	}
   295  
   296  	if src == nil {
   297  		return s.ScanBool(Bool{})
   298  	}
   299  
   300  	if len(src) != 1 {
   301  		return fmt.Errorf("invalid length for bool: %v", len(src))
   302  	}
   303  
   304  	return s.ScanBool(Bool{Bool: src[0] == 1, Valid: true})
   305  }
   306  
   307  type scanPlanTextAnyToBoolScanner struct{}
   308  
   309  func (scanPlanTextAnyToBoolScanner) Scan(src []byte, dst any) error {
   310  	s, ok := (dst).(BoolScanner)
   311  	if !ok {
   312  		return ErrScanTargetTypeChanged
   313  	}
   314  
   315  	if src == nil {
   316  		return s.ScanBool(Bool{})
   317  	}
   318  
   319  	if len(src) == 0 {
   320  		return fmt.Errorf("cannot scan empty string into %T", dst)
   321  	}
   322  
   323  	v, err := planTextToBool(src)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	return s.ScanBool(Bool{Bool: v, Valid: true})
   329  }
   330  
   331  // https://www.postgresql.org/docs/11/datatype-boolean.html
   332  func planTextToBool(src []byte) (bool, error) {
   333  	s := string(bytes.ToLower(bytes.TrimSpace(src)))
   334  
   335  	switch {
   336  	case strings.HasPrefix("true", s), strings.HasPrefix("yes", s), s == "on", s == "1":
   337  		return true, nil
   338  	case strings.HasPrefix("false", s), strings.HasPrefix("no", s), strings.HasPrefix("off", s), s == "0":
   339  		return false, nil
   340  	default:
   341  		return false, fmt.Errorf("unknown boolean string representation %q", src)
   342  	}
   343  }
   344  

View as plain text