-
-
Notifications
You must be signed in to change notification settings - Fork 63
Use dolt JSON encoding #2639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
zachmu
wants to merge
29
commits into
main
Choose a base branch
from
zachmu/json-enc
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Use dolt JSON encoding #2639
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
5e6ea56
using indexedjsondocument
zachmu 93cd5f9
merge main
zachmu f7648e1
fix test harness for json
zachmu 6409b07
always use json types in json func output
zachmu 9c0c972
more json conversion
zachmu c7679a1
converting more jsonb stuff
zachmu fa5f48d
more jsonb conversion
zachmu e06e03d
Merge branch 'main' into zachmu/json-enc
zachmu 2ea3f7a
fixed jsonb_object_field
zachmu ef86f58
merge dev branch
zachmu d39a2f4
turn json encoding back on
zachmu 6324c85
merge dev branch
zachmu 298b82c
turn json encoding back on
zachmu 7c74053
Fixed remaining areas of JSON support
zachmu c2cf3e1
formatting
zachmu a67af1d
linting
zachmu 6a575ad
formatting
zachmu 58b76e1
unfocus test
zachmu de02e2e
unfocus test
zachmu 94133f2
removed JSONB ordering test
zachmu cf79f4a
deleted unused json document type
zachmu 09ca8e5
Merge branch 'main' into zachmu/json-enc
zachmu 47f98ac
restore deleted json_document.go
zachmu ad4e7c7
Restore legacy ser/deser paths
zachmu 589098a
[ga-format-pr] Run scripts/format_repo.sh
zachmu 13249bf
new dolt
zachmu f648270
merge main
zachmu af13d08
new dolt
zachmu 3d4c9d8
revert latest dolt changes
zachmu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,8 +15,9 @@ | |
| package cast | ||
|
|
||
| import ( | ||
| "github.com/cockroachdb/errors" | ||
| "encoding/json" | ||
|
|
||
| "github.com/cockroachdb/errors" | ||
| "github.com/dolthub/go-mysql-server/sql" | ||
| "github.com/shopspring/decimal" | ||
|
|
||
|
|
@@ -30,173 +31,250 @@ func initJsonB() { | |
| jsonbAssignment() | ||
| } | ||
|
|
||
| // jsonbGetInterface extracts the native Go value from a JSONB value (sql.JSONWrapper or string). | ||
| func jsonbGetInterface(ctx *sql.Context, val any) (any, error) { | ||
| switch v := val.(type) { | ||
| case sql.JSONWrapper: | ||
| return v.ToInterface(ctx) | ||
| case string: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to handle sql.Wrapper[string] too? |
||
| var result any | ||
| if err := json.Unmarshal([]byte(v), &result); err != nil { | ||
| return nil, errors.Errorf("invalid JSON: %v", err) | ||
| } | ||
| return result, nil | ||
| default: | ||
| return nil, errors.Errorf("unexpected JSONB value type: %T", val) | ||
| } | ||
| } | ||
|
|
||
| // jsonbNumberToDecimal converts various numeric types from JSON deserialization to decimal.Decimal. | ||
| func jsonbNumberToDecimal(v any) (decimal.Decimal, bool) { | ||
| switch n := v.(type) { | ||
| case float64: | ||
| return decimal.NewFromFloat(n), true | ||
| case float32: | ||
| return decimal.NewFromFloat32(n), true | ||
| case json.Number: | ||
| d, err := decimal.NewFromString(n.String()) | ||
| if err != nil { | ||
| return decimal.Decimal{}, false | ||
| } | ||
| return d, true | ||
| case int64: | ||
| return decimal.NewFromInt(n), true | ||
| case int32: | ||
| return decimal.NewFromInt(int64(n)), true | ||
| case decimal.Decimal: | ||
| return n, true | ||
| } | ||
| return decimal.Decimal{}, false | ||
| } | ||
|
|
||
| // jsonbExplicit registers all explicit casts. This comprises only the "From" types. | ||
| func jsonbExplicit() { | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Bool, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch value := v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| return nil, errors.Errorf("cannot cast jsonb numeric to type %s", targetType.String()) | ||
| case pgtypes.JsonValueBoolean: | ||
| return bool(value), nil | ||
| case pgtypes.JsonValueNull: | ||
| case bool: | ||
| return value, nil | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| if _, ok := jsonbNumberToDecimal(v); ok { | ||
| return nil, errors.Errorf("cannot cast jsonb numeric to type %s", targetType.String()) | ||
| } | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| }, | ||
| }) | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Float32, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| f, _ := decimal.Decimal(value).Float64() | ||
| return float32(f), nil | ||
| case pgtypes.JsonValueBoolean: | ||
| case bool: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNull: | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| d, ok := jsonbNumberToDecimal(v) | ||
| if !ok { | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| f, _ := d.Float64() | ||
| return float32(f), nil | ||
| } | ||
| }, | ||
| }) | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Float64, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| f, _ := decimal.Decimal(value).Float64() | ||
| return f, nil | ||
| case pgtypes.JsonValueBoolean: | ||
| case bool: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNull: | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| d, ok := jsonbNumberToDecimal(v) | ||
| if !ok { | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| f, _ := d.Float64() | ||
| return f, nil | ||
| } | ||
| }, | ||
| }) | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Int16, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| d := decimal.Decimal(value) | ||
| case bool: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| d, ok := jsonbNumberToDecimal(v) | ||
| if !ok { | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| if d.LessThan(pgtypes.NumericValueMinInt16) || d.GreaterThan(pgtypes.NumericValueMaxInt16) { | ||
| return nil, errors.Errorf("smallint out of range") | ||
| } | ||
| return int16(d.IntPart()), nil | ||
| case pgtypes.JsonValueBoolean: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNull: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| } | ||
| }, | ||
| }) | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Int32, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| d := decimal.Decimal(value) | ||
| case bool: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| d, ok := jsonbNumberToDecimal(v) | ||
| if !ok { | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| if d.LessThan(pgtypes.NumericValueMinInt32) || d.GreaterThan(pgtypes.NumericValueMaxInt32) { | ||
| return nil, errors.Errorf("integer out of range") | ||
| } | ||
| return int32(d.IntPart()), nil | ||
| case pgtypes.JsonValueBoolean: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNull: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| } | ||
| }, | ||
| }) | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Int64, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| d := decimal.Decimal(value) | ||
| case bool: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| d, ok := jsonbNumberToDecimal(v) | ||
| if !ok { | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| if d.LessThan(pgtypes.NumericValueMinInt64) || d.GreaterThan(pgtypes.NumericValueMaxInt64) { | ||
| return nil, errors.Errorf("bigint out of range") | ||
| } | ||
| return int64(d.IntPart()), nil | ||
| case pgtypes.JsonValueBoolean: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNull: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| } | ||
| }, | ||
| }) | ||
| framework.MustAddExplicitTypeCast(framework.TypeCast{ | ||
| FromType: pgtypes.JsonB, | ||
| ToType: pgtypes.Numeric, | ||
| Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { | ||
| switch value := val.(pgtypes.JsonDocument).Value.(type) { | ||
| case pgtypes.JsonValueObject: | ||
| v, err := jsonbGetInterface(ctx, val) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| switch v.(type) { | ||
| case map[string]interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb object to type %s", targetType.String()) | ||
| case pgtypes.JsonValueArray: | ||
| case []interface{}: | ||
| return nil, errors.Errorf("cannot cast jsonb array to type %s", targetType.String()) | ||
| case pgtypes.JsonValueString: | ||
| case string: | ||
| return nil, errors.Errorf("cannot cast jsonb string to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNumber: | ||
| return decimal.Decimal(value), nil | ||
| case pgtypes.JsonValueBoolean: | ||
| case bool: | ||
| return nil, errors.Errorf("cannot cast jsonb boolean to type %s", targetType.String()) | ||
| case pgtypes.JsonValueNull: | ||
| case nil: | ||
| return nil, errors.Errorf("cannot cast jsonb null to type %s", targetType.String()) | ||
| default: | ||
| return nil, errors.Errorf("") | ||
| d, ok := jsonbNumberToDecimal(v) | ||
| if !ok { | ||
| return nil, errors.Errorf("unexpected jsonb value type: %T", v) | ||
| } | ||
| return d, nil | ||
| } | ||
| }, | ||
| }) | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of being a string, we might also encounter a type that implements sql.Wrapper[string], such as TextStorage.