Try to create a [`SqlServerRowDecoder`] that will decode [`tiberius::Row`]s that match the shape of the provided [`SqlServerTableDesc`], to [`mz_repr::Row`]s that match the shape of the provided [`RelationDesc`].
(
table: &SqlServerTableDesc,
desc: &RelationDesc,
)
| 1140 | /// the shape of the provided [`SqlServerTableDesc`], to [`mz_repr::Row`]s that match the |
| 1141 | /// shape of the provided [`RelationDesc`]. |
| 1142 | pub fn try_new( |
| 1143 | table: &SqlServerTableDesc, |
| 1144 | desc: &RelationDesc, |
| 1145 | ) -> Result<Self, SqlServerError> { |
| 1146 | let decoders = desc |
| 1147 | .iter() |
| 1148 | .map(|(col_name, col_type)| { |
| 1149 | let sql_server_col = table |
| 1150 | .columns |
| 1151 | .iter() |
| 1152 | .find(|col| col.name.as_ref() == col_name.as_str()) |
| 1153 | .ok_or_else(|| { |
| 1154 | // TODO(sql_server2): Structured Error. |
| 1155 | anyhow::anyhow!("no SQL Server column with name {col_name} found") |
| 1156 | })?; |
| 1157 | let Some(sql_server_col_typ) = sql_server_col.column_type.as_ref() else { |
| 1158 | return Err(SqlServerError::ProgrammingError(format!( |
| 1159 | "programming error, {col_name} should have been exluded", |
| 1160 | ))); |
| 1161 | }; |
| 1162 | |
| 1163 | // This shouldn't be true, but be defensive. |
| 1164 | // |
| 1165 | // TODO(sql_server2): Maybe allow the Materialize column type to be |
| 1166 | // more nullable than our decoding type? |
| 1167 | // |
| 1168 | // Sad. Our timestamp types don't roundtrip their precision through |
| 1169 | // parsing so we ignore the mismatch here. |
| 1170 | let matches = match (&sql_server_col_typ.scalar_type, &col_type.scalar_type) { |
| 1171 | (SqlScalarType::Timestamp { .. }, SqlScalarType::Timestamp { .. }) |
| 1172 | | (SqlScalarType::TimestampTz { .. }, SqlScalarType::TimestampTz { .. }) => { |
| 1173 | // Types match so check nullability. |
| 1174 | sql_server_col_typ.nullable == col_type.nullable |
| 1175 | } |
| 1176 | (_, _) => sql_server_col_typ == col_type, |
| 1177 | }; |
| 1178 | if !matches { |
| 1179 | return Err(SqlServerError::ProgrammingError(format!( |
| 1180 | "programming error, {col_name} has mismatched type {:?} vs {:?}", |
| 1181 | sql_server_col.column_type, col_type |
| 1182 | ))); |
| 1183 | } |
| 1184 | |
| 1185 | let name = Arc::clone(&sql_server_col.name); |
| 1186 | let decoder = sql_server_col.decode_type.clone(); |
| 1187 | // Note: We specifically use the `SqlColumnType` from the SqlServerTableDesc |
| 1188 | // because it retains precision. |
| 1189 | // |
| 1190 | // See: <https://github.com/MaterializeInc/database-issues/issues/3179>. |
| 1191 | let col_typ = sql_server_col_typ.clone(); |
| 1192 | |
| 1193 | Ok::<_, SqlServerError>((name, col_typ, decoder)) |
| 1194 | }) |
| 1195 | .collect::<Result<_, _>>()?; |
| 1196 | |
| 1197 | Ok(SqlServerRowDecoder { decoders }) |
| 1198 | } |
| 1199 |