sfackler / rust-postgres

Native PostgreSQL driver for the Rust programming language
Apache License 2.0
3.43k stars 436 forks source link

Sending text array (two dimension) #1169

Closed gabrieligbastos closed 1 month ago

gabrieligbastos commented 1 month ago

I have a function like:

create function check_parameter_2(p_payload text[])
    returns TABLE(id uuid, name text, value text)
    language plpgsql
as
$$
    DECLARE
    BEGIN
        -- Validate and insert payload
        FOR i IN 1..COALESCE(array_length(p_payload, 1), 0) LOOP
            DECLARE
                v_graph_node_field_id UUID;
                v_graph_node_field_name TEXT;
                v_command_field_value TEXT;
            BEGIN
                v_graph_node_field_name := p_payload[i][1]::TEXT;
                v_command_field_value := p_payload[i][2]::TEXT;

                SELECT gnf.id
                INTO v_graph_node_field_id
                FROM saga.graph_node_field gnf
                WHERE gnf.graph_node_id = 'c4ea80a0-cf9a-448a-99b1-e337d9daf053' AND gnf.name = v_graph_node_field_name;

                -- Return the result
                RETURN QUERY SELECT
                    v_graph_node_field_id,
                    v_graph_node_field_name,
                    v_command_field_value;
            END;
        END LOOP;
    END;
    $$;

I tried a lot of different things to make the rust call it and it works. The final workaround was to create a wrapper like:

CREATE OR REPLACE FUNCTION public.check_parameter_6(p_payload TEXT)
    RETURNS TABLE (
      id UUID,
      name TEXT,
      value TEXT
    ) AS $$
    DECLARE
    BEGIN
        RETURN QUERY SELECT * FROM public.check_parameter_2(p_payload::TEXT[]);
    END;
    $$ LANGUAGE plpgsql;

Any solution to make it able to call directly? Some of things i tried, first i created a own class extending HashMap

#[derive(Debug, Serialize, Deserialize)]
pub struct DatabasePayload(pub HashMap<String, String>);

Then i implemented ToSql in it, trying some options, the first was:

impl ToSql for DatabasePayload {
    fn to_sql(&self, ty: &Type, out: &mut BytesMut, ) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
        // Convert the HashMap to the required format: "{{\"uuid_key\",\"value\"}, .. , {\"uuid_key\",\"value\"}}"
        let mut array_elements = Vec::new();
        for (key, value) in &self.0 {
            array_elements.push(format!("{{\"{}\",\"{}\"}}", key, value));
        }
        let array_string = format!("{{{}}}", array_elements.join(","));
        info!("Sending string as: {}", array_string);
        out.extend_from_slice(array_string.as_bytes());
        Ok(IsNull::No)

    fn accepts(ty: &Type) -> bool {
        info!("Asking for: {}", ty.name());
        matches!(ty.name().to_lowercase().as_str(), "text[]" | "_text" | "text")
    }

    to_sql_checked!();
}

This it was saying the max lenght (293838793) of array was higher than the (6) allowed. Then i tried, using this repo as base, to implement a custom array writer

let member_type = match *ty.kind() {
            Kind::Array(ref member) => member,
            _ => return Err("expected array type".into()),
        };

        // let elements: Vec<(String, String)> = self.0.iter()
        //     .map(|(key, value)| (key, value))
        //     // .map(|(key, value)| format!("{{\"{}\",\"{}\"}}", escape_string(key), escape_string(value)))
        //     .collect();

        let dimension = types::ArrayDimension {
            len: self.0.len() as i32,
            lower_bound: 1,
        };
        let inner_dimension = types::ArrayDimension {
            len: 2,
            lower_bound: 1,
        };

        own_array_to_sql(
            Some(dimension),
            member_type.oid(),
            self.0.iter(),
            |e, inner_out| {
                let key = escape_string(e.0);
                let value = escape_string(e.1);
                let formatted_item = format!("{{{},{}}}", key, value);
                let bytes = formatted_item.as_bytes();

                // Log the formatted item and its byte representation
                info!("Formatted item: {}", formatted_item);
                info!("Bytes being sent: {:?}", bytes);

                inner_out.extend_from_slice(bytes);

                info!("Sending key: {} and value: {}", e.0, e.1);
                Ok(IsNull::No)
            },
            out,
        )?;

        Ok(IsNull::No)

something like this, but it was either saying that im trying to send array of type that was not expected, or it sends but getting all null, because it when send it was wrapping my data into quotes, so could never make it work.

any help on this so i get rid of the workaround function? thanks

gabrieligbastos commented 1 month ago

I tried to understand what was being sent byte by byte, was this:

Decoded string: "B\0\0\0\0\0s10\0\0\u{1}\0\u{1}\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\u{19}\0\0\0\n\0\0\0\u{1}\0\0\0\u{18}{should_fail_at_9,false}\0\0\0\u{19}{should_fail_at_10,false}\0\0\0\u{17}{should_fail_at_3,true}\0\0\0\u{18}{should_fail_at_2,false}\0\0\0\u{18}{should_fail_at_7,false}\0\0\0\u{18}{should_fail_at_6,false}\0\0\0\u{18}{should_fail_at_4,false}\0\0\0\u{18}{should_fail_at_5,false}\0\0\0\u{19}{should_fail_at_11,false}\0\0\0\0{should_fail_at_8,false}"
Byte: 42, Char: 'B'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 73, Char: 's'
Byte: 31, Char: '1'
Byte: 30, Char: '0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 01, Char: '\u{1}'
Byte: 00, Char: '\0'
Byte: 01, Char: '\u{1}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 01, Char: '\u{1}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 19, Char: '\u{19}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 0a, Char: '\n'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 01, Char: '\u{1}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 18, Char: '\u{18}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 39, Char: '9'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 19, Char: '\u{19}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 31, Char: '1'
Byte: 30, Char: '0'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 17, Char: '\u{17}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 33, Char: '3'
Byte: 2c, Char: ','
Byte: 74, Char: 't'
Byte: 72, Char: 'r'
Byte: 75, Char: 'u'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 18, Char: '\u{18}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 32, Char: '2'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 18, Char: '\u{18}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 37, Char: '7'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 18, Char: '\u{18}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 36, Char: '6'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 18, Char: '\u{18}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 34, Char: '4'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 18, Char: '\u{18}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 35, Char: '5'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 19, Char: '\u{19}'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 31, Char: '1'
Byte: 31, Char: '1'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 00, Char: '\0'
Byte: 7b, Char: '{'
Byte: 73, Char: 's'
Byte: 68, Char: 'h'
Byte: 6f, Char: 'o'
Byte: 75, Char: 'u'
Byte: 6c, Char: 'l'
Byte: 64, Char: 'd'
Byte: 5f, Char: '_'
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 69, Char: 'i'
Byte: 6c, Char: 'l'
Byte: 5f, Char: '_'
Byte: 61, Char: 'a'
Byte: 74, Char: 't'
Byte: 5f, Char: '_'
Byte: 38, Char: '8'
Byte: 2c, Char: ','
Byte: 66, Char: 'f'
Byte: 61, Char: 'a'
Byte: 6c, Char: 'l'
Byte: 73, Char: 's'
Byte: 65, Char: 'e'
Byte: 7d, Char: '}'
sfackler commented 1 month ago

ToSql is expected to return the value encoded in Postgres's binary protocol, not the text protocol. https://github.com/sfackler/rust-postgres-array can help make that easier to work with.

gabrieligbastos commented 1 month ago

Made it work at the other repo you said @sfackler thanks one more time in here..

For information of others, if get in this trouble. https://github.com/sfackler/rust-postgres-array/issues/14