github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/json/noms_json_value.go (about)

     1  // Copyright 2021 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package json
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"strings"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  
    24  	"github.com/dolthub/dolt/go/store/types"
    25  )
    26  
    27  var ErrUnexpectedJSONTypeIn = errors.New("unexpected type during JSON marshalling")
    28  var ErrUnexpectedJSONTypeOut = errors.New("unexpected type during JSON unmarshalling")
    29  
    30  const (
    31  	JSONNull = "null"
    32  )
    33  
    34  // NomsJSON is a type alias for types.JSON. The alias allows MySQL-specific
    35  // logic to be kept separate from the storage-layer code in pkg types.
    36  type NomsJSON types.JSON
    37  
    38  var _ sql.JSONValue = NomsJSON{}
    39  
    40  // NomsJSONFromJSONValue converts a sql.JSONValue to a NomsJSON value.
    41  func NomsJSONFromJSONValue(ctx context.Context, vrw types.ValueReadWriter, val sql.JSONValue) (NomsJSON, error) {
    42  	if noms, ok := val.(NomsJSON); ok {
    43  		return noms, nil
    44  	}
    45  
    46  	sqlDoc, err := val.Unmarshall(sql.NewContext(ctx))
    47  	if err != nil {
    48  		return NomsJSON{}, err
    49  	}
    50  
    51  	v, err := marshalJSON(ctx, vrw, sqlDoc.Val)
    52  	if err != nil {
    53  		return NomsJSON{}, err
    54  	}
    55  
    56  	doc, err := types.NewJSONDoc(vrw.Format(), vrw, v)
    57  	if err != nil {
    58  		return NomsJSON{}, err
    59  	}
    60  
    61  	return NomsJSON(doc), nil
    62  }
    63  
    64  func marshalJSON(ctx context.Context, vrw types.ValueReadWriter, val interface{}) (types.Value, error) {
    65  	if val == nil {
    66  		return types.NullValue, nil
    67  	}
    68  
    69  	switch val := val.(type) {
    70  	case []interface{}:
    71  		return marshalJSONArray(ctx, vrw, val)
    72  	case map[string]interface{}:
    73  		return marshalJSONObject(ctx, vrw, val)
    74  	case bool:
    75  		return types.Bool(val), nil
    76  	case string:
    77  		return types.String(val), nil
    78  	case float64:
    79  		return types.Float(val), nil
    80  
    81  	// TODO(andy): unclear how to handle these
    82  	case float32:
    83  		return types.Float(val), nil
    84  	case int:
    85  		return types.Float(val), nil
    86  	case int8:
    87  		return types.Float(val), nil
    88  	case int16:
    89  		return types.Float(val), nil
    90  	case int32:
    91  		return types.Float(val), nil
    92  	case int64:
    93  		return types.Float(val), nil
    94  	case uint:
    95  		return types.Float(val), nil
    96  	case uint8:
    97  		return types.Float(val), nil
    98  	case uint16:
    99  		return types.Float(val), nil
   100  	case uint32:
   101  		return types.Float(val), nil
   102  	case uint64:
   103  		return types.Float(val), nil
   104  	default:
   105  		return nil, ErrUnexpectedJSONTypeIn
   106  	}
   107  }
   108  
   109  func marshalJSONArray(ctx context.Context, vrw types.ValueReadWriter, arr []interface{}) (types.Value, error) {
   110  	var err error
   111  	vals := make([]types.Value, len(arr))
   112  	for i, elem := range arr {
   113  		vals[i], err = marshalJSON(ctx, vrw, elem)
   114  		if err != nil {
   115  			return nil, err
   116  		}
   117  	}
   118  	return types.NewList(ctx, vrw, vals...)
   119  }
   120  
   121  func marshalJSONObject(ctx context.Context, vrw types.ValueReadWriter, obj map[string]interface{}) (types.Value, error) {
   122  	var err error
   123  	i := 0
   124  	vals := make([]types.Value, len(obj)*2)
   125  	for k, v := range obj {
   126  		vals[i] = types.String(k)
   127  		vals[i+1], err = marshalJSON(ctx, vrw, v)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  		i += 2
   132  	}
   133  	return types.NewMap(ctx, vrw, vals...)
   134  }
   135  
   136  // Unmarshall implements the sql.JSONValue interface.
   137  func (v NomsJSON) Unmarshall(ctx *sql.Context) (doc sql.JSONDocument, err error) {
   138  	nomsVal, err := types.JSON(v).Inner()
   139  	if err != nil {
   140  		return sql.JSONDocument{}, err
   141  	}
   142  
   143  	val, err := unmarshalJSON(ctx, nomsVal)
   144  	if err != nil {
   145  		return sql.JSONDocument{}, err
   146  	}
   147  
   148  	return sql.JSONDocument{Val: val}, nil
   149  }
   150  
   151  func unmarshalJSON(ctx context.Context, val types.Value) (interface{}, error) {
   152  	switch val := val.(type) {
   153  	case types.Null:
   154  		return nil, nil
   155  	case types.Bool:
   156  		return bool(val), nil
   157  	case types.String:
   158  		return string(val), nil
   159  	case types.Float:
   160  		return float64(val), nil
   161  	case types.List:
   162  		return unmarshalJSONArray(ctx, val)
   163  	case types.Map:
   164  		return unmarshalJSONObject(ctx, val)
   165  	default:
   166  		return nil, ErrUnexpectedJSONTypeIn
   167  	}
   168  }
   169  
   170  func unmarshalJSONArray(ctx context.Context, l types.List) (arr []interface{}, err error) {
   171  	arr = make([]interface{}, l.Len())
   172  	err = l.Iter(ctx, func(v types.Value, index uint64) (stop bool, err error) {
   173  		arr[index], err = unmarshalJSON(ctx, v)
   174  		return
   175  	})
   176  	return
   177  }
   178  
   179  func unmarshalJSONObject(ctx context.Context, m types.Map) (obj map[string]interface{}, err error) {
   180  	obj = make(map[string]interface{}, m.Len())
   181  	err = m.Iter(ctx, func(key, value types.Value) (stop bool, err error) {
   182  		ks, ok := key.(types.String)
   183  		if !ok {
   184  			return false, ErrUnexpectedJSONTypeOut
   185  		}
   186  
   187  		obj[string(ks)], err = unmarshalJSON(ctx, value)
   188  		return
   189  	})
   190  	return
   191  }
   192  
   193  // Compare implements the sql.JSONValue interface.
   194  func (v NomsJSON) Compare(ctx *sql.Context, other sql.JSONValue) (cmp int, err error) {
   195  	noms, ok := other.(NomsJSON)
   196  	if !ok {
   197  		doc, err := v.Unmarshall(ctx)
   198  		if err != nil {
   199  			return 0, err
   200  		}
   201  		return doc.Compare(ctx, other)
   202  	}
   203  
   204  	return types.JSON(v).Compare(types.JSON(noms))
   205  }
   206  
   207  // ToString implements the sql.JSONValue interface.
   208  func (v NomsJSON) ToString(ctx *sql.Context) (string, error) {
   209  	jd, err := types.JSON(v).Inner()
   210  	if err != nil {
   211  		return "", err
   212  	}
   213  
   214  	sb := &strings.Builder{}
   215  	if err = marshalToString(ctx, sb, jd); err != nil {
   216  		return "", err
   217  	}
   218  
   219  	return sb.String(), nil
   220  }
   221  
   222  func marshalToString(ctx context.Context, sb *strings.Builder, val types.Value) (err error) {
   223  	switch val := val.(type) {
   224  	case types.Null:
   225  		sb.WriteString(JSONNull)
   226  
   227  	case types.Bool:
   228  		sb.WriteString(val.HumanReadableString())
   229  
   230  	case types.String:
   231  		sb.WriteString(val.HumanReadableString())
   232  
   233  	case types.Float:
   234  		sb.WriteString(val.HumanReadableString())
   235  
   236  	case types.List:
   237  		sb.WriteRune('[')
   238  		seenOne := false
   239  		err = val.Iter(ctx, func(v types.Value, _ uint64) (stop bool, err error) {
   240  			if seenOne {
   241  				sb.WriteString(", ")
   242  			}
   243  			seenOne = true
   244  			err = marshalToString(ctx, sb, v)
   245  			return
   246  		})
   247  		if err != nil {
   248  			return err
   249  		}
   250  		sb.WriteRune(']')
   251  
   252  	case types.Map:
   253  		sb.WriteRune('{')
   254  		seenOne := false
   255  		err = val.Iter(ctx, func(k, v types.Value) (stop bool, err error) {
   256  			if seenOne {
   257  				sb.WriteString(", ")
   258  			}
   259  			seenOne = true
   260  
   261  			sb.WriteString(k.HumanReadableString())
   262  			sb.WriteString(": ")
   263  
   264  			err = marshalToString(ctx, sb, v)
   265  			return
   266  		})
   267  		if err != nil {
   268  			return err
   269  		}
   270  		sb.WriteRune('}')
   271  
   272  	default:
   273  		err = ErrUnexpectedJSONTypeOut
   274  	}
   275  	return
   276  }