| 100 | } |
| 101 | |
| 102 | fn encode_copy_row_csv( |
| 103 | CopyCsvFormatParams { |
| 104 | delimiter: delim, |
| 105 | quote, |
| 106 | escape, |
| 107 | header: _, |
| 108 | null, |
| 109 | }: &CopyCsvFormatParams, |
| 110 | row: &RowRef, |
| 111 | typ: &SqlRelationType, |
| 112 | out: &mut Vec<u8>, |
| 113 | ) -> Result<(), io::Error> { |
| 114 | let null = null.as_bytes(); |
| 115 | let is_special = |c: &u8| *c == *delim || *c == *quote || *c == b'\r' || *c == b'\n'; |
| 116 | let mut buf = BytesMut::new(); |
| 117 | for (idx, field) in mz_pgrepr::values_from_row(row, typ).into_iter().enumerate() { |
| 118 | if idx > 0 { |
| 119 | out.push(*delim); |
| 120 | } |
| 121 | match field { |
| 122 | None => out.extend(null), |
| 123 | Some(field) => { |
| 124 | buf.clear(); |
| 125 | field.encode_text(&mut buf); |
| 126 | // A field needs quoting if: |
| 127 | // * It is the only field and the value is exactly the end |
| 128 | // of copy marker. |
| 129 | // * The field contains a special character. |
| 130 | // * The field is exactly the NULL sentinel. |
| 131 | if (typ.column_types.len() == 1 && buf == END_OF_COPY_MARKER) |
| 132 | || buf.iter().any(is_special) |
| 133 | || &*buf == null |
| 134 | { |
| 135 | // Quote the value by wrapping it in the quote character and |
| 136 | // emitting the escape character before any quote or escape |
| 137 | // characters within. |
| 138 | out.push(*quote); |
| 139 | for b in &buf { |
| 140 | if *b == *quote || *b == *escape { |
| 141 | out.push(*escape); |
| 142 | } |
| 143 | out.push(*b); |
| 144 | } |
| 145 | out.push(*quote); |
| 146 | } else { |
| 147 | // The value does not need quoting and can be emitted |
| 148 | // directly. |
| 149 | out.extend(&buf); |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | out.push(b'\n'); |
| 155 | Ok(()) |
| 156 | } |
| 157 | |
| 158 | pub struct CopyTextFormatParser<'a> { |
| 159 | data: &'a [u8], |