mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-27 00:51:39 -06:00
Fixes dolthub/dolt#9556 The issue was in dolt's JSON path parser where unnecessarily quoted simple field names like $."a" were being rejected, despite MySQL accepting them. This caused compatibility issues with Django's compile_json_path() function which always quotes keys. Changes: - Fixed lexer in json_location.go to properly handle quoted field names - Adjusted token position when encountering opening quotes - Corrected empty string detection logic for quoted keys - Added comprehensive tests matching the customer's use case The fix ensures MySQL compatibility for: - $.a (unquoted field names) - $."a" (unnecessarily quoted simple field names) - $."a key" (necessarily quoted field names with spaces) - $."a"."b" (nested quoted field names) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
341 lines
13 KiB
Bash
341 lines
13 KiB
Bash
#!/usr/bin/env bats
|
|
load $BATS_TEST_DIRNAME/helper/common.bash
|
|
|
|
setup() {
|
|
setup_common
|
|
}
|
|
|
|
teardown() {
|
|
teardown_common
|
|
}
|
|
|
|
@test "json: Create table with JSON column" {
|
|
run dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
SQL
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "json: query JSON values" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"a":1}'), (2, '{"b":2}');
|
|
SQL
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":1}"' ]
|
|
[ "${lines[2]}" = '2,"{""b"":2}"' ]
|
|
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"c":3}' WHERE pk = 2;
|
|
SQL
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":1}"' ]
|
|
[ "${lines[2]}" = '2,"{""c"":3}"' ]
|
|
|
|
dolt sql <<SQL
|
|
DELETE FROM js WHERE pk = 2;
|
|
SQL
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":1}"' ]
|
|
}
|
|
|
|
@test "json: JSON value printing" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"a":1}'), (2, '{"b":2}');
|
|
SQL
|
|
|
|
run dolt sql -q "SELECT * FROM js;"
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[0]}" = '+----+---------+' ]
|
|
[ "${lines[1]}" = '| pk | js |' ]
|
|
[ "${lines[2]}" = '+----+---------+' ]
|
|
[ "${lines[3]}" = '| 1 | {"a":1} |' ]
|
|
[ "${lines[4]}" = '| 2 | {"b":2} |' ]
|
|
[ "${lines[5]}" = '+----+---------+' ]
|
|
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[0]}" = 'pk,js' ]
|
|
[ "${lines[1]}" = '1,"{""a"":1}"' ]
|
|
[ "${lines[2]}" = '2,"{""b"":2}"' ]
|
|
|
|
dolt sql -q "SELECT * FROM js;" -r json
|
|
run dolt sql -q "SELECT * FROM js;" -r json
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[0]}" = '{"rows": [{"js":{"a":1},"pk":1},{"js":{"b":2},"pk":2}]}' ]
|
|
|
|
dolt sql <<SQL
|
|
insert into js values (3, '["abc", 123, 1.5, {"a": 123, "b":[456, "def"]}]');
|
|
SQL
|
|
|
|
dolt sql -q "SELECT * FROM js where pk = 3" -r json
|
|
run dolt sql -q "SELECT * FROM js where pk = 3" -r json
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[0]}" = '{"rows": [{"js":["abc",123,1.5,{"a":123,"b":[456,"def"]}],"pk":3}]}' ]
|
|
|
|
}
|
|
|
|
@test "json: JSON value printing HTML characters" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"<>&":"<>&"}');
|
|
SQL
|
|
|
|
dolt sql -q "SELECT * FROM js" -r json
|
|
run dolt sql -q "SELECT * FROM js" -r json
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[0]}" = '{"rows": [{"js":{"<>&":"<>&"},"pk":1}]}' ]
|
|
|
|
}
|
|
|
|
@test "json: diff JSON values" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"a":1}'), (2, '{"b":2}');
|
|
SQL
|
|
dolt add .
|
|
dolt commit -am "added JSON table"
|
|
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"a":11}' WHERE pk = 1;
|
|
DELETE FROM js WHERE pk = 2;
|
|
INSERT INTO js VALUES (3, '{"c":3}');
|
|
SQL
|
|
|
|
dolt diff
|
|
run dolt diff
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[0]}" = 'diff --dolt a/js b/js' ]
|
|
[ "${lines[3]}" = '+---+----+----------+' ]
|
|
[ "${lines[4]}" = '| | pk | js |' ]
|
|
[ "${lines[5]}" = '+---+----+----------+' ]
|
|
[ "${lines[6]}" = '| < | 1 | {"a":1} |' ]
|
|
[ "${lines[7]}" = '| > | 1 | {"a":11} |' ]
|
|
[ "${lines[8]}" = '| - | 2 | {"b":2} |' ]
|
|
[ "${lines[9]}" = '| + | 3 | {"c":3} |' ]
|
|
[ "${lines[10]}" = '+---+----+----------+' ]
|
|
}
|
|
|
|
@test "json: merge JSON values" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"a":1}'), (2, '{"b":2}');
|
|
SQL
|
|
dolt add .
|
|
dolt commit -am "added JSON table"
|
|
dolt branch other
|
|
dolt branch another
|
|
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"a":11}' WHERE pk = 1;
|
|
SQL
|
|
dolt commit -am "made changes on branch main"
|
|
|
|
dolt checkout other
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"b":22}' WHERE pk = 2;
|
|
SQL
|
|
dolt commit -am "made changes on branch other"
|
|
|
|
dolt checkout main
|
|
dolt merge other --no-commit
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":11}"' ]
|
|
[ "${lines[2]}" = '2,"{""b"":22}"' ]
|
|
dolt commit -am "merged other into main"
|
|
|
|
# test merge conflicts
|
|
dolt checkout another
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"b":99}' WHERE pk = 2;
|
|
SQL
|
|
dolt commit -am "made changes on branch another"
|
|
|
|
run dolt merge other -m "merge"
|
|
[ "$status" -eq 1 ]
|
|
[[ "$output" =~ "CONFLICT" ]] || false
|
|
run dolt conflicts resolve --ours js
|
|
[ "$status" -eq 0 ]
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":1}"' ]
|
|
[ "${lines[2]}" = '2,"{""b"":99}"' ]
|
|
}
|
|
|
|
@test "json: merge JSON values with stored procedure" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"a":1}'), (2, '{"b":2}');
|
|
SQL
|
|
dolt add .
|
|
dolt commit -am "added JSON table"
|
|
dolt branch other
|
|
dolt branch another
|
|
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"a":11}' WHERE pk = 1;
|
|
SQL
|
|
dolt commit -am "made changes on branch main"
|
|
|
|
dolt checkout other
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"b":22}' WHERE pk = 2;
|
|
SQL
|
|
dolt commit -am "made changes on branch other"
|
|
|
|
dolt checkout main
|
|
dolt merge other --no-commit
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":11}"' ]
|
|
[ "${lines[2]}" = '2,"{""b"":22}"' ]
|
|
dolt commit -am "merged other into main"
|
|
|
|
# test merge conflicts
|
|
dolt checkout another
|
|
dolt sql <<SQL
|
|
UPDATE js SET js = '{"b":99}' WHERE pk = 2;
|
|
SQL
|
|
dolt commit -am "made changes on branch another"
|
|
|
|
run dolt merge other -m "merge"
|
|
[ "$status" -eq 1 ]
|
|
[[ "$output" =~ "CONFLICT" ]] || false
|
|
run dolt sql -q "call dolt_conflicts_resolve('--ours', 'js')"
|
|
[ "$status" -eq 0 ]
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":1}"' ]
|
|
[ "${lines[2]}" = '2,"{""b"":99}"' ]
|
|
}
|
|
|
|
@test "json: insert value with special characters" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '{"a":"<>&"}');
|
|
SQL
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"{""a"":""<>&""}"' ]
|
|
}
|
|
|
|
|
|
@test "json: insert array with special characters" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
INSERT INTO js VALUES (1, '[{"a":"<>&"}]');
|
|
SQL
|
|
run dolt sql -q "SELECT * FROM js;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,"[{""a"":""<>&""}]"' ]
|
|
}
|
|
|
|
@test "json: insert large string value (> 1MB)" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE t (
|
|
pk int PRIMARY KEY,
|
|
j1 json
|
|
);
|
|
SQL
|
|
|
|
dolt sql -f $BATS_TEST_DIRNAME/json-large-value-insert.sql
|
|
|
|
dolt sql -q "SELECT pk, length(j1) FROM t;" -r csv
|
|
run dolt sql -q "SELECT pk, length(j1) FROM t;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '1,3145771' ]
|
|
}
|
|
|
|
# This test inserts a large JSON document with the `dolt_dont_optimize_json` flag set.
|
|
# We expect that the document gets stored as a blob.
|
|
@test "json: Test dolt_optimize_json system variable" {
|
|
run dolt sql <<SQL
|
|
set @@dolt_optimize_json = 0;
|
|
CREATE TABLE js (
|
|
pk int PRIMARY KEY,
|
|
js json
|
|
);
|
|
insert into js values (1, "[[[[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]],[[[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]],[[[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]],[[[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]], [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]]]");
|
|
SQL
|
|
# If the document isn't put into an IndexedJsonDocument, it will have the following hash.
|
|
run dolt show qivuleqpbin1eise78h5u8k1hqe4f07g
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" =~ "Blob" ]] || false
|
|
}
|
|
|
|
# Tests for dolthub/dolt#9556: JSON path handling with unnecessary quotes
|
|
# This tests Django compatibility where compile_json_path() always quotes keys
|
|
@test "json: JSON_SET with quoted field names" {
|
|
dolt sql <<SQL
|
|
CREATE TABLE test_data (data JSON);
|
|
INSERT INTO test_data VALUES ('{}');
|
|
SQL
|
|
|
|
# Test unquoted JSON paths (should work)
|
|
run dolt sql -q "UPDATE test_data SET data = JSON_SET(data, '$.a', 'b');"
|
|
[ "$status" -eq 0 ]
|
|
|
|
run dolt sql -q "SELECT data FROM test_data;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '"{""a"":""b""}"' ]
|
|
|
|
# Test necessarily quoted JSON paths (should work)
|
|
run dolt sql -q "UPDATE test_data SET data = JSON_SET(data, '$.\"a key\"', 'b');"
|
|
[ "$status" -eq 0 ]
|
|
|
|
run dolt sql -q "SELECT data FROM test_data;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '"{""a"":""b"",""a key"":""b""}"' ]
|
|
|
|
# Test unnecessarily quoted JSON paths (this was the failing case)
|
|
run dolt sql -q "UPDATE test_data SET data = JSON_SET(data, '$.\"c\"', 'test');"
|
|
[ "$status" -eq 0 ]
|
|
|
|
run dolt sql -q "SELECT data FROM test_data;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '"{""a"":""b"",""a key"":""b"",""c"":""test""}"' ]
|
|
|
|
# Test nested unnecessarily quoted paths (first create the parent object)
|
|
run dolt sql -q "UPDATE test_data SET data = JSON_SET(data, '$.\"d\"', JSON_OBJECT());"
|
|
[ "$status" -eq 0 ]
|
|
|
|
run dolt sql -q "UPDATE test_data SET data = JSON_SET(data, '$.\"d\".\"e\"', 'nested');"
|
|
[ "$status" -eq 0 ]
|
|
|
|
run dolt sql -q "SELECT data FROM test_data;" -r csv
|
|
[ "$status" -eq 0 ]
|
|
[ "${lines[1]}" = '"{""a"":""b"",""a key"":""b"",""c"":""test"",""d"":{""e"":""nested""}}"' ]
|
|
}
|