LCOV - code coverage report
Current view: top level - proxy/src/serverless - json.rs (source / functions) Coverage Total Hit
Test: 17080b14f46954d6812ea0a7dad4b2247e0840a8.info Lines: 89.2 % 324 289
Test Date: 2025-07-08 18:30:10 Functions: 90.0 % 30 27

            Line data    Source code
       1              : use postgres_client::Row;
       2              : use postgres_client::types::{Kind, Type};
       3              : use serde_json::{Map, Value};
       4              : 
       5              : //
       6              : // Convert json non-string types to strings, so that they can be passed to Postgres
       7              : // as parameters.
       8              : //
       9           10 : pub(crate) fn json_to_pg_text(json: Vec<Value>) -> Vec<Option<String>> {
      10           10 :     json.iter().map(json_value_to_pg_text).collect()
      11           10 : }
      12              : 
      13           11 : fn json_value_to_pg_text(value: &Value) -> Option<String> {
      14           11 :     match value {
      15              :         // special care for nulls
      16            1 :         Value::Null => None,
      17              : 
      18              :         // convert to text with escaping
      19            3 :         v @ (Value::Bool(_) | Value::Number(_) | Value::Object(_)) => Some(v.to_string()),
      20              : 
      21              :         // avoid escaping here, as we pass this as a parameter
      22            4 :         Value::String(s) => Some(s.clone()),
      23              : 
      24              :         // special care for arrays
      25            3 :         Value::Array(_) => json_array_to_pg_array(value),
      26              :     }
      27           11 : }
      28              : 
      29              : //
      30              : // Serialize a JSON array to a Postgres array. Contrary to the strings in the params
      31              : // in the array we need to escape the strings. Postgres is okay with arrays of form
      32              : // '{1,"2",3}'::int[], so we don't check that array holds values of the same type, leaving
      33              : // it for Postgres to check.
      34              : //
      35              : // Example of the same escaping in node-postgres: packages/pg/lib/utils.js
      36              : //
      37           23 : fn json_array_to_pg_array(value: &Value) -> Option<String> {
      38           23 :     match value {
      39              :         // special care for nulls
      40            2 :         Value::Null => None,
      41              : 
      42              :         // convert to text with escaping
      43              :         // here string needs to be escaped, as it is part of the array
      44           13 :         v @ (Value::Bool(_) | Value::Number(_) | Value::String(_)) => Some(v.to_string()),
      45            2 :         v @ Value::Object(_) => json_array_to_pg_array(&Value::String(v.to_string())),
      46              : 
      47              :         // recurse into array
      48            6 :         Value::Array(arr) => {
      49            6 :             let vals = arr
      50            6 :                 .iter()
      51            6 :                 .map(json_array_to_pg_array)
      52           18 :                 .map(|v| v.unwrap_or_else(|| "NULL".to_string()))
      53            6 :                 .collect::<Vec<_>>()
      54            6 :                 .join(",");
      55              : 
      56            6 :             Some(format!("{{{vals}}}"))
      57              :         }
      58              :     }
      59           23 : }
      60              : 
      61              : #[derive(Debug, thiserror::Error)]
      62              : pub(crate) enum JsonConversionError {
      63              :     #[error("internal error compute returned invalid data: {0}")]
      64              :     AsTextError(postgres_client::Error),
      65              :     #[error("parse int error: {0}")]
      66              :     ParseIntError(#[from] std::num::ParseIntError),
      67              :     #[error("parse float error: {0}")]
      68              :     ParseFloatError(#[from] std::num::ParseFloatError),
      69              :     #[error("parse json error: {0}")]
      70              :     ParseJsonError(#[from] serde_json::Error),
      71              :     #[error("unbalanced array")]
      72              :     UnbalancedArray,
      73              :     #[error("unbalanced quoted string")]
      74              :     UnbalancedString,
      75              : }
      76              : 
      77              : enum OutputMode {
      78              :     Array(Vec<Value>),
      79              :     Object(Map<String, Value>),
      80              : }
      81              : 
      82              : impl OutputMode {
      83            0 :     fn key(&mut self, key: &str) -> &mut Value {
      84            0 :         match self {
      85            0 :             OutputMode::Array(values) => push_entry(values, Value::Null),
      86            0 :             OutputMode::Object(map) => map.entry(key.to_string()).or_insert(Value::Null),
      87              :         }
      88            0 :     }
      89              : 
      90            0 :     fn finish(self) -> Value {
      91            0 :         match self {
      92            0 :             OutputMode::Array(values) => Value::Array(values),
      93            0 :             OutputMode::Object(map) => Value::Object(map),
      94              :         }
      95            0 :     }
      96              : }
      97              : 
      98           90 : fn push_entry<T>(arr: &mut Vec<T>, t: T) -> &mut T {
      99           90 :     arr.push(t);
     100           90 :     arr.last_mut().expect("a value was just inserted")
     101           90 : }
     102              : 
     103              : //
     104              : // Convert postgres row with text-encoded values to JSON object
     105              : //
     106            0 : pub(crate) fn pg_text_row_to_json(
     107            0 :     row: &Row,
     108            0 :     raw_output: bool,
     109            0 :     array_mode: bool,
     110            0 : ) -> Result<Value, JsonConversionError> {
     111            0 :     let mut entries = if array_mode {
     112            0 :         OutputMode::Array(Vec::with_capacity(row.columns().len()))
     113              :     } else {
     114            0 :         OutputMode::Object(Map::with_capacity(row.columns().len()))
     115              :     };
     116              : 
     117            0 :     for (i, column) in row.columns().iter().enumerate() {
     118            0 :         let pg_value = row.as_text(i).map_err(JsonConversionError::AsTextError)?;
     119              : 
     120            0 :         let value = entries.key(column.name());
     121              : 
     122            0 :         match pg_value {
     123            0 :             Some(v) if raw_output => *value = Value::String(v.to_string()),
     124            0 :             Some(v) => pg_text_to_json(value, v, column.type_())?,
     125            0 :             None => *value = Value::Null,
     126              :         }
     127              :     }
     128              : 
     129            0 :     Ok(entries.finish())
     130            0 : }
     131              : 
     132              : //
     133              : // Convert postgres text-encoded value to JSON value
     134              : //
     135           76 : fn pg_text_to_json(
     136           76 :     output: &mut Value,
     137           76 :     val: &str,
     138           76 :     pg_type: &Type,
     139           76 : ) -> Result<(), JsonConversionError> {
     140           76 :     if let Kind::Array(elem_type) = pg_type.kind() {
     141              :         // todo: we should fetch this from postgres.
     142            0 :         let delimiter = ',';
     143              : 
     144            0 :         let mut array = vec![];
     145            0 :         pg_array_parse(&mut array, val, elem_type, delimiter)?;
     146            0 :         *output = Value::Array(array);
     147            0 :         return Ok(());
     148           76 :     }
     149              : 
     150           76 :     match *pg_type {
     151           12 :         Type::BOOL => *output = Value::Bool(val == "t"),
     152              :         Type::INT2 | Type::INT4 => {
     153           14 :             let val = val.parse::<i32>()?;
     154           14 :             *output = Value::Number(serde_json::Number::from(val));
     155              :         }
     156              :         Type::FLOAT4 | Type::FLOAT8 => {
     157           23 :             let fval = val.parse::<f64>()?;
     158           23 :             let num = serde_json::Number::from_f64(fval);
     159           23 :             if let Some(num) = num {
     160           14 :                 *output = Value::Number(num);
     161           14 :             } else {
     162            9 :                 // Pass Nan, Inf, -Inf as strings
     163            9 :                 // JS JSON.stringify() does converts them to null, but we
     164            9 :                 // want to preserve them, so we pass them as strings
     165            9 :                 *output = Value::String(val.to_string());
     166            9 :             }
     167              :         }
     168            7 :         Type::JSON | Type::JSONB => *output = serde_json::from_str(val)?,
     169           20 :         _ => *output = Value::String(val.to_string()),
     170              :     }
     171              : 
     172           76 :     Ok(())
     173           76 : }
     174              : 
     175              : /// Parse postgres array into JSON array.
     176              : ///
     177              : /// This is a bit involved because we need to handle nested arrays and quoted
     178              : /// values. Unlike postgres we don't check that all nested arrays have the same
     179              : /// dimensions, we just return them as is.
     180              : ///
     181              : /// <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-IO>
     182              : ///
     183              : /// The external text representation of an array value consists of items that are interpreted
     184              : /// according to the I/O conversion rules for the array's element type, plus decoration that
     185              : /// indicates the array structure. The decoration consists of curly braces (`{` and `}`) around
     186              : /// the array value plus delimiter characters between adjacent items. The delimiter character
     187              : /// is usually a comma (,) but can be something else: it is determined by the typdelim setting
     188              : /// for the array's element type. Among the standard data types provided in the PostgreSQL
     189              : /// distribution, all use a comma, except for type box, which uses a semicolon (;).
     190              : ///
     191              : /// In a multidimensional array, each dimension (row, plane, cube, etc.)
     192              : /// gets its own level of curly braces, and delimiters must be written between adjacent
     193              : /// curly-braced entities of the same level.
     194           22 : fn pg_array_parse(
     195           22 :     elements: &mut Vec<Value>,
     196           22 :     mut pg_array: &str,
     197           22 :     elem: &Type,
     198           22 :     delim: char,
     199           22 : ) -> Result<(), JsonConversionError> {
     200              :     // skip bounds decoration, eg:
     201              :     // `[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}`
     202              :     // technically these are significant, but we have no way to represent them in json.
     203           22 :     if let Some('[') = pg_array.chars().next() {
     204            1 :         let Some((_bounds, array)) = pg_array.split_once('=') else {
     205            0 :             return Err(JsonConversionError::UnbalancedArray);
     206              :         };
     207            1 :         pg_array = array;
     208           21 :     }
     209              : 
     210              :     // whitespace might preceed a `{`.
     211           22 :     let pg_array = pg_array.trim_start();
     212              : 
     213           22 :     let rest = pg_array_parse_inner(elements, pg_array, elem, delim)?;
     214           22 :     if !rest.is_empty() {
     215            0 :         return Err(JsonConversionError::UnbalancedArray);
     216           22 :     }
     217              : 
     218           22 :     Ok(())
     219           22 : }
     220              : 
     221              : /// reads a single array from the `pg_array` string and pushes each values to `elements`.
     222              : /// returns the rest of the `pg_array` string that was not read.
     223           39 : fn pg_array_parse_inner<'a>(
     224           39 :     elements: &mut Vec<Value>,
     225           39 :     mut pg_array: &'a str,
     226           39 :     elem: &Type,
     227           39 :     delim: char,
     228           39 : ) -> Result<&'a str, JsonConversionError> {
     229              :     // array should have a `{` prefix.
     230           39 :     pg_array = pg_array
     231           39 :         .strip_prefix('{')
     232           39 :         .ok_or(JsonConversionError::UnbalancedArray)?;
     233              : 
     234           39 :     let mut q = String::new();
     235              : 
     236              :     loop {
     237           90 :         let value = push_entry(elements, Value::Null);
     238           90 :         pg_array = pg_array_parse_item(value, &mut q, pg_array, elem, delim)?;
     239              : 
     240              :         // check for separator.
     241           90 :         if let Some(next) = pg_array.strip_prefix(delim) {
     242           51 :             // next item.
     243           51 :             pg_array = next;
     244           51 :         } else {
     245           39 :             break;
     246              :         }
     247              :     }
     248              : 
     249           39 :     let Some(next) = pg_array.strip_prefix('}') else {
     250              :         // missing `}` terminator.
     251            0 :         return Err(JsonConversionError::UnbalancedArray);
     252              :     };
     253              : 
     254              :     // whitespace might follow a `}`.
     255           39 :     Ok(next.trim_start())
     256           39 : }
     257              : 
     258              : /// reads a single item from the `pg_array` string.
     259              : /// returns the rest of the `pg_array` string that was not read.
     260              : ///
     261              : /// `quoted` is a scratch allocation that has no defined output.
     262           90 : fn pg_array_parse_item<'a>(
     263           90 :     output: &mut Value,
     264           90 :     quoted: &mut String,
     265           90 :     mut pg_array: &'a str,
     266           90 :     elem: &Type,
     267           90 :     delim: char,
     268           90 : ) -> Result<&'a str, JsonConversionError> {
     269              :     // We are trying to parse an array item.
     270              :     // This could be a new array, if this is a multi-dimentional array.
     271              :     // This could be a quoted string representing `elem`.
     272              :     // This could be an unquoted string representing `elem`.
     273              : 
     274              :     // whitespace might preceed an item.
     275           90 :     pg_array = pg_array.trim_start();
     276              : 
     277           90 :     if pg_array.starts_with('{') {
     278              :         // nested array.
     279           17 :         let mut nested = vec![];
     280           17 :         pg_array = pg_array_parse_inner(&mut nested, pg_array, elem, delim)?;
     281           17 :         *output = Value::Array(nested);
     282           17 :         return Ok(pg_array);
     283           73 :     }
     284              : 
     285           73 :     if let Some(mut pg_array) = pg_array.strip_prefix('"') {
     286              :         // the parsed string is un-escaped and written into quoted.
     287           15 :         pg_array = pg_array_parse_quoted(quoted, pg_array)?;
     288              : 
     289              :         // we have un-escaped the string, parse it as pgtext.
     290           15 :         pg_text_to_json(output, quoted, elem)?;
     291              : 
     292           15 :         return Ok(pg_array);
     293           58 :     }
     294              : 
     295              :     // we need to parse an item. read until we find a delimiter or `}`.
     296           58 :     let index = pg_array
     297           58 :         .find([delim, '}'])
     298           58 :         .ok_or(JsonConversionError::UnbalancedArray)?;
     299              : 
     300              :     let item;
     301           58 :     (item, pg_array) = pg_array.split_at(index);
     302              : 
     303              :     // item might have trailing whitespace that we need to ignore.
     304           58 :     let item = item.trim_end();
     305              : 
     306              :     // we might have an item string:
     307              :     // check for null
     308           58 :     if item == "NULL" {
     309            7 :         *output = Value::Null;
     310            7 :     } else {
     311           51 :         pg_text_to_json(output, item, elem)?;
     312              :     }
     313              : 
     314           58 :     Ok(pg_array)
     315           90 : }
     316              : 
     317              : /// reads a single quoted item from the `pg_array` string.
     318              : ///
     319              : /// Returns the rest of the `pg_array` string that was not read.
     320              : /// The output is written into `quoted`.
     321              : ///
     322              : /// The pg_array string must have a `"` terminator, but the `"` initial value
     323              : /// must have already been removed from the input. The terminator is removed.
     324           15 : fn pg_array_parse_quoted<'a>(
     325           15 :     quoted: &mut String,
     326           15 :     mut pg_array: &'a str,
     327           15 : ) -> Result<&'a str, JsonConversionError> {
     328              :     // The array output routine will put double quotes around element values if they are empty strings,
     329              :     // contain curly braces, delimiter characters, double quotes, backslashes, or white space,
     330              :     // or match the word `NULL`. Double quotes and backslashes embedded in element values will be backslash-escaped.
     331              :     // For numeric data types it is safe to assume that double quotes will never appear,
     332              :     // but for textual data types one should be prepared to cope with either the presence or absence of quotes.
     333              : 
     334           15 :     quoted.clear();
     335              : 
     336              :     // We write to quoted in chunks terminated by an escape character.
     337              :     // Eg if we have the input `foo\"bar"`, then we write `foo`, then `"`, then finally `bar`.
     338              : 
     339              :     loop {
     340              :         // we need to parse an chunk. read until we find a '\\' or `"`.
     341           30 :         let i = pg_array
     342           30 :             .find(['\\', '"'])
     343           30 :             .ok_or(JsonConversionError::UnbalancedString)?;
     344              : 
     345              :         let chunk: &str;
     346           30 :         (chunk, pg_array) = pg_array
     347           30 :             .split_at_checked(i)
     348           30 :             .expect("i is guaranteed to be in-bounds of pg_array");
     349              : 
     350              :         // push the chunk.
     351           30 :         quoted.push_str(chunk);
     352              : 
     353              :         // consume the chunk_end character.
     354              :         let chunk_end: char;
     355           30 :         (chunk_end, pg_array) =
     356           30 :             split_first_char(pg_array).expect("pg_array should start with either '\\\\' or '\"'");
     357              : 
     358              :         // finished.
     359           30 :         if chunk_end == '"' {
     360              :             // whitespace might follow the '"'.
     361           15 :             pg_array = pg_array.trim_start();
     362              : 
     363           15 :             break Ok(pg_array);
     364           15 :         }
     365              : 
     366              :         // consume the escaped character.
     367              :         let escaped: char;
     368           15 :         (escaped, pg_array) =
     369           15 :             split_first_char(pg_array).ok_or(JsonConversionError::UnbalancedString)?;
     370              : 
     371           15 :         quoted.push(escaped);
     372              :     }
     373           15 : }
     374              : 
     375           45 : fn split_first_char(s: &str) -> Option<(char, &str)> {
     376           45 :     let mut chars = s.chars();
     377           45 :     let c = chars.next()?;
     378           45 :     Some((c, chars.as_str()))
     379           45 : }
     380              : 
     381              : #[cfg(test)]
     382              : mod tests {
     383              :     use serde_json::json;
     384              : 
     385              :     use super::*;
     386              : 
     387              :     #[test]
     388            1 :     fn test_atomic_types_to_pg_params() {
     389            1 :         let json = vec![Value::Bool(true), Value::Bool(false)];
     390            1 :         let pg_params = json_to_pg_text(json);
     391            1 :         assert_eq!(
     392              :             pg_params,
     393            1 :             vec![Some("true".to_owned()), Some("false".to_owned())]
     394              :         );
     395              : 
     396            1 :         let json = vec![Value::Number(serde_json::Number::from(42))];
     397            1 :         let pg_params = json_to_pg_text(json);
     398            1 :         assert_eq!(pg_params, vec![Some("42".to_owned())]);
     399              : 
     400            1 :         let json = vec![Value::String("foo\"".to_string())];
     401            1 :         let pg_params = json_to_pg_text(json);
     402            1 :         assert_eq!(pg_params, vec![Some("foo\"".to_owned())]);
     403              : 
     404            1 :         let json = vec![Value::Null];
     405            1 :         let pg_params = json_to_pg_text(json);
     406            1 :         assert_eq!(pg_params, vec![None]);
     407            1 :     }
     408              : 
     409              :     #[test]
     410            1 :     fn test_json_array_to_pg_array() {
     411              :         // atoms and escaping
     412            1 :         let json = "[true, false, null, \"NULL\", 42, \"foo\", \"bar\\\"-\\\\\"]";
     413            1 :         let json: Value = serde_json::from_str(json).unwrap();
     414            1 :         let pg_params = json_to_pg_text(vec![json]);
     415            1 :         assert_eq!(
     416              :             pg_params,
     417            1 :             vec![Some(
     418            1 :                 "{true,false,NULL,\"NULL\",42,\"foo\",\"bar\\\"-\\\\\"}".to_owned()
     419            1 :             )]
     420              :         );
     421              : 
     422              :         // nested arrays
     423            1 :         let json = "[[true, false], [null, 42], [\"foo\", \"bar\\\"-\\\\\"]]";
     424            1 :         let json: Value = serde_json::from_str(json).unwrap();
     425            1 :         let pg_params = json_to_pg_text(vec![json]);
     426            1 :         assert_eq!(
     427              :             pg_params,
     428            1 :             vec![Some(
     429            1 :                 "{{true,false},{NULL,42},{\"foo\",\"bar\\\"-\\\\\"}}".to_owned()
     430            1 :             )]
     431              :         );
     432              :         // array of objects
     433            1 :         let json = r#"[{"foo": 1},{"bar": 2}]"#;
     434            1 :         let json: Value = serde_json::from_str(json).unwrap();
     435            1 :         let pg_params = json_to_pg_text(vec![json]);
     436            1 :         assert_eq!(
     437              :             pg_params,
     438            1 :             vec![Some(r#"{"{\"foo\":1}","{\"bar\":2}"}"#.to_owned())]
     439              :         );
     440            1 :     }
     441              : 
     442           10 :     fn pg_text_to_json(val: &str, pg_type: &Type) -> Value {
     443           10 :         let mut v = Value::Null;
     444           10 :         super::pg_text_to_json(&mut v, val, pg_type).unwrap();
     445           10 :         v
     446           10 :     }
     447              : 
     448           22 :     fn pg_array_parse(pg_array: &str, pg_type: &Type) -> Value {
     449           22 :         let mut array = vec![];
     450           22 :         super::pg_array_parse(&mut array, pg_array, pg_type, ',').unwrap();
     451           22 :         Value::Array(array)
     452           22 :     }
     453              : 
     454              :     #[test]
     455            1 :     fn test_atomic_types_parse() {
     456            1 :         assert_eq!(pg_text_to_json("foo", &Type::TEXT), json!("foo"));
     457            1 :         assert_eq!(pg_text_to_json("42", &Type::INT4), json!(42));
     458            1 :         assert_eq!(pg_text_to_json("42", &Type::INT2), json!(42));
     459            1 :         assert_eq!(pg_text_to_json("42", &Type::INT8), json!("42"));
     460            1 :         assert_eq!(pg_text_to_json("42.42", &Type::FLOAT8), json!(42.42));
     461            1 :         assert_eq!(pg_text_to_json("42.42", &Type::FLOAT4), json!(42.42));
     462            1 :         assert_eq!(pg_text_to_json("NaN", &Type::FLOAT4), json!("NaN"));
     463            1 :         assert_eq!(
     464            1 :             pg_text_to_json("Infinity", &Type::FLOAT4),
     465            1 :             json!("Infinity")
     466              :         );
     467            1 :         assert_eq!(
     468            1 :             pg_text_to_json("-Infinity", &Type::FLOAT4),
     469            1 :             json!("-Infinity")
     470              :         );
     471              : 
     472            1 :         let json: Value =
     473            1 :             serde_json::from_str("{\"s\":\"str\",\"n\":42,\"f\":4.2,\"a\":[null,3,\"a\"]}")
     474            1 :                 .unwrap();
     475            1 :         assert_eq!(
     476            1 :             pg_text_to_json(
     477            1 :                 r#"{"s":"str","n":42,"f":4.2,"a":[null,3,"a"]}"#,
     478            1 :                 &Type::JSONB
     479              :             ),
     480              :             json
     481              :         );
     482            1 :     }
     483              : 
     484              :     #[test]
     485            1 :     fn test_pg_array_parse_text() {
     486            4 :         fn pt(pg_arr: &str) -> Value {
     487            4 :             pg_array_parse(pg_arr, &Type::TEXT)
     488            4 :         }
     489            1 :         assert_eq!(
     490            1 :             pt(r#"{"aa\"\\\,a",cha,"bbbb"}"#),
     491            1 :             json!(["aa\"\\,a", "cha", "bbbb"])
     492              :         );
     493            1 :         assert_eq!(
     494            1 :             pt(r#"{{"foo","bar"},{"bee","bop"}}"#),
     495            1 :             json!([["foo", "bar"], ["bee", "bop"]])
     496              :         );
     497            1 :         assert_eq!(
     498            1 :             pt(r#"{{{{"foo",NULL,"bop",bup}}}}"#),
     499            1 :             json!([[[["foo", null, "bop", "bup"]]]])
     500              :         );
     501            1 :         assert_eq!(
     502            1 :             pt(r#"{{"1",2,3},{4,NULL,6},{NULL,NULL,NULL}}"#),
     503            1 :             json!([["1", "2", "3"], ["4", null, "6"], [null, null, null]])
     504              :         );
     505            1 :     }
     506              : 
     507              :     #[test]
     508            1 :     fn test_pg_array_parse_bool() {
     509            4 :         fn pb(pg_arr: &str) -> Value {
     510            4 :             pg_array_parse(pg_arr, &Type::BOOL)
     511            4 :         }
     512            1 :         assert_eq!(pb(r#"{t,f,t}"#), json!([true, false, true]));
     513            1 :         assert_eq!(pb(r#"{{t,f,t}}"#), json!([[true, false, true]]));
     514            1 :         assert_eq!(
     515            1 :             pb(r#"{{t,f},{f,t}}"#),
     516            1 :             json!([[true, false], [false, true]])
     517              :         );
     518            1 :         assert_eq!(
     519            1 :             pb(r#"{{t,NULL},{NULL,f}}"#),
     520            1 :             json!([[true, null], [null, false]])
     521              :         );
     522            1 :     }
     523              : 
     524              :     #[test]
     525            1 :     fn test_pg_array_parse_numbers() {
     526            9 :         fn pn(pg_arr: &str, ty: &Type) -> Value {
     527            9 :             pg_array_parse(pg_arr, ty)
     528            9 :         }
     529            1 :         assert_eq!(pn(r#"{1,2,3}"#, &Type::INT4), json!([1, 2, 3]));
     530            1 :         assert_eq!(pn(r#"{1,2,3}"#, &Type::INT2), json!([1, 2, 3]));
     531            1 :         assert_eq!(pn(r#"{1,2,3}"#, &Type::INT8), json!(["1", "2", "3"]));
     532            1 :         assert_eq!(pn(r#"{1,2,3}"#, &Type::FLOAT4), json!([1.0, 2.0, 3.0]));
     533            1 :         assert_eq!(pn(r#"{1,2,3}"#, &Type::FLOAT8), json!([1.0, 2.0, 3.0]));
     534            1 :         assert_eq!(
     535            1 :             pn(r#"{1.1,2.2,3.3}"#, &Type::FLOAT4),
     536            1 :             json!([1.1, 2.2, 3.3])
     537              :         );
     538            1 :         assert_eq!(
     539            1 :             pn(r#"{1.1,2.2,3.3}"#, &Type::FLOAT8),
     540            1 :             json!([1.1, 2.2, 3.3])
     541              :         );
     542            1 :         assert_eq!(
     543            1 :             pn(r#"{NaN,Infinity,-Infinity}"#, &Type::FLOAT4),
     544            1 :             json!(["NaN", "Infinity", "-Infinity"])
     545              :         );
     546            1 :         assert_eq!(
     547            1 :             pn(r#"{NaN,Infinity,-Infinity}"#, &Type::FLOAT8),
     548            1 :             json!(["NaN", "Infinity", "-Infinity"])
     549              :         );
     550            1 :     }
     551              : 
     552              :     #[test]
     553            1 :     fn test_pg_array_with_decoration() {
     554            1 :         fn p(pg_arr: &str) -> Value {
     555            1 :             pg_array_parse(pg_arr, &Type::INT2)
     556            1 :         }
     557            1 :         assert_eq!(
     558            1 :             p(r#"[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}"#),
     559            1 :             json!([[[1, 2, 3], [4, 5, 6]]])
     560              :         );
     561            1 :     }
     562              : 
     563              :     #[test]
     564            1 :     fn test_pg_array_parse_json() {
     565            4 :         fn pt(pg_arr: &str) -> Value {
     566            4 :             pg_array_parse(pg_arr, &Type::JSONB)
     567            4 :         }
     568            1 :         assert_eq!(pt(r#"{"{}"}"#), json!([{}]));
     569            1 :         assert_eq!(
     570            1 :             pt(r#"{"{\"foo\": 1, \"bar\": 2}"}"#),
     571            1 :             json!([{"foo": 1, "bar": 2}])
     572              :         );
     573            1 :         assert_eq!(
     574            1 :             pt(r#"{"{\"foo\": 1}", "{\"bar\": 2}"}"#),
     575            1 :             json!([{"foo": 1}, {"bar": 2}])
     576              :         );
     577            1 :         assert_eq!(
     578            1 :             pt(r#"{{"{\"foo\": 1}", "{\"bar\": 2}"}}"#),
     579            1 :             json!([[{"foo": 1}, {"bar": 2}]])
     580              :         );
     581            1 :     }
     582              : }
        

Generated by: LCOV version 2.1-beta