fix(kingbase): format bit column values as bit-string literals

This commit is contained in:
zipg authored and GitHub committed 2026-09-04 00:29:17 +08:00
1 parent 730a4eca5f
commit de73c2bd73
4 files changed
+274 -40

No files matched your search

@@ -5,9 +5,9 @@ use super::{
};
use crate::data_grid_sql::{
build_column_predicate, build_data_grid_copy_insert_statement, build_data_grid_copy_update_statements,
data_grid_qualified_table_name, format_grid_sql_literal, is_auto_generated_column, is_grid_insert_omitted_column,
is_non_identity_generated_column, supports_relational_copy_predicates, DataGridCopyInsertStatementOptions,
DataGridCopyUpdateStatementOptions, DataGridTableMeta,
data_grid_qualified_table_name, format_grid_sql_literal_with_identifier_quote, is_auto_generated_column,
is_grid_insert_omitted_column, is_non_identity_generated_column, supports_relational_copy_predicates,
DataGridCopyInsertStatementOptions, DataGridCopyUpdateStatementOptions, DataGridTableMeta,
};
use serde_json::Value;
use std::collections::{hash_map::Entry, HashMap, HashSet};
@@ -31,7 +31,15 @@ pub(super) fn write_sql_in_list(
}
let value = &row[*source_index];
let info = context.selected_column_info[column_index];
tuple.extend_from_slice(format_grid_sql_literal(value, context.request.database_type, info).as_bytes());
tuple.extend_from_slice(
format_grid_sql_literal_with_identifier_quote(
value,
context.request.database_type,
info,
context.request.identifier_quote.as_deref(),
)
.as_bytes(),
);
}
if context.selected_columns.len() > 1 {
tuple.push(b')');
@@ -248,6 +248,34 @@ fn sql_in_list_deduplicates_only_identical_rendered_literals() {
assert_eq!(result.text, "(1, '1', NULL, 'O''Reilly')");
}
#[test]
fn sql_in_list_honors_kingbase_bit_literals() {
let mut request = request(DataGridExtractorId::SqlInList);
request.database_type = Some(DatabaseType::Kingbase);
request.identifier_quote = Some("`".to_string());
request.columns = vec![column("flag", 0)];
request.selected_column_indexes = vec![0];
request.table_meta = Some(DataGridTableMeta {
catalog: None,
database: None,
schema: None,
table_name: "flags".to_string(),
primary_keys: vec![],
columns: Some(vec![DataGridColumnInfo {
name: "flag".to_string(),
data_type: "pg_catalog.bit(1)".to_string(),
is_nullable: true,
is_primary_key: false,
column_default: None,
extra: None,
}]),
});
request.rows = vec![vec![json!("0")], vec![json!("1")]];
let result = extract_data_grid_selection(request).expect("Kingbase SQL IN extraction");
assert_eq!(result.text, "(b'0', b'1')");
}
#[test]
fn preserves_duplicate_json_columns_without_overwriting_values() {
let mut request = request(DataGridExtractorId::Json);
@@ -176,6 +176,7 @@ fn build_iotdb_insert_statement(
value,
Some(DatabaseType::Iotdb),
column_info_for(iotdb_column_info(options), column),
options.identifier_quote.as_deref(),
)
})
.collect::<Vec<_>>()
+233 -36
View File
@@ -411,6 +411,7 @@ pub fn build_data_grid_copy_update_statements(options: DataGridCopyUpdateStateme
row.get(*index).unwrap_or(&Value::Null),
options.database_type,
*info,
options.identifier_quote.as_deref(),
)
)
})
@@ -511,6 +512,7 @@ pub fn build_data_grid_copy_insert_statement(options: DataGridCopyInsertStatemen
row.get(*index).unwrap_or(&Value::Null),
options.database_type,
info.as_ref(),
options.identifier_quote.as_deref(),
)
})
.collect::<Vec<_>>()
@@ -604,7 +606,8 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
value,
options.database_type,
&options.column_name,
options.column_info.as_ref()
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
)
)),
DataGridContextFilterMode::LessThanOrEqual => Some(format!(
@@ -613,7 +616,8 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
value,
options.database_type,
&options.column_name,
options.column_info.as_ref()
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
)
)),
DataGridContextFilterMode::GreaterThan => Some(format!(
@@ -622,7 +626,8 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
value,
options.database_type,
&options.column_name,
options.column_info.as_ref()
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
)
)),
DataGridContextFilterMode::GreaterThanOrEqual => Some(format!(
@@ -631,7 +636,8 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
value,
options.database_type,
&options.column_name,
options.column_info.as_ref()
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
)
)),
DataGridContextFilterMode::In => build_data_grid_context_membership_filter_condition(
@@ -640,6 +646,7 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
options.database_type,
&options.column_name,
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
false,
),
DataGridContextFilterMode::NotIn => build_data_grid_context_membership_filter_condition(
@@ -648,6 +655,7 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
options.database_type,
&options.column_name,
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
true,
),
DataGridContextFilterMode::Between => build_data_grid_context_range_filter_condition(
@@ -657,6 +665,7 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
options.database_type,
&options.column_name,
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
false,
),
DataGridContextFilterMode::NotBetween => build_data_grid_context_range_filter_condition(
@@ -666,6 +675,7 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
options.database_type,
&options.column_name,
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
true,
),
DataGridContextFilterMode::Equals => Some(format!(
@@ -674,7 +684,8 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
value,
options.database_type,
&options.column_name,
options.column_info.as_ref()
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
)
)),
DataGridContextFilterMode::NotEquals => Some(format!(
@@ -683,7 +694,8 @@ pub fn build_data_grid_context_filter_condition(options: DataGridContextFilterCo
value,
options.database_type,
&options.column_name,
options.column_info.as_ref()
options.column_info.as_ref(),
options.identifier_quote.as_deref(),
)
)),
}
@@ -694,13 +706,14 @@ fn format_data_grid_context_filter_literal(
database_type: Option<DatabaseType>,
column_name: &str,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
) -> String {
if database_type == Some(DatabaseType::Iotdb) && column_info.is_none() && column_name.eq_ignore_ascii_case("time") {
if let Some(value) = value.as_str().filter(|value| is_decimal_i64(value)) {
return value.to_string();
}
}
format_grid_sql_literal(value, database_type, column_info)
format_grid_sql_literal_with_identifier_quote(value, database_type, column_info, identifier_quote)
}
fn is_decimal_i64(value: &str) -> bool {
@@ -714,6 +727,7 @@ fn build_data_grid_context_membership_filter_condition(
database_type: Option<DatabaseType>,
column_name: &str,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
negated: bool,
) -> Option<String> {
if values.is_empty() {
@@ -728,7 +742,8 @@ fn build_data_grid_context_membership_filter_condition(
has_null = true;
continue;
}
let literal = format_data_grid_context_filter_literal(value, database_type, column_name, column_info);
let literal =
format_data_grid_context_filter_literal(value, database_type, column_name, column_info, identifier_quote);
if seen_literals.insert(literal.clone()) {
literals.push(literal);
}
@@ -786,14 +801,17 @@ fn build_data_grid_context_range_filter_condition(
database_type: Option<DatabaseType>,
column_name: &str,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
negated: bool,
) -> Option<String> {
let end_value = end_value?;
if start_value.is_null() || end_value.is_null() {
return None;
}
let start = format_data_grid_context_filter_literal(start_value, database_type, column_name, column_info);
let end = format_data_grid_context_filter_literal(end_value, database_type, column_name, column_info);
let start =
format_data_grid_context_filter_literal(start_value, database_type, column_name, column_info, identifier_quote);
let end =
format_data_grid_context_filter_literal(end_value, database_type, column_name, column_info, identifier_quote);
if database_type == Some(DatabaseType::Neo4j) {
return if negated {
Some(format!("({column} < {start} OR {column} > {end})"))
@@ -987,7 +1005,12 @@ pub fn build_data_grid_conditional_update_sql(options: DataGridConditionalUpdate
let sets = format!(
"{} = {}",
data_grid_identifier(options.database_type, &column_info.name, options.identifier_quote.as_deref()),
format_grid_save_sql_literal(&options.value, options.database_type, Some(column_info))
format_grid_save_sql_literal(
&options.value,
options.database_type,
Some(column_info),
options.identifier_quote.as_deref(),
)
);
Some(data_grid_statement(
options.database_type,
@@ -1328,7 +1351,12 @@ fn build_data_grid_save_statements(
Some(format!(
"{} = {}",
data_grid_identifier(options.database_type, column, options.identifier_quote.as_deref()),
format_grid_save_sql_literal(value, options.database_type, column_info_for(column_info, column))
format_grid_save_sql_literal(
value,
options.database_type,
column_info_for(column_info, column),
options.identifier_quote.as_deref(),
)
))
})
.collect::<Vec<_>>()
@@ -1433,7 +1461,12 @@ fn build_data_grid_save_statements(
let values = insert_pairs
.iter()
.map(|(column, value)| {
format_grid_save_sql_literal(value, options.database_type, column_info_for(column_info, column))
format_grid_save_sql_literal(
value,
options.database_type,
column_info_for(column_info, column),
options.identifier_quote.as_deref(),
)
})
.collect::<Vec<_>>()
.join(", ");
@@ -1533,7 +1566,12 @@ fn build_data_grid_rollback_statements(
let values = insert_pairs
.iter()
.map(|(column, value)| {
format_grid_assignment_sql_literal(value, options.database_type, column_info_for(column_info, column))
format_grid_assignment_sql_literal(
value,
options.database_type,
column_info_for(column_info, column),
options.identifier_quote.as_deref(),
)
})
.collect::<Vec<_>>()
.join(", ");
@@ -1594,7 +1632,8 @@ fn build_data_grid_rollback_statements(
format_grid_assignment_sql_literal(
row.get(*column_index).unwrap_or(&Value::Null),
options.database_type,
column_info_for(column_info, column)
column_info_for(column_info, column),
options.identifier_quote.as_deref(),
)
)
})
@@ -1882,9 +1921,14 @@ fn build_hive_values_insert(
.map(|(column, value)| {
let info = column_info_for(metadata_columns, column);
if save_literals {
format_grid_save_sql_literal(value, options.database_type, info)
format_grid_save_sql_literal(value, options.database_type, info, options.identifier_quote.as_deref())
} else {
format_grid_assignment_sql_literal(value, options.database_type, info)
format_grid_assignment_sql_literal(
value,
options.database_type,
info,
options.identifier_quote.as_deref(),
)
}
})
.collect::<Vec<_>>()
@@ -1942,6 +1986,7 @@ fn format_grid_copy_insert_sql_literal(
value: &Value,
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
) -> String {
if is_oracle_temporal_literal_database(database_type) {
if let Some(text) = value.as_str() {
@@ -1952,13 +1997,14 @@ fn format_grid_copy_insert_sql_literal(
}
}
}
format_grid_assignment_sql_literal(value, database_type, column_info)
format_grid_assignment_sql_literal(value, database_type, column_info, identifier_quote)
}
fn format_grid_assignment_sql_literal(
value: &Value,
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
) -> String {
if matches!(database_type, Some(DatabaseType::Oracle | DatabaseType::OceanbaseOracle)) {
if let Some(constructor) = column_info.and_then(|column| oracle_character_lob_constructor(&column.data_type)) {
@@ -1968,9 +2014,14 @@ fn format_grid_assignment_sql_literal(
}
}
if (value.is_array() || value.is_object()) && is_json_document_column(column_info) {
return format_grid_sql_literal(&Value::String(value.to_string()), database_type, column_info);
return format_grid_sql_literal_with_identifier_quote(
&Value::String(value.to_string()),
database_type,
column_info,
identifier_quote,
);
}
format_grid_sql_literal(value, database_type, column_info)
format_grid_sql_literal_with_identifier_quote(value, database_type, column_info, identifier_quote)
}
fn format_oracle_lob_assignment_literal(text: &str, constructor: &str) -> String {
@@ -2033,15 +2084,27 @@ pub fn format_grid_sql_literal(
value: &Value,
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
) -> String {
format_grid_sql_literal_with_identifier_quote(value, database_type, column_info, None)
}
pub(crate) fn format_grid_sql_literal_with_identifier_quote(
value: &Value,
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
) -> String {
if value.is_null() {
return "NULL".to_string();
}
// Boolean values on BIT columns always use numeric 0/1.
// Boolean values on BIT columns use database-native boolean/bit literals.
// This covers MySQL, SQL Server, and any other database where BIT
// is a numeric/boolean type rather than a bit-string type like
// PostgreSQL's bit(n).
if let Some(value) = value.as_bool() {
if is_kingbase_bit_literal_column(database_type, column_info) {
return format!("b'{}'", if value { '1' } else { '0' });
}
// SQL Server has no TRUE/FALSE literals (its boolean type is BIT, which
// is_bit_literal_column already covers); any other column there still
// needs numeric 1/0 instead of a literal.
@@ -2050,7 +2113,19 @@ pub fn format_grid_sql_literal(
}
return if value { "TRUE" } else { "FALSE" }.to_string();
}
if is_mysql_bit_literal_column(database_type, column_info) {
if is_kingbase_bit_literal_column(database_type, column_info) {
if let Some(number) = value.as_number() {
if let Some(literal) = format_kingbase_bit_literal_text(&number.to_string()) {
return literal;
}
}
if let Some(text) = value.as_str() {
if let Some(literal) = format_kingbase_bit_literal_text(text) {
return literal;
}
}
}
if is_mysql_bit_literal_column(database_type, column_info, identifier_quote) {
if let Some(number) = value.as_number() {
return number.to_string();
}
@@ -2106,7 +2181,12 @@ pub fn format_grid_sql_literal(
}
if database_type == Some(DatabaseType::ManticoreSearch) {
if let Some(typed_value) = manticore_typed_attribute_value(&text, column_info) {
return format_grid_sql_literal(&typed_value, database_type, column_info);
return format_grid_sql_literal_with_identifier_quote(
&typed_value,
database_type,
column_info,
identifier_quote,
);
}
}
if text.is_empty() {
@@ -2245,11 +2325,12 @@ fn format_grid_save_sql_literal(
value: &Value,
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
) -> String {
if empty_string_saves_as_null(value, column_info) {
"NULL".to_string()
} else {
format_grid_assignment_sql_literal(value, database_type, column_info)
format_grid_assignment_sql_literal(value, database_type, column_info, identifier_quote)
}
}
@@ -2377,8 +2458,21 @@ fn regex_like_local_datetime(text: &str) -> Option<Rfc3339Parts> {
Some(Rfc3339Parts { date: date.to_string(), time: time.to_string(), fraction, zone: String::new() })
}
fn is_mysql_bit_literal_column(database_type: Option<DatabaseType>, column_info: Option<&DataGridColumnInfo>) -> bool {
is_mysql_datetime_literal_database(database_type)
fn is_mysql_bit_literal_column(
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
identifier_quote: Option<&str>,
) -> bool {
(is_mysql_datetime_literal_database(database_type)
|| (database_type == Some(DatabaseType::Kingbase) && identifier_quote == Some("`")))
&& column_info.map(|column| is_bit_column_type(&column.data_type)).unwrap_or(false)
}
fn is_kingbase_bit_literal_column(
database_type: Option<DatabaseType>,
column_info: Option<&DataGridColumnInfo>,
) -> bool {
database_type == Some(DatabaseType::Kingbase)
&& column_info.map(|column| is_bit_column_type(&column.data_type)).unwrap_or(false)
}
@@ -2526,6 +2620,27 @@ fn format_mysql_bit_literal_text(text: &str) -> Option<String> {
None
}
fn format_kingbase_bit_literal_text(text: &str) -> Option<String> {
let trimmed = text.trim();
if trimmed.eq_ignore_ascii_case("true") {
return Some("b'1'".to_string());
}
if trimmed.eq_ignore_ascii_case("false") {
return Some("b'0'".to_string());
}
if !trimmed.is_empty() && trimmed.chars().all(|ch| matches!(ch, '0' | '1')) {
return Some(format!("b'{trimmed}'"));
}
let lower = trimmed.to_ascii_lowercase();
if lower.starts_with("b'") && trimmed.ends_with('\'') {
let bits = &trimmed[2..trimmed.len() - 1];
if !bits.is_empty() && bits.chars().all(|ch| matches!(ch, '0' | '1')) {
return Some(format!("b'{bits}'"));
}
}
None
}
fn is_mysql_datetime_literal_database(database_type: Option<DatabaseType>) -> bool {
matches!(
database_type,
@@ -2783,9 +2898,12 @@ pub(crate) fn build_column_predicate(
if value.is_null() {
format!("{ident} IS NULL")
} else if use_binary_text_comparison && uses_mysql_binary_text_predicate(database_type, value, column_info) {
format!("BINARY {ident} = {}", format_grid_assignment_sql_literal(value, database_type, column_info))
format!(
"BINARY {ident} = {}",
format_grid_assignment_sql_literal(value, database_type, column_info, identifier_quote)
)
} else {
let literal = format_grid_assignment_sql_literal(value, database_type, column_info);
let literal = format_grid_assignment_sql_literal(value, database_type, column_info, identifier_quote);
if use_binary_text_comparison {
if let Some(predicate) = postgres_keyless_json_predicate(database_type, &ident, &literal, column_info) {
return predicate;
@@ -2807,9 +2925,12 @@ fn build_save_column_predicate(
if value.is_null() || empty_string_saves_as_null(value, column_info) {
format!("{ident} IS NULL")
} else if use_binary_text_comparison && uses_mysql_binary_text_predicate(database_type, value, column_info) {
format!("BINARY {ident} = {}", format_grid_save_sql_literal(value, database_type, column_info))
format!(
"BINARY {ident} = {}",
format_grid_save_sql_literal(value, database_type, column_info, identifier_quote)
)
} else {
let literal = format_grid_save_sql_literal(value, database_type, column_info);
let literal = format_grid_save_sql_literal(value, database_type, column_info, identifier_quote);
if use_binary_text_comparison {
if let Some(predicate) = postgres_keyless_json_predicate(database_type, &ident, &literal, column_info) {
return predicate;
@@ -5316,7 +5437,8 @@ mod tests {
fn prepares_oracle_clob_update_without_oversized_string_literals() {
let clob = column("body", "CLOB", true, None);
let large_value = "x".repeat(4205);
let literal = format_grid_assignment_sql_literal(&json!(large_value), Some(DatabaseType::Oracle), Some(&clob));
let literal =
format_grid_assignment_sql_literal(&json!(large_value), Some(DatabaseType::Oracle), Some(&clob), None);
let chunks = literal
.split("TO_CLOB('")
.skip(1)
@@ -5328,21 +5450,26 @@ mod tests {
assert_eq!(chunks[0].len(), ORACLE_LOB_LITERAL_CHUNK_BYTES);
assert_eq!(chunks[1].len(), 4205 - ORACLE_LOB_LITERAL_CHUNK_BYTES);
assert_eq!(
format_grid_assignment_sql_literal(&json!("short"), Some(DatabaseType::Oracle), Some(&clob)),
format_grid_assignment_sql_literal(&json!("short"), Some(DatabaseType::Oracle), Some(&clob), None),
"'short'"
);
let nclob = column("body", "NCLOB", true, None);
assert!(format_grid_assignment_sql_literal(&json!(large_value), Some(DatabaseType::Oracle), Some(&nclob))
.starts_with("TO_NCLOB('"));
assert!(format_grid_assignment_sql_literal(
&json!(large_value),
Some(DatabaseType::Oracle),
Some(&nclob),
None
)
.starts_with("TO_NCLOB('"));
let special_value = format!("{}'\\", "x".repeat(3998));
let special_literal =
format_grid_assignment_sql_literal(&json!(special_value), Some(DatabaseType::Oracle), Some(&clob));
format_grid_assignment_sql_literal(&json!(special_value), Some(DatabaseType::Oracle), Some(&clob), None);
assert!(special_literal.starts_with("TO_CLOB('"));
assert!(special_literal.contains("''"));
assert!(special_literal.contains("\\\\"));
let varchar = column("body", "VARCHAR2(5000)", true, None);
let varchar_literal =
format_grid_assignment_sql_literal(&json!(large_value), Some(DatabaseType::Oracle), Some(&varchar));
format_grid_assignment_sql_literal(&json!(large_value), Some(DatabaseType::Oracle), Some(&varchar), None);
assert!(varchar_literal.starts_with("'"));
assert!(!varchar_literal.contains("TO_CLOB("));
@@ -6649,6 +6776,76 @@ mod tests {
assert_eq!(format_grid_sql_literal(&json!("0"), Some(DatabaseType::Postgres), Some(&bit)), "'0'");
}
#[test]
fn formats_kingbase_bit_literals_for_both_compatibility_modes() {
let bit = column("flag", "pg_catalog.bit(1)", true, None);
assert_eq!(
format_grid_sql_literal_with_identifier_quote(
&json!("0"),
Some(DatabaseType::Kingbase),
Some(&bit),
Some("`"),
),
"b'0'"
);
assert_eq!(
format_grid_sql_literal_with_identifier_quote(
&json!("10101010"),
Some(DatabaseType::Kingbase),
Some(&column("flags", "pg_catalog.bit(8)", true, None)),
Some("`"),
),
"b'10101010'"
);
assert_eq!(
format_grid_sql_literal_with_identifier_quote(
&json!("0"),
Some(DatabaseType::Kingbase),
Some(&bit),
Some("\""),
),
"b'0'"
);
assert_eq!(
format_grid_sql_literal_with_identifier_quote(
&json!(1),
Some(DatabaseType::Kingbase),
Some(&bit),
Some("\""),
),
"b'1'"
);
}
#[test]
fn kingbase_bit_save_uses_bit_string_literals() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {
database_type: Some(DatabaseType::Kingbase),
identifier_quote: Some("`".to_string()),
table_meta: DataGridTableMeta {
catalog: None,
database: None,
schema: Some("public".to_string()),
table_name: "flags".to_string(),
primary_keys: vec!["id".to_string()],
columns: Some(vec![column("id", "int", false, None), column("flag", "pg_catalog.bit(1)", true, None)]),
},
columns: vec!["id".to_string(), "flag".to_string()],
source_columns: None,
rows: vec![vec![json!(1), json!("1")]],
dirty_rows: vec![(0, vec![(1, json!("0"))])],
deleted_rows: vec![],
new_rows: vec![],
});
assert_eq!(result.statements, vec!["UPDATE `public`.`flags` SET `flag` = b'0' WHERE `id` = 1;"],);
assert_eq!(
result.rollback_statements,
vec!["UPDATE `public`.`flags` SET `flag` = b'1' WHERE `id` = 1 AND `flag` = b'0';"],
);
}
#[test]
fn postgres_literals_use_escape_strings_for_stable_backslash_semantics() {
let value = json!(r#"{"json_raw":"{\"foo\":1,\"bar\":\"sometext\"}"}"#);