Files
go/lib/column/object_json.go
zeekay 0c90ebe02e debrand: finish the rename across source, docs and dev infra
Source: the unexported clickhouse struct becomes client, the ch receiver and
chproto import alias follow, error message prefixes read datastore, and the
JSON serializer interface drops the vendor infix. resources' metadata key and
its accessor are renamed together, with a test proving the embedded YAML still
unmarshals — a silent drift there would have made every connection take the
unsupported-server-version branch.

Codegen: all three templates still emitted the upstream module paths, so
regenerating would have quietly undone the rename. Note the templates have
drifted from the files they produce (regenerating today drops QBit and
scanTypeDuration), so the generated files are left as tracked and only the
templates are corrected.

Dead links: an earlier mechanical pass invented URLs that do not resolve —
github.com/ClickHouse/datastore-go, hanzo-ds/driver issue numbers that belong to
upstream, and mangled names of independent projects (mailru, uptrace, nikepan,
EpicStep). Third-party names are not ours to rewrite, so those directories are
removed rather than repaired, and provenance comments now point at real files.

Dev infra: compose runs our own server image at the tag production runs, since
the vendor's alpine tag does not exist in our registry. Cluster hostnames,
nginx upstreams and server configs are renamed as one set.

CHANGELOG.md is upstream's release history for versions this module never
released; rewriting it would fabricate a lineage, so it is dropped and the
upstream base recorded in README instead.

The two long-standing vet findings are fixed by keying the struct literals
rather than suppressed. LICENSE and contributors/list keep the vendor name on
purpose: one is the copyright notice Apache-2.0 requires, the other is real
people's names. CI now enforces build, vet, test, gofmt and a grep gate.
2026-07-26 11:46:39 -07:00

907 lines
27 KiB
Go

package column
//
// import (
// "fmt"
// "github.com/hanzo-ds/native/proto"
// "reflect"
// "strings"
// )
//
// // This JSON type implementation was done for an experimental Object('JSON') type:
// // https://docs.hanzo.ai/datastore/sql-reference/data-types/object-data-type
// // It's already deprecated in Datastore and will be removed in the future.
// // Since Datastore 24.8, the Object('JSON') type is no longer alias for JSON type.
// // The new JSON type has been introduced: https://docs.hanzo.ai/datastore/sql-reference/data-types/newjson
// // However, the new JSON type is not supported by the driver yet.
// //
// // This implementation is kept for backward compatibility and will be removed in the future. TODO: remove this
//
// // inverse mapping - go types to datastore types
// var kindMappings = map[reflect.Kind]string{
// reflect.String: "String",
// reflect.Int: "Int64",
// reflect.Int8: "Int8",
// reflect.Int16: "Int16",
// reflect.Int32: "Int32",
// reflect.Int64: "Int64",
// reflect.Uint: "UInt64",
// reflect.Uint8: "UInt8",
// reflect.Uint16: "UInt16",
// reflect.Uint32: "UInt32",
// reflect.Uint64: "UInt64",
// reflect.Float32: "Float32",
// reflect.Float64: "Float64",
// reflect.Bool: "Bool",
// }
//
// // complex types for which a mapping exists - currently we map to String but could enhance in the future for other types
// var typeMappings = map[string]struct{}{
// // currently JSON doesn't support DateTime, Decimal or IP so mapped to String
// "time.Time": {},
// "decimal.Decimal": {},
// "net.IP": {},
// "uuid.UUID": {},
// }
//
// type ObjectJSON interface {
// Interface
// appendEmptyValue() error
// }
//
// type JSONParent interface {
// upsertValue(name string, ct string) (*JSONValue, error)
// upsertList(name string) (*JSONList, error)
// upsertObject(name string) (*JSONObject, error)
// insertEmptyColumn(name string) error
// columnNames() []string
// rows() int
// }
//
// func parseType(name string, vType reflect.Type, values any, isArray bool, jCol JSONParent, numEmpty int) error {
// _, ok := typeMappings[vType.String()]
// if !ok {
// return &UnsupportedColumnTypeError{
// t: Type(vType.String()),
// }
// }
// ct := "String"
// if isArray {
// ct = fmt.Sprintf("Array(%s)", ct)
// }
// col, err := jCol.upsertValue(name, ct)
// if err != nil {
// return err
// }
// col.origType = vType
//
// //pre pad with empty - e.g. for new values in maps
// for i := 0; i < numEmpty; i++ {
// if isArray {
// // empty array for nil of the right type
// err = col.AppendRow([]string{})
// } else {
// // empty value of the type
// err = col.AppendRow(fmt.Sprint(reflect.New(vType).Elem().Interface()))
// }
// if err != nil {
// return err
// }
// }
// if isArray {
// iValues := reflect.ValueOf(values)
// sValues := make([]string, iValues.Len(), iValues.Len())
// for i := 0; i < iValues.Len(); i++ {
// sValues[i] = fmt.Sprint(iValues.Index(i).Interface())
// }
// return col.AppendRow(sValues)
// }
// return col.AppendRow(fmt.Sprint(values))
// }
//
// func parsePrimitive(name string, kind reflect.Kind, values any, isArray bool, jCol JSONParent, numEmpty int) error {
// ct, ok := kindMappings[kind]
// if !ok {
// return &UnsupportedColumnTypeError{
// t: Type(fmt.Sprintf("%s - %s", kind, reflect.TypeOf(values).String())),
// }
// }
// var err error
// if isArray {
// ct = fmt.Sprintf("Array(%s)", ct)
// // if we have a []any we will need to cast to the target column type - this will be based on the first
// // values types. Inconsistent slices will fail.
// values, err = convertSlice(values)
// if err != nil {
// return err
// }
// }
// col, err := jCol.upsertValue(name, ct)
// if err != nil {
// return err
// }
//
// //pre pad with empty - e.g. for new values in maps
// for i := 0; i < numEmpty; i++ {
// if isArray {
// // empty array for nil of the right type
// err = col.AppendRow(reflect.MakeSlice(reflect.TypeOf(values), 0, 0).Interface())
// } else {
// err = col.AppendRow(nil)
// }
// if err != nil {
// return err
// }
// }
//
// return col.AppendRow(values)
// }
//
// // converts a []any of primitives to a typed slice
// // maybe this can be done with reflection but likely slower. investigate.
// // this uses the first value to determine the type - subsequent values must currently be of the same type - we might cast later
// // but wider driver doesn't support e.g. int to int64
// func convertSlice(values any) (any, error) {
// rValues := reflect.ValueOf(values)
// if rValues.Len() == 0 || rValues.Index(0).Kind() != reflect.Interface {
// return values, nil
// }
// var fType reflect.Type
// for i := 0; i < rValues.Len(); i++ {
// elem := rValues.Index(i).Elem()
// if elem.IsValid() {
// fType = elem.Type()
// break
// }
// }
// if fType == nil {
// return []any{}, nil
// }
// typedSlice := reflect.MakeSlice(reflect.SliceOf(fType), 0, rValues.Len())
// for i := 0; i < rValues.Len(); i++ {
// value := rValues.Index(i)
// if value.IsNil() {
// typedSlice = reflect.Append(typedSlice, reflect.Zero(fType))
// continue
// }
// if rValues.Index(i).Elem().Type() != fType {
// return nil, &Error{
// ColumnType: fmt.Sprint(fType),
// Err: fmt.Errorf("inconsistent slices are not supported - expected %s got %s", fType, rValues.Index(i).Elem().Type()),
// }
// }
// typedSlice = reflect.Append(typedSlice, rValues.Index(i).Elem())
// }
// return typedSlice.Interface(), nil
// }
//
// func (jCol *JSONList) createNewOffsets(num int) {
// for i := 0; i < num; i++ {
// //single depth so can take 1st
// if jCol.offsets[0].values.col.Rows() == 0 {
// // first entry in the column
// jCol.offsets[0].values.col.Append(0)
// } else {
// // entry for this object to see offset from last - offsets are cumulative
// jCol.offsets[0].values.col.Append(jCol.offsets[0].values.col.Row(jCol.offsets[0].values.col.Rows() - 1))
// }
// }
// }
//
// func parseSlice(name string, values any, jCol JSONParent, preFill int) error {
// fType := reflect.TypeOf(values).Elem()
// sKind := fType.Kind()
// rValues := reflect.ValueOf(values)
//
// if sKind == reflect.Interface {
// //use the first element to determine if it is a complex or primitive map - after this we need consistent dimensions
// if rValues.Len() == 0 {
// return nil
// }
// var value reflect.Value
// for i := 0; i < rValues.Len(); i++ {
// value = rValues.Index(i).Elem()
// if value.IsValid() {
// break
// }
// }
// if !value.IsValid() {
// return nil
// }
// fType = value.Type()
// sKind = value.Kind()
// }
//
// if _, ok := typeMappings[fType.String()]; ok {
// return parseType(name, fType, values, true, jCol, preFill)
// } else if sKind == reflect.Struct || sKind == reflect.Map || sKind == reflect.Slice {
// if rValues.Len() == 0 {
// return nil
// }
// col, err := jCol.upsertList(name)
// if err != nil {
// return err
// }
// col.createNewOffsets(preFill + 1)
// for i := 0; i < rValues.Len(); i++ {
// // increment offset
// col.offsets[0].values.col[col.offsets[0].values.col.Rows()-1] += 1
// value := rValues.Index(i)
// sKind = value.Kind()
// if sKind == reflect.Interface {
// sKind = value.Elem().Kind()
// }
// switch sKind {
// case reflect.Struct:
// col.isNested = true
// if err = oldIterateStruct(value, col, 0); err != nil {
// return err
// }
// case reflect.Map:
// col.isNested = true
// if err = oldIterateMap(value, col, 0); err != nil {
// return err
// }
// case reflect.Slice:
// if err = parseSlice("", value.Interface(), col, 0); err != nil {
// return err
// }
// default:
// // only happens if slice has a primitive mixed with complex types in a []any
// return &Error{
// ColumnType: fmt.Sprint(sKind),
// Err: fmt.Errorf("slices must be same dimension in column %s", col.Name()),
// }
// }
// }
// return nil
// }
// return parsePrimitive(name, sKind, values, true, jCol, preFill)
// }
//
// func parseStruct(name string, structVal reflect.Value, jCol JSONParent, preFill int) error {
// col, err := jCol.upsertObject(name)
// if err != nil {
// return err
// }
// return oldIterateStruct(structVal, col, preFill)
// }
//
// func oldIterateStruct(structVal reflect.Value, col JSONParent, preFill int) error {
// // structs generally have consistent field counts but we ignore nil values that are any as we can't infer from
// // these until they occur - so we might need to either backfill when to do occur or insert empty based on previous
// if structVal.Kind() == reflect.Interface {
// // can happen if passed from []any
// structVal = structVal.Elem()
// }
//
// currentColumns := col.columnNames()
// columnLookup := make(map[string]struct{})
// numRows := col.rows()
// for _, name := range currentColumns {
// columnLookup[name] = struct{}{}
// }
// addedColumns := make([]string, structVal.NumField(), structVal.NumField())
// newColumn := false
//
// for i := 0; i < structVal.NumField(); i++ {
// fName, omit := getStructFieldName(structVal.Type().Field(i))
// if omit {
// continue
// }
// field := structVal.Field(i)
// if !field.CanInterface() {
// // can't interface - likely not exported so ignore the field
// continue
// }
// kind := field.Kind()
// value := field.Interface()
// fType := field.Type()
// //resolve underlying kind
// if kind == reflect.Interface {
// if value == nil {
// // ignore nil fields
// continue
// }
// kind = reflect.TypeOf(value).Kind()
// field = reflect.ValueOf(value)
// fType = field.Type()
// }
// if _, ok := columnLookup[fName]; !ok && len(currentColumns) > 0 {
// // new column - need to handle missing
// preFill = numRows
// newColumn = true
// }
// if _, ok := typeMappings[fType.String()]; ok {
// if err := parseType(fName, fType, value, false, col, preFill); err != nil {
// return err
// }
// } else {
// switch kind {
// case reflect.Slice:
// if reflect.ValueOf(value).Len() == 0 {
// continue
// }
// if err := parseSlice(fName, value, col, preFill); err != nil {
// return err
// }
// case reflect.Struct:
// if err := parseStruct(fName, field, col, preFill); err != nil {
// return err
// }
// case reflect.Map:
// if err := parseMap(fName, field, col, preFill); err != nil {
// return err
// }
// default:
// if err := parsePrimitive(fName, kind, value, false, col, preFill); err != nil {
// return err
// }
// }
// }
// addedColumns[i] = fName
// if newColumn {
// // reset as otherwise prefill overflow to other fields. But don't reset if this prefill has come from
// // a higher level
// preFill = 0
// }
// }
// // handle missing
// missingColumns := difference(currentColumns, addedColumns)
// for _, name := range missingColumns {
// if err := col.insertEmptyColumn(name); err != nil {
// return err
// }
// }
// return nil
// }
//
// func parseMap(name string, mapVal reflect.Value, jCol JSONParent, preFill int) error {
// if mapVal.Type().Key().Kind() != reflect.String {
// return &Error{
// ColumnType: fmt.Sprint(mapVal.Type().Key().Kind()),
// Err: fmt.Errorf("map keys must be string for column %s", name),
// }
// }
// col, err := jCol.upsertObject(name)
// if err != nil {
// return err
// }
// return oldIterateMap(mapVal, col, preFill)
// }
//
// func oldIterateMap(mapVal reflect.Value, col JSONParent, preFill int) error {
// // maps can have inconsistent numbers of elements - we must ensure they are consistent in the encoding
// // two inconsistent options - 1. new - map has new columns 2. massing - map has missing columns
// // for (1) we need to update previous, for (2) we need to ensure we add a null entry
// if mapVal.Kind() == reflect.Interface {
// // can happen if passed from []any
// mapVal = mapVal.Elem()
// }
//
// currentColumns := col.columnNames()
// //gives us a fast lookup for large maps
// columnLookup := make(map[string]struct{})
// numRows := col.rows()
// // true if we need nil values
// for _, name := range currentColumns {
// columnLookup[name] = struct{}{}
// }
// addedColumns := make([]string, len(mapVal.MapKeys()), len(mapVal.MapKeys()))
// newColumn := false
// for i, key := range mapVal.MapKeys() {
// if newColumn {
// // reset as otherwise prefill overflow to other fields. But don't reset if this prefill has come from
// // a higher level
// preFill = 0
// }
//
// name := getMapFieldName(key.Interface().(string))
// if _, ok := columnLookup[name]; !ok && len(currentColumns) > 0 {
// // new column - need to handle
// preFill = numRows
// newColumn = true
// }
// field := mapVal.MapIndex(key)
// kind := field.Kind()
// fType := field.Type()
//
// if kind == reflect.Interface {
// if field.Interface() == nil {
// // ignore nil fields
// continue
// }
// kind = reflect.TypeOf(field.Interface()).Kind()
// field = reflect.ValueOf(field.Interface())
// fType = field.Type()
// }
// if _, ok := typeMappings[fType.String()]; ok {
// if err := parseType(name, fType, field.Interface(), false, col, preFill); err != nil {
// return err
// }
// } else {
// switch kind {
// case reflect.Struct:
// if err := parseStruct(name, field, col, preFill); err != nil {
// return err
// }
// case reflect.Slice:
// if err := parseSlice(name, field.Interface(), col, preFill); err != nil {
// return err
// }
// case reflect.Map:
// if err := parseMap(name, field, col, preFill); err != nil {
// return err
// }
// default:
// if err := parsePrimitive(name, kind, field.Interface(), false, col, preFill); err != nil {
// return err
// }
// }
// }
// addedColumns[i] = name
// }
// // handle missing
// missingColumns := difference(currentColumns, addedColumns)
// for _, name := range missingColumns {
// if err := col.insertEmptyColumn(name); err != nil {
// return err
// }
// }
// return nil
// }
//
// func appendStructOrMap(jCol *JSONObject, data any) error {
// vData := reflect.ValueOf(data)
// kind := vData.Kind()
// if kind == reflect.Struct {
// return oldIterateStruct(vData, jCol, 0)
// }
// if kind == reflect.Map {
// if reflect.TypeOf(data).Key().Kind() != reflect.String {
// return &Error{
// ColumnType: fmt.Sprint(reflect.TypeOf(data).Key().Kind()),
// Err: fmt.Errorf("map keys must be string for column %s", jCol.Name()),
// }
// }
// if jCol.columns == nil && vData.Len() == 0 {
// // if map is empty, we need to create an empty Tuple to make sure subcolumns protocol is happy
// // _dummy is a Datastore internal name for empty Tuple subcolumn
// // it has the same effect as `INSERT INTO single_json_type_table VALUES ('{}');`
// jCol.upsertValue("_dummy", "Int8")
// return jCol.insertEmptyColumn("_dummy")
// }
// return oldIterateMap(vData, jCol, 0)
// }
// return &UnsupportedColumnTypeError{
// t: Type(fmt.Sprint(kind)),
// }
// }
//
// type JSONValue struct {
// Interface
// // represents the type e.g. uuid - these may have been mapped to a Column type support by JSON e.g. String
// origType reflect.Type
// }
//
// func (jCol *JSONValue) Reset() {
// jCol.Interface.Reset()
// }
//
// func (jCol *JSONValue) appendEmptyValue() error {
// switch jCol.Interface.(type) {
// case *Array:
// if jCol.Rows() > 0 {
// return jCol.AppendRow(reflect.MakeSlice(reflect.TypeOf(jCol.Row(0, false)), 0, 0).Interface())
// }
// return &Error{
// ColumnType: "unknown",
// Err: fmt.Errorf("can't add empty value to column %s - no entries to infer type", jCol.Name()),
// }
// default:
// // can't just append nil here as we need a custom nil value for the type
// if jCol.origType != nil {
// return jCol.AppendRow(fmt.Sprint(reflect.New(jCol.origType).Elem().Interface()))
// }
// return jCol.AppendRow(nil)
// }
// }
//
// func (jCol *JSONValue) Type() Type {
// return Type(fmt.Sprintf("%s %s", jCol.Name(), jCol.Interface.Type()))
// }
//
// type JSONList struct {
// Array
// name string
// isNested bool // indicates if this a list of objects i.e. a Nested
// }
//
// func (jCol *JSONList) Name() string {
// return jCol.name
// }
//
// func (jCol *JSONList) columnNames() []string {
// return jCol.Array.values.(*JSONObject).columnNames()
// }
//
// func (jCol *JSONList) rows() int {
// return jCol.values.(*JSONObject).Rows()
// }
//
// func createJSONList(name string, sc *ServerContext) (jCol *JSONList) {
// // lists are represented as Nested which are in turn encoded as Array(Tuple()). We thus pass a Array(JSONObject())
// // as this encodes like a tuple
// lCol := &JSONList{
// name: name,
// }
// lCol.values = &JSONObject{sc: sc}
// // depth should always be one as nested arrays aren't possible
// lCol.depth = 1
// lCol.scanType = scanTypeSlice
// offsetScanTypes := []reflect.Type{lCol.scanType}
// lCol.offsets = []*offset{{
// scanType: offsetScanTypes[0],
// }}
// return lCol
// }
//
// func (jCol *JSONList) appendEmptyValue() error {
// // only need to bump the offsets
// jCol.createNewOffsets(1)
// return nil
// }
//
// func (jCol *JSONList) insertEmptyColumn(name string) error {
// return jCol.values.(*JSONObject).insertEmptyColumn(name)
// }
//
// func (jCol *JSONList) upsertValue(name string, ct string) (*JSONValue, error) {
// // check if column exists and reuse if same type, error if same name and different type
// jObj := jCol.values.(*JSONObject)
// cols := jObj.columns
// for i := range cols {
// sCol := cols[i]
// if sCol.Name() == name {
// vCol, ok := cols[i].(*JSONValue)
// if !ok {
// sType := cols[i].Type()
// return nil, &Error{
// ColumnType: fmt.Sprint(sType),
// Err: fmt.Errorf("type mismatch in column %s - expected value, got %s", name, sType),
// }
// }
// tType := vCol.Interface.Type()
// if tType != Type(ct) {
// return nil, &Error{
// ColumnType: ct,
// Err: fmt.Errorf("type mismatch in column %s - expected %s, got %s", name, tType, ct),
// }
// }
// return vCol, nil
// }
// }
// col, err := Type(ct).Column(name, jObj.sc)
// if err != nil {
// return nil, err
// }
// vCol := &JSONValue{
// Interface: col,
// }
// jCol.values.(*JSONObject).columns = append(cols, vCol) // nolint:gocritic
// return vCol, nil
// }
//
// func (jCol *JSONList) upsertList(name string) (*JSONList, error) {
// // check if column exists and reuse if same type, error if same name and different type
// jObj := jCol.values.(*JSONObject)
// cols := jCol.values.(*JSONObject).columns
// for i := range cols {
// sCol := cols[i]
// if sCol.Name() == name {
// sCol, ok := cols[i].(*JSONList)
// if !ok {
// return nil, &Error{
// ColumnType: fmt.Sprint(cols[i].Type()),
// Err: fmt.Errorf("type mismatch in column %s - expected list, got %s", name, cols[i].Type()),
// }
// }
// return sCol, nil
// }
// }
// lCol := createJSONList(name, jObj.sc)
// jCol.values.(*JSONObject).columns = append(cols, lCol) // nolint:gocritic
// return lCol, nil
//
// }
//
// func (jCol *JSONList) upsertObject(name string) (*JSONObject, error) {
// // check if column exists and reuse if same type, error if same name and different type
// jObj := jCol.values.(*JSONObject)
// cols := jObj.columns
// for i := range cols {
// sCol := cols[i]
// if sCol.Name() == name {
// sCol, ok := cols[i].(*JSONObject)
// if !ok {
// sType := cols[i].Type()
// return nil, &Error{
// ColumnType: fmt.Sprint(sType),
// Err: fmt.Errorf("type mismatch in column %s, expected object got %s", name, sType),
// }
// }
// return sCol, nil
// }
// }
// // lists are represented as Nested which are in turn encoded as Array(Tuple()). We thus pass a Array(JSONObject())
// // as this encodes like a tuple
// oCol := &JSONObject{
// name: name,
// sc: jObj.sc,
// }
// jCol.values.(*JSONObject).columns = append(cols, oCol) // nolint:gocritic
// return oCol, nil
// }
//
// func (jCol *JSONList) Type() Type {
// cols := jCol.values.(*JSONObject).columns
// subTypes := make([]string, len(cols))
// for i, v := range cols {
// subTypes[i] = string(v.Type())
// }
// // can be a list of lists or a nested
// if jCol.isNested {
// return Type(fmt.Sprintf("%s Nested(%s)", jCol.name, strings.Join(subTypes, ", ")))
// }
// return Type(fmt.Sprintf("%s Array(%s)", jCol.name, strings.Join(subTypes, ", ")))
// }
//
// type JSONObject struct {
// columns []ObjectJSON
// name string
// root bool
// encoding uint8
// sc *ServerContext
// }
//
// func (jCol *JSONObject) Reset() {
// for i := range jCol.columns {
// jCol.columns[i].Reset()
// }
// }
//
// func (jCol *JSONObject) Name() string {
// return jCol.name
// }
//
// func (jCol *JSONObject) columnNames() []string {
// columns := make([]string, len(jCol.columns), len(jCol.columns))
// for i := range jCol.columns {
// columns[i] = jCol.columns[i].Name()
// }
// return columns
// }
//
// func (jCol *JSONObject) rows() int {
// return jCol.Rows()
// }
//
// func (jCol *JSONObject) appendEmptyValue() error {
// for i := range jCol.columns {
// if err := jCol.columns[i].appendEmptyValue(); err != nil {
// return err
// }
// }
// return nil
// }
//
// func (jCol *JSONObject) insertEmptyColumn(name string) error {
// for i := range jCol.columns {
// if jCol.columns[i].Name() == name {
// if err := jCol.columns[i].appendEmptyValue(); err != nil {
// return err
// }
// return nil
// }
// }
// return &Error{
// ColumnType: "unknown",
// Err: fmt.Errorf("column %s is missing - empty value cannot be appended", name),
// }
// }
//
// func (jCol *JSONObject) upsertValue(name string, ct string) (*JSONValue, error) {
// for i := range jCol.columns {
// sCol := jCol.columns[i]
// if sCol.Name() == name {
// vCol, ok := jCol.columns[i].(*JSONValue)
// if !ok {
// sType := jCol.columns[i].Type()
// return nil, &Error{
// ColumnType: fmt.Sprint(sType),
// Err: fmt.Errorf("type mismatch in column %s, expected value got %s", name, sType),
// }
// }
// if vCol.Interface.Type() != Type(ct) {
// return nil, &Error{
// ColumnType: ct,
// Err: fmt.Errorf("type mismatch in column %s, expected %s got %s", name, vCol.Interface.Type(), ct),
// }
// }
// return vCol, nil
// }
// }
// col, err := Type(ct).Column(name, jCol.sc)
// if err != nil {
// return nil, err
// }
// vCol := &JSONValue{
// Interface: col,
// }
// jCol.columns = append(jCol.columns, vCol)
// return vCol, nil
// }
//
// func (jCol *JSONObject) upsertList(name string) (*JSONList, error) {
// for i := range jCol.columns {
// sCol := jCol.columns[i]
// if sCol.Name() == name {
// sCol, ok := jCol.columns[i].(*JSONList)
// if !ok {
// sType := jCol.columns[i].Type()
// return nil, &Error{
// ColumnType: fmt.Sprint(sType),
// Err: fmt.Errorf("type mismatch in column %s, expected list got %s", name, sType),
// }
// }
// return sCol, nil
// }
// }
// lCol := createJSONList(name, jCol.sc)
// jCol.columns = append(jCol.columns, lCol)
// return lCol, nil
// }
//
// func (jCol *JSONObject) upsertObject(name string) (*JSONObject, error) {
// // check if it exists
// for i := range jCol.columns {
// sCol := jCol.columns[i]
// if sCol.Name() == name {
// sCol, ok := jCol.columns[i].(*JSONObject)
// if !ok {
// sType := jCol.columns[i].Type()
// return nil, &Error{
// ColumnType: fmt.Sprint(sType),
// Err: fmt.Errorf("type mismatch in column %s, expected object got %s", name, sType),
// }
// }
// return sCol, nil
// }
// }
// // not present so create
// oCol := &JSONObject{
// name: name,
// sc: jCol.sc,
// }
// jCol.columns = append(jCol.columns, oCol)
// return oCol, nil
// }
//
// func (jCol *JSONObject) Type() Type {
// if jCol.root {
// return "Object('json')"
// }
// return jCol.FullType()
// }
//
// func (jCol *JSONObject) FullType() Type {
// subTypes := make([]string, len(jCol.columns))
// for i, v := range jCol.columns {
// subTypes[i] = string(v.Type())
// }
// if jCol.root {
// return Type(fmt.Sprintf("Tuple(%s)", strings.Join(subTypes, ", ")))
// }
// return Type(fmt.Sprintf("%s Tuple(%s)", jCol.name, strings.Join(subTypes, ", ")))
// }
//
// func (jCol *JSONObject) ScanType() reflect.Type {
// return scanTypeMap
// }
//
// func (jCol *JSONObject) Rows() int {
// if len(jCol.columns) != 0 {
// return jCol.columns[0].Rows()
// }
// return 0
// }
//
// // Datastore returns JSON as a tuple i.e. these will never be invoked
//
// func (jCol *JSONObject) Row(i int, ptr bool) any {
// panic("Not implemented")
// }
//
// func (jCol *JSONObject) ScanRow(dest any, row int) error {
// panic("Not implemented")
// }
//
// func (jCol *JSONObject) Append(v any) (nulls []uint8, err error) {
// jSlice := reflect.ValueOf(v)
// if jSlice.Kind() != reflect.Slice {
// return nil, &ColumnConverterError{
// Op: "Append",
// To: string(jCol.Type()),
// From: fmt.Sprintf("slice of structs/map or strings required - received %T", v),
// }
// }
// for i := 0; i < jSlice.Len(); i++ {
// if err := jCol.AppendRow(jSlice.Index(i).Interface()); err != nil {
// return nil, err
// }
// }
// return nil, nil
// }
//
// func (jCol *JSONObject) AppendRow(v any) error {
// if reflect.ValueOf(v).Kind() == reflect.Struct || reflect.ValueOf(v).Kind() == reflect.Map {
// if jCol.columns != nil && jCol.encoding == 1 {
// return &Error{
// ColumnType: fmt.Sprint(jCol.Type()),
// Err: fmt.Errorf("encoding of JSON columns cannot be mixed in a batch - %s cannot be added as previously String", reflect.ValueOf(v).Kind()),
// }
// }
// err := appendStructOrMap(jCol, v)
// return err
// }
// switch v := v.(type) {
// case string:
// if jCol.columns != nil && jCol.encoding == 0 {
// return &Error{
// ColumnType: fmt.Sprint(jCol.Type()),
// Err: fmt.Errorf("encoding of JSON columns cannot be mixed in a batch - %s cannot be added as previously Struct/Map", reflect.ValueOf(v).Kind()),
// }
// }
// jCol.encoding = 1
// if jCol.columns == nil {
// jCol.columns = append(jCol.columns, &JSONValue{Interface: &String{}})
// }
// jCol.columns[0].AppendRow(v)
// default:
// return &ColumnConverterError{
// Op: "AppendRow",
// To: "String",
// From: fmt.Sprintf("json row must be struct, map or string - received %T", v),
// }
// }
// return nil
// }
//
// func (jCol *JSONObject) Decode(reader *proto.Reader, rows int) error {
// panic("Not implemented")
// }
//
// func (jCol *JSONObject) Encode(buffer *proto.Buffer) {
// if jCol.root && jCol.encoding == 0 {
// buffer.PutString(string(jCol.FullType()))
// }
// for _, c := range jCol.columns {
// c.Encode(buffer)
// }
// }
//
// func (jCol *JSONObject) ReadStatePrefix(reader *proto.Reader) error {
// _, err := reader.UInt8()
// return err
// }
//
// func (jCol *JSONObject) WriteStatePrefix(buffer *proto.Buffer) error {
// buffer.PutUInt8(jCol.encoding)
// return nil
// }
//
// var (
// _ Interface = (*JSONObject)(nil)
// _ CustomSerialization = (*JSONObject)(nil)
// )