diff --git a/crates/agent/src/integration_tests/harness/connectors.rs b/crates/agent/src/integration_tests/harness/connectors.rs index 8629edf271a..6c1dfd46491 100644 --- a/crates/agent/src/integration_tests/harness/connectors.rs +++ b/crates/agent/src/integration_tests/harness/connectors.rs @@ -211,26 +211,25 @@ impl Connectors for TestConnectors { .get("deltaUpdates") .and_then(|d| d.as_bool()) .unwrap_or(false); - let constraints = collection + let projection_constraints = collection .projections .iter() - .map(|p| { - ( - p.field.clone(), + .map(|p| materialize::response::validated::ProjectionConstraint { + field: p.field.clone(), + constraint: Some( materialize::response::validated::Constraint { r#type: 3, reason: "all fields are recommended in tests" .to_string(), folded_field: String::new(), }, - ) + ), }) .collect(); let resource_path = mock_resource_path(&resource_config); materialize::response::validated::Binding { case_insensitive_fields: false, - constraints, - projection_constraints: Vec::new(), + projection_constraints, resource_path, delta_updates, ser_policy: None, diff --git a/crates/dekaf-connector/src/lib.rs b/crates/dekaf-connector/src/lib.rs index 63847f82238..7887b064266 100644 --- a/crates/dekaf-connector/src/lib.rs +++ b/crates/dekaf-connector/src/lib.rs @@ -6,7 +6,6 @@ use proto_flow::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Copy)] #[serde(rename_all = "snake_case")] @@ -135,25 +134,25 @@ where parsed_outer_config.variant.clone() ))?; - let constraints = binding + let projection_constraints = binding .collection .context("collection must exist")? .projections .iter() - .map(|projection| { - ( - projection.field.clone(), - constraint_for_projection(&projection, &parsed_inner_config), - ) + .map(|projection| validated::ProjectionConstraint { + field: projection.field.clone(), + constraint: Some(constraint_for_projection( + &projection, + &parsed_inner_config, + )), }) - .collect::>(); + .collect::>(); Ok::( validated::Binding { case_insensitive_fields: false, - constraints, delta_updates: true, - projection_constraints: Vec::new(), + projection_constraints, resource_path: vec![resource_config.topic_name], ser_policy: None, }, diff --git a/crates/flow-web/FIELD_SELECTION.md b/crates/flow-web/FIELD_SELECTION.md index 98299e992df..539780e87c8 100644 --- a/crates/flow-web/FIELD_SELECTION.md +++ b/crates/flow-web/FIELD_SELECTION.md @@ -117,10 +117,10 @@ const input = { }, validated: { resourcePath: ["users"], - constraints: { - "id": { type: "FIELD_REQUIRED", reason: "Primary key" }, - "name": { type: "FIELD_OPTIONAL", reason: "User data" } - }, + projectionConstraints: [ + { field: "id", constraint: { type: "FIELD_REQUIRED", reason: "Primary key" } }, + { field: "name", constraint: { type: "FIELD_OPTIONAL", reason: "User data" } } + ], caseInsensitiveFields: false } } diff --git a/crates/flow-web/tests/web.rs b/crates/flow-web/tests/web.rs index 8c197a6eb08..1e5a9888d54 100644 --- a/crates/flow-web/tests/web.rs +++ b/crates/flow-web/tests/web.rs @@ -122,24 +122,36 @@ fn test_field_selection() { }, "validated": { "resourcePath": ["test_table"], - "constraints": { - "id": { - "type": "FIELD_OPTIONAL", - "reason": "Available field" + "projectionConstraints": [ + { + "field": "id", + "constraint": { + "type": "FIELD_OPTIONAL", + "reason": "Available field" + } }, - "flow_published_at": { - "type": "FIELD_OPTIONAL", - "reason": "Available field" + { + "field": "flow_published_at", + "constraint": { + "type": "FIELD_OPTIONAL", + "reason": "Available field" + } }, - "value": { - "type": "FIELD_OPTIONAL", - "reason": "Available field" + { + "field": "value", + "constraint": { + "type": "FIELD_OPTIONAL", + "reason": "Available field" + } }, - "bad": { - "type": "FIELD_FORBIDDEN", - "reason": "Not today, pal." + { + "field": "bad", + "constraint": { + "type": "FIELD_FORBIDDEN", + "reason": "Not today, pal." + } } - }, + ], "deltaUpdates": false } } diff --git a/crates/proto-flow/src/materialize.rs b/crates/proto-flow/src/materialize.rs index b3be5608d6e..e71ecadc606 100644 --- a/crates/proto-flow/src/materialize.rs +++ b/crates/proto-flow/src/materialize.rs @@ -313,8 +313,8 @@ pub mod response { /// Nested message and enum types in `Validated`. pub mod validated { /// ProjectionConstraint pairs a projection field name with a single Constraint. - /// Unlike the legacy `constraints` map, a repeated list of ProjectionConstraint - /// allows multiple constraints to be expressed for the same field simultaneously. + /// A repeated list of ProjectionConstraint allows multiple constraints to be + /// expressed for the same field simultaneously. /// For example, INCOMPATIBLE and LOCATION_REQUIRED on the same field signals that /// the field is required but the existing destination column has an incompatible /// type, and a backfill is needed. @@ -453,17 +453,6 @@ pub mod response { /// (for example) "myField" and "MyField" from co-existing within a table. #[prost(bool, tag = "5")] pub case_insensitive_fields: bool, - /// Constraints imposed by the connector, keyed by field name. - /// Projections of the CollectionSpec which are missing from - /// constraints are implicitly forbidden. - /// - /// Deprecated: use projection_constraints instead. When projection_constraints - /// is non-empty it is authoritative and this map is ignored by the control - /// plane. This field is retained for backward compatibility with connectors - /// that have not yet migrated to the list form. - #[prost(btree_map = "string, message", tag = "1")] - pub constraints: - ::prost::alloc::collections::BTreeMap<::prost::alloc::string::String, Constraint>, /// Components of the resource path which fully qualify the resource /// identified by this binding. /// @@ -493,12 +482,11 @@ pub mod response { #[prost(message, optional, tag = "4")] pub ser_policy: ::core::option::Option, /// Constraints on each projection, as a list that allows multiple constraints - /// per projection field. When non-empty, this field is authoritative and the - /// legacy `constraints` map is ignored. + /// per projection field. Projections of the CollectionSpec which are missing + /// from this list are implicitly forbidden. /// - /// Connectors should populate this field instead of `constraints`. The list - /// form allows expressing compound requirements that a single constraint type - /// cannot capture. For example, emitting both INCOMPATIBLE and + /// The list form allows expressing compound requirements that a single + /// constraint type cannot capture. For example, emitting both INCOMPATIBLE and /// LOCATION_REQUIRED for the same field signals that the field is required /// but the existing destination column is incompatible; a backfill is required. #[prost(message, repeated, tag = "6")] diff --git a/crates/proto-flow/src/materialize.serde.rs b/crates/proto-flow/src/materialize.serde.rs index 24be109e786..8179357dd2c 100644 --- a/crates/proto-flow/src/materialize.serde.rs +++ b/crates/proto-flow/src/materialize.serde.rs @@ -3208,9 +3208,6 @@ impl serde::Serialize for response::validated::Binding { if self.case_insensitive_fields { len += 1; } - if !self.constraints.is_empty() { - len += 1; - } if !self.resource_path.is_empty() { len += 1; } @@ -3227,9 +3224,6 @@ impl serde::Serialize for response::validated::Binding { if self.case_insensitive_fields { struct_ser.serialize_field("caseInsensitiveFields", &self.case_insensitive_fields)?; } - if !self.constraints.is_empty() { - struct_ser.serialize_field("constraints", &self.constraints)?; - } if !self.resource_path.is_empty() { struct_ser.serialize_field("resourcePath", &self.resource_path)?; } @@ -3254,7 +3248,6 @@ impl<'de> serde::Deserialize<'de> for response::validated::Binding { const FIELDS: &[&str] = &[ "case_insensitive_fields", "caseInsensitiveFields", - "constraints", "resource_path", "resourcePath", "delta_updates", @@ -3268,7 +3261,6 @@ impl<'de> serde::Deserialize<'de> for response::validated::Binding { #[allow(clippy::enum_variant_names)] enum GeneratedField { CaseInsensitiveFields, - Constraints, ResourcePath, DeltaUpdates, SerPolicy, @@ -3296,7 +3288,6 @@ impl<'de> serde::Deserialize<'de> for response::validated::Binding { { match value { "caseInsensitiveFields" | "case_insensitive_fields" => Ok(GeneratedField::CaseInsensitiveFields), - "constraints" => Ok(GeneratedField::Constraints), "resourcePath" | "resource_path" => Ok(GeneratedField::ResourcePath), "deltaUpdates" | "delta_updates" => Ok(GeneratedField::DeltaUpdates), "serPolicy" | "ser_policy" => Ok(GeneratedField::SerPolicy), @@ -3321,7 +3312,6 @@ impl<'de> serde::Deserialize<'de> for response::validated::Binding { V: serde::de::MapAccess<'de>, { let mut case_insensitive_fields__ = None; - let mut constraints__ = None; let mut resource_path__ = None; let mut delta_updates__ = None; let mut ser_policy__ = None; @@ -3334,14 +3324,6 @@ impl<'de> serde::Deserialize<'de> for response::validated::Binding { } case_insensitive_fields__ = Some(map_.next_value()?); } - GeneratedField::Constraints => { - if constraints__.is_some() { - return Err(serde::de::Error::duplicate_field("constraints")); - } - constraints__ = Some( - map_.next_value::>()? - ); - } GeneratedField::ResourcePath => { if resource_path__.is_some() { return Err(serde::de::Error::duplicate_field("resourcePath")); @@ -3373,7 +3355,6 @@ impl<'de> serde::Deserialize<'de> for response::validated::Binding { } Ok(response::validated::Binding { case_insensitive_fields: case_insensitive_fields__.unwrap_or_default(), - constraints: constraints__.unwrap_or_default(), resource_path: resource_path__.unwrap_or_default(), delta_updates: delta_updates__.unwrap_or_default(), ser_policy: ser_policy__, diff --git a/crates/proto-flow/tests/regression.rs b/crates/proto-flow/tests/regression.rs index bd82128705a..9558a9fd706 100644 --- a/crates/proto-flow/tests/regression.rs +++ b/crates/proto-flow/tests/regression.rs @@ -668,29 +668,6 @@ fn ex_materialize_response() -> materialize::Response { bindings: vec![materialize::response::validated::Binding { resource_path: vec!["some".to_string(), "path".to_string()], case_insensitive_fields: true, - constraints: [ - ( - "req_field".to_string(), - materialize::response::validated::Constraint { - r#type: - materialize::response::validated::constraint::Type::FieldRequired - as i32, - reason: "is required".to_string(), - folded_field: "REQ_FIELD".to_string(), - }, - ), - ( - "opt_field".to_string(), - materialize::response::validated::Constraint { - r#type: - materialize::response::validated::constraint::Type::FieldOptional - as i32, - reason: "is optional".to_string(), - folded_field: String::new(), - }, - ), - ] - .into(), projection_constraints: vec![ materialize::response::validated::ProjectionConstraint { field: "flow_document".to_string(), diff --git a/crates/proto-flow/tests/snapshots/regression__materialize_response_json.snap b/crates/proto-flow/tests/snapshots/regression__materialize_response_json.snap index 35380987419..681088812e1 100644 --- a/crates/proto-flow/tests/snapshots/regression__materialize_response_json.snap +++ b/crates/proto-flow/tests/snapshots/regression__materialize_response_json.snap @@ -37,17 +37,6 @@ expression: json_test(msg) "bindings": [ { "caseInsensitiveFields": true, - "constraints": { - "opt_field": { - "type": "FIELD_OPTIONAL", - "reason": "is optional" - }, - "req_field": { - "type": "FIELD_REQUIRED", - "reason": "is required", - "foldedField": "REQ_FIELD" - } - }, "resourcePath": [ "some", "path" diff --git a/crates/proto-flow/tests/snapshots/regression__materialize_response_proto.snap b/crates/proto-flow/tests/snapshots/regression__materialize_response_proto.snap index 49a02f1c76a..b9c5517199d 100644 --- a/crates/proto-flow/tests/snapshots/regression__materialize_response_proto.snap +++ b/crates/proto-flow/tests/snapshots/regression__materialize_response_proto.snap @@ -21,35 +21,31 @@ expression: proto_test(msg) |65651211 7b227265 66726573 68223a22| ee..{"refresh":" 00000100 |68647222 7d52100a 06616363 65737312| hdr"}R...access. 00000110 |06226865 7265225a 04504f53 54620450| ."here"Z.POSTb.P 00000120 -|4f535412 e2010adf 010a1c0a 096f7074| OST..........opt 00000130 -|5f666965 6c64120f 10041a0b 6973206f| _field......is o 00000140 -|7074696f 6e616c0a 270a0972 65715f66| ptional.'..req_f 00000150 -|69656c64 121a1001 1a0b6973 20726571| ield......is req 00000160 -|75697265 64220952 45515f46 49454c44| uired".REQ_FIELD 00000170 -|1204736f 6d651204 70617468 1801220a| ..some..path..". 00000180 -|08808004 10e80718 e8072801 323b0a0d| ..........(.2;.. 00000190 -|666c6f77 5f646f63 756d656e 74122a10| flow_document.*. 000001a0 -|021a2674 68652072 6f6f7420 646f6375| ..&the root docu 000001b0 -|6d656e74 206d7573 74206265 206d6174| ment must be mat 000001c0 -|65726961 6c697a65 64323d0a 0d666c6f| erialized2=..flo 000001d0 -|775f646f 63756d65 6e74122c 10061a28| w_document.,...( 000001e0 -|65786973 74696e67 20636f6c 756d6e20| existing column 000001f0 -|68617320 616e2069 6e636f6d 70617469| has an incompati 00000200 -|626c6520 74797065 1a2a0a10 49206469| ble type.*..I di 00000210 -|6420736f 6d652073 74756666 12160a12| d some stuff.... 00000220 -|7b227374 61746522 3a227570 64617465| {"state":"update 00000230 -|227d1001 22680a64 0a4a0a15 612f7265| "}.."h.d.J..a/re 00000240 -|61642f6a 6f75726e 616c3b73 75666669| ad/journal;suffi 00000250 -|78123108 b9601215 0a050309 08050712| x.1..`.......... 00000260 -|0c09e321 00000000 000010d7 0812150a| ...!............ 00000270 -|05070c66 2b1d120c 09350100 00000000| ...f+....5...... 00000280 -|0010ae11 12160a0e 616e2f61 636b2f6a| ........an/ack/j 00000290 -|6f75726e 616c1204 03040205 10012a14| ournal........*. 000002a0 -|08041210 7b226c6f 61646564 223a2264| ....{"loaded":"d 000002b0 -|6f63227d 32180a16 0a127b22 73746174| oc"}2.....{"stat 000002c0 -|65223a22 75706461 7465227d 10013a18| e":"update"}..:. 000002d0 -|0a160a12 7b227374 61746522 3a227570| ....{"state":"up 000002e0 -|64617465 227d1001 42180a16 0a127b22| date"}..B.....{" 000002f0 -|73746174 65223a22 75706461 7465227d| state":"update"} 00000300 -|1001a206 06120248 691801| .......Hi.. 00000310 - 0000031b +|4f535412 9b010a98 01120473 6f6d6512| OST........some. 00000130 +|04706174 68180122 0a088080 0410e807| .path.."........ 00000140 +|18e80728 01323b0a 0d666c6f 775f646f| ...(.2;..flow_do 00000150 +|63756d65 6e74122a 10021a26 74686520| cument.*...&the 00000160 +|726f6f74 20646f63 756d656e 74206d75| root document mu 00000170 +|73742062 65206d61 74657269 616c697a| st be materializ 00000180 +|6564323d 0a0d666c 6f775f64 6f63756d| ed2=..flow_docum 00000190 +|656e7412 2c10061a 28657869 7374696e| ent.,...(existin 000001a0 +|6720636f 6c756d6e 20686173 20616e20| g column has an 000001b0 +|696e636f 6d706174 69626c65 20747970| incompatible typ 000001c0 +|651a2a0a 10492064 69642073 6f6d6520| e.*..I did some 000001d0 +|73747566 6612160a 127b2273 74617465| stuff....{"state 000001e0 +|223a2275 70646174 65227d10 0122680a| ":"update"}.."h. 000001f0 +|640a4a0a 15612f72 6561642f 6a6f7572| d.J..a/read/jour 00000200 +|6e616c3b 73756666 69781231 08b96012| nal;suffix.1..`. 00000210 +|150a0503 09080507 120c09e3 21000000| ............!... 00000220 +|00000010 d7081215 0a05070c 662b1d12| ............f+.. 00000230 +|0c093501 00000000 000010ae 1112160a| ..5............. 00000240 +|0e616e2f 61636b2f 6a6f7572 6e616c12| .an/ack/journal. 00000250 +|04030402 0510012a 14080412 107b226c| .......*.....{"l 00000260 +|6f616465 64223a22 646f6322 7d32180a| oaded":"doc"}2.. 00000270 +|160a127b 22737461 7465223a 22757064| ...{"state":"upd 00000280 +|61746522 7d10013a 180a160a 127b2273| ate"}..:.....{"s 00000290 +|74617465 223a2275 70646174 65227d10| tate":"update"}. 000002a0 +|0142180a 160a127b 22737461 7465223a| .B.....{"state": 000002b0 +|22757064 61746522 7d1001a2 06061202| "update"}....... 000002c0 +|48691801| Hi.. 000002d0 + 000002d4 diff --git a/crates/validation/src/field_selection.rs b/crates/validation/src/field_selection.rs index 788c614f84b..4fb4ddad0ce 100644 --- a/crates/validation/src/field_selection.rs +++ b/crates/validation/src/field_selection.rs @@ -5,33 +5,24 @@ use proto_flow::{flow, materialize}; use std::collections::BTreeMap; use tables::EitherOrBoth as EOB; -/// Normalize the legacy `constraints` map and the new `projection_constraints` -/// list into a canonical per-field vector. When `projection_constraints` is -/// non-empty it is authoritative and the map is ignored. Multiple entries with -/// the same field name are preserved so that compound signals (e.g. -/// INCOMPATIBLE + LOCATION_REQUIRED) are visible to field selection. +/// Normalize the `projection_constraints` list into a canonical per-field +/// vector. Multiple entries with the same field name are preserved so that +/// compound signals (e.g. INCOMPATIBLE + LOCATION_REQUIRED) are visible to +/// field selection. pub fn normalize_constraints( binding: &materialize::response::validated::Binding, ) -> BTreeMap> { - if !binding.projection_constraints.is_empty() { - let mut out: BTreeMap> = BTreeMap::new(); - for pc in &binding.projection_constraints { - // Entries with a missing constraint are skipped here; - // `walk_materialization` separately reports them as a connector - // error during validation. - let Some(c) = pc.constraint.as_ref() else { - continue; - }; - out.entry(pc.field.clone()).or_default().push(c.clone()); - } - out - } else { - binding - .constraints - .iter() - .map(|(f, c)| (f.clone(), vec![c.clone()])) - .collect() + let mut out: BTreeMap> = BTreeMap::new(); + for pc in &binding.projection_constraints { + // Entries with a missing constraint are skipped here; + // `walk_materialization` separately reports them as a connector + // error during validation. + let Some(c) = pc.constraint.as_ref() else { + continue; + }; + out.entry(pc.field.clone()).or_default().push(c.clone()); } + out } /// Select is a rationale for including a field in selection. diff --git a/crates/validation/src/noop.rs b/crates/validation/src/noop.rs index 8ea8b227abc..588a27d594e 100644 --- a/crates/validation/src/noop.rs +++ b/crates/validation/src/noop.rs @@ -109,27 +109,27 @@ impl Connectors for NoOpConnectors { .map(|(i, binding)| { // Return FIELD_OPTIONAL for every collection projection // so that field selection validation succeeds. - let constraints = binding + let projection_constraints = binding .collection .as_ref() .map(|c| &c.projections) .into_iter() .flatten() .map(|p| { - ( - p.field.clone(), - materialize::response::validated::Constraint { + materialize::response::validated::ProjectionConstraint { + field: p.field.clone(), + constraint: Some(materialize::response::validated::Constraint { r#type: materialize::response::validated::constraint::Type::FieldOptional as i32, reason: String::new(), folded_field: String::new(), - }, - ) + }), + } }) .collect(); materialize::response::validated::Binding { resource_path: vec![format!("binding-{i}")], - constraints, + projection_constraints, ..Default::default() } }) diff --git a/crates/validation/tests/common.rs b/crates/validation/tests/common.rs index 8b3feefad92..35bfb1e840c 100644 --- a/crates/validation/tests/common.rs +++ b/crates/validation/tests/common.rs @@ -958,24 +958,30 @@ impl validation::Connectors for MockDriverCalls { .iter() .take(validate.bindings.len()) .map(|b| { - let mut out = materialize::response::validated::Binding { + let projection_constraints = b + .constraints + .iter() + .map(|(field, constraint)| { + let mut constraint = constraint.clone(); + // NOTE(johnny): clunky support for test_materialization_driver_unknown_constraints, + // to work around serde deser not allowing parsing of invalid enum values. + if constraint.r#type == 0 && b.type_override != 0 { + constraint.r#type = b.type_override; + } + materialize::response::validated::ProjectionConstraint { + field: field.clone(), + constraint: Some(constraint), + } + }) + .collect(); + + materialize::response::validated::Binding { case_insensitive_fields: b.case_insensitive_fields, - constraints: b.constraints.clone(), - projection_constraints: Vec::new(), + projection_constraints, delta_updates: call.delta_updates, resource_path: b.resource_path.clone(), ser_policy: None, - }; - - // NOTE(johnny): clunky support for test_materialization_driver_unknown_constraints, - // to work around serde deser not allowing parsing of invalid enum values. - for c in out.constraints.iter_mut() { - if c.1.r#type == 0 && b.type_override != 0 { - c.1.r#type = b.type_override; - } } - - out }) .collect(); diff --git a/crates/validation/tests/snapshots/materialization_collection_resets__bindings_are_backfilled_on_reset.snap b/crates/validation/tests/snapshots/materialization_collection_resets__bindings_are_backfilled_on_reset.snap index ab5380d7a99..6748046a37f 100644 --- a/crates/validation/tests/snapshots/materialization_collection_resets__bindings_are_backfilled_on_reset.snap +++ b/crates/validation/tests/snapshots/materialization_collection_resets__bindings_are_backfilled_on_reset.snap @@ -45,45 +45,63 @@ expression: outcome.built_materializations bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - "id": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "a", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "id", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - "id": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "b", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "id", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/materialization_collection_resets__reset_backfills_for_disable_task_and_binding.snap b/crates/validation/tests/snapshots/materialization_collection_resets__reset_backfills_for_disable_task_and_binding.snap index 39f354f4d76..f98766ae63c 100644 --- a/crates/validation/tests/snapshots/materialization_collection_resets__reset_backfills_for_disable_task_and_binding.snap +++ b/crates/validation/tests/snapshots/materialization_collection_resets__reset_backfills_for_disable_task_and_binding.snap @@ -46,45 +46,63 @@ expression: outcome.built_materializations bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - "id": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "a", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "id", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - "id": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "b", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "id", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap b/crates/validation/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap index 56ae98dc1d6..117a57e8f30 100644 --- a/crates/validation/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap +++ b/crates/validation/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap @@ -1945,24 +1945,33 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - "id": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - }, resource_path: [ "enabled-stream", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "id", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/scenario_tests__golden_all_visits.snap b/crates/validation/tests/snapshots/scenario_tests__golden_all_visits.snap index 976328c2be7..8c7bedb08f7 100644 --- a/crates/validation/tests/snapshots/scenario_tests__golden_all_visits.snap +++ b/crates/validation/tests/snapshots/scenario_tests__golden_all_visits.snap @@ -6451,24 +6451,33 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - "int": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - }, resource_path: [ "view", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "int", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ], }, ], }, @@ -7009,72 +7018,115 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "Int": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "bit": Constraint { - r#type: FieldForbidden, - reason: "field forbidden", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - "int": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "str": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - }, resource_path: [ "Web!hook", "foo bar", ], delta_updates: true, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "Int", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "bit", + constraint: Some( + Constraint { + r#type: FieldForbidden, + reason: "field forbidden", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "int", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "str", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ], }, Binding { case_insensitive_fields: false, - constraints: { - "Extra": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - "Len": Constraint { - r#type: LocationRecommended, - reason: "optional", - folded_field: "", - }, - "Root": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - "int": Constraint { - r#type: FieldRequired, - reason: "required", - folded_field: "", - }, - }, resource_path: [ "targe+", "two", ], delta_updates: true, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "Extra", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "Len", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "Root", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "int", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap b/crates/validation/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap index 394a13a24d8..b969cce3837 100644 --- a/crates/validation/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap +++ b/crates/validation/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap @@ -327,34 +327,53 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "flow_document": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - "id": Constraint { - r#type: FieldRequired, - reason: "location required", - folded_field: "", - }, - "naughty_f": Constraint { - r#type: FieldForbidden, - reason: "field forbidden", - folded_field: "", - }, - "naughty_u": Constraint { - r#type: Incompatible, - reason: "field incompatible", - folded_field: "", - }, - }, resource_path: [ "anything", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "id", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "naughty_f", + constraint: Some( + Constraint { + r#type: FieldForbidden, + reason: "field forbidden", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "naughty_u", + constraint: Some( + Constraint { + r#type: Incompatible, + reason: "field incompatible", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/transition_tests__inserts.snap b/crates/validation/tests/snapshots/transition_tests__inserts.snap index 88f5c7f0834..42ac2a0db38 100644 --- a/crates/validation/tests/snapshots/transition_tests__inserts.snap +++ b/crates/validation/tests/snapshots/transition_tests__inserts.snap @@ -1645,35 +1645,54 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "F1": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - "f_one": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "f_two": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "table", "path", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "F1", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_one", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_two", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/transition_tests__manual_redact_salt_override.snap b/crates/validation/tests/snapshots/transition_tests__manual_redact_salt_override.snap index ee09985c74f..b7793efec6f 100644 --- a/crates/validation/tests/snapshots/transition_tests__manual_redact_salt_override.snap +++ b/crates/validation/tests/snapshots/transition_tests__manual_redact_salt_override.snap @@ -1925,35 +1925,54 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "F1": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - "f_one": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "f_two": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "table", "path", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "F1", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_one", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_two", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/transition_tests__reset_materialization.snap b/crates/validation/tests/snapshots/transition_tests__reset_materialization.snap index 9b5ede5936a..3bfeea9367e 100644 --- a/crates/validation/tests/snapshots/transition_tests__reset_materialization.snap +++ b/crates/validation/tests/snapshots/transition_tests__reset_materialization.snap @@ -43,35 +43,54 @@ expression: outcome.built_materializations bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "F1": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - "f_one": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "f_two": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "table", "path", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "F1", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_one", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_two", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/transition_tests__reset_of_used_collection.snap b/crates/validation/tests/snapshots/transition_tests__reset_of_used_collection.snap index b217b45fc3b..6631421f95c 100644 --- a/crates/validation/tests/snapshots/transition_tests__reset_of_used_collection.snap +++ b/crates/validation/tests/snapshots/transition_tests__reset_of_used_collection.snap @@ -1932,35 +1932,54 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "F1": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - "f_one": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "f_two": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "table", "path", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "F1", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_one", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_two", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/transition_tests__updates.snap b/crates/validation/tests/snapshots/transition_tests__updates.snap index de61498d767..0fd2979a926 100644 --- a/crates/validation/tests/snapshots/transition_tests__updates.snap +++ b/crates/validation/tests/snapshots/transition_tests__updates.snap @@ -1923,35 +1923,54 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "F1": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - "f_one": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "f_two": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "table", "path", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "F1", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_one", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_two", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/crates/validation/tests/snapshots/transition_tests__updates_with_clobbered_meta_path.snap b/crates/validation/tests/snapshots/transition_tests__updates_with_clobbered_meta_path.snap index 8057a1678a3..d72cc9db6c7 100644 --- a/crates/validation/tests/snapshots/transition_tests__updates_with_clobbered_meta_path.snap +++ b/crates/validation/tests/snapshots/transition_tests__updates_with_clobbered_meta_path.snap @@ -1917,35 +1917,54 @@ Outcome { bindings: [ Binding { case_insensitive_fields: false, - constraints: { - "F1": Constraint { - r#type: FieldOptional, - reason: "optional", - folded_field: "", - }, - "f_one": Constraint { - r#type: LocationRecommended, - reason: "recommended", - folded_field: "", - }, - "f_two": Constraint { - r#type: FieldRequired, - reason: "field required", - folded_field: "", - }, - "flow_document": Constraint { - r#type: LocationRequired, - reason: "location required", - folded_field: "", - }, - }, resource_path: [ "table", "path", ], delta_updates: false, ser_policy: None, - projection_constraints: [], + projection_constraints: [ + ProjectionConstraint { + field: "F1", + constraint: Some( + Constraint { + r#type: FieldOptional, + reason: "optional", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_one", + constraint: Some( + Constraint { + r#type: LocationRecommended, + reason: "recommended", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "f_two", + constraint: Some( + Constraint { + r#type: FieldRequired, + reason: "field required", + folded_field: "", + }, + ), + }, + ProjectionConstraint { + field: "flow_document", + constraint: Some( + Constraint { + r#type: LocationRequired, + reason: "location required", + folded_field: "", + }, + ), + }, + ], }, ], }, diff --git a/go/protocols/materialize/extensions.go b/go/protocols/materialize/extensions.go index ba59b66fb4e..cd929fa84bb 100644 --- a/go/protocols/materialize/extensions.go +++ b/go/protocols/materialize/extensions.go @@ -84,16 +84,6 @@ func (m *Response_Validated) Validate() error { // Validate returns an error if the ValidateResponse_Binding isn't well-formed. func (m *Response_Validated_Binding) Validate() error { - for field, constraint := range m.Constraints { - if constraint == nil { - return pb.ExtendContext( - pb.NewValidationError("Constraint is missing"), "Constraints[%s]", field) - } else if _, ok := Response_Validated_Constraint_Type_name[int32(constraint.Type)]; !ok { - return pb.ExtendContext( - pb.NewValidationError("unknown Constraint Type %v", constraint), - "Constraints[%s]", field) - } - } // Validate each ProjectionConstraint entry and collect the first folded_field // seen for each field so we can enforce consistency across multiple entries. firstFoldedField := make(map[string]string) diff --git a/go/protocols/materialize/materialize.pb.go b/go/protocols/materialize/materialize.pb.go index 6bbed513a80..b24d8df35d9 100644 --- a/go/protocols/materialize/materialize.pb.go +++ b/go/protocols/materialize/materialize.pb.go @@ -849,11 +849,11 @@ func (m *Response_Validated) XXX_DiscardUnknown() { var xxx_messageInfo_Response_Validated proto.InternalMessageInfo // ProjectionConstraint pairs a projection field name with a single Constraint. -// Unlike the legacy `constraints` map, a repeated list of ProjectionConstraint -// allows multiple constraints to be expressed for the same field simultaneously. -// For example, INCOMPATIBLE and LOCATION_REQUIRED on the same field signals that -// the field is required but the existing destination column has an incompatible -// type, and a backfill is needed. +// A repeated list of ProjectionConstraint allows multiple constraints to be +// expressed for the same field simultaneously. For example, INCOMPATIBLE and +// LOCATION_REQUIRED on the same field signals that the field is required but +// the existing destination column has an incompatible type, and a backfill is +// needed. type Response_Validated_ProjectionConstraint struct { Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` Constraint *Response_Validated_Constraint `protobuf:"bytes,2,opt,name=constraint,proto3" json:"constraint,omitempty"` @@ -967,15 +967,6 @@ type Response_Validated_Binding struct { // want to preserve natural casing of fields, but they will still prohibit // (for example) "myField" and "MyField" from co-existing within a table. CaseInsensitiveFields bool `protobuf:"varint,5,opt,name=case_insensitive_fields,json=caseInsensitiveFields,proto3" json:"case_insensitive_fields,omitempty"` - // Constraints imposed by the connector, keyed by field name. - // Projections of the CollectionSpec which are missing from - // constraints are implicitly forbidden. - // - // Deprecated: use projection_constraints instead. When projection_constraints - // is non-empty it is authoritative and this map is ignored by the control - // plane. This field is retained for backward compatibility with connectors - // that have not yet migrated to the list form. - Constraints map[string]*Response_Validated_Constraint `protobuf:"bytes,1,rep,name=constraints,proto3" json:"constraints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Components of the resource path which fully qualify the resource // identified by this binding. // - For an RDBMS, this might be []{dbname, schema, table}. @@ -1001,12 +992,11 @@ type Response_Validated_Binding struct { // Serialization policy to use for this binding. SerPolicy *flow.SerPolicy `protobuf:"bytes,4,opt,name=ser_policy,json=serPolicy,proto3" json:"ser_policy,omitempty"` // Constraints on each projection, as a list that allows multiple constraints - // per projection field. When non-empty, this field is authoritative and the - // legacy `constraints` map is ignored. + // per projection field. Projections of the CollectionSpec which are missing + // from this list are implicitly forbidden. // - // Connectors should populate this field instead of `constraints`. The list - // form allows expressing compound requirements that a single constraint type - // cannot capture. For example, emitting both INCOMPATIBLE and + // The list form allows expressing compound requirements that a single + // constraint type cannot capture. For example, emitting both INCOMPATIBLE and // LOCATION_REQUIRED for the same field signals that the field is required // but the existing destination column is incompatible; a backfill is required. ProjectionConstraints []*Response_Validated_ProjectionConstraint `protobuf:"bytes,6,rep,name=projection_constraints,json=projectionConstraints,proto3" json:"projection_constraints,omitempty"` @@ -1488,7 +1478,6 @@ func init() { proto.RegisterType((*Response_Validated_ProjectionConstraint)(nil), "materialize.Response.Validated.ProjectionConstraint") proto.RegisterType((*Response_Validated_Constraint)(nil), "materialize.Response.Validated.Constraint") proto.RegisterType((*Response_Validated_Binding)(nil), "materialize.Response.Validated.Binding") - proto.RegisterMapType((map[string]*Response_Validated_Constraint)(nil), "materialize.Response.Validated.Binding.ConstraintsEntry") proto.RegisterType((*Response_Applied)(nil), "materialize.Response.Applied") proto.RegisterType((*Response_Opened)(nil), "materialize.Response.Opened") proto.RegisterType((*Response_Loaded)(nil), "materialize.Response.Loaded") @@ -1506,128 +1495,127 @@ func init() { } var fileDescriptor_3e8b62b327f34bc6 = []byte{ - // 1921 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcf, 0x6f, 0x1b, 0xc7, - 0x15, 0xf6, 0x52, 0xfc, 0xf9, 0x48, 0x59, 0xd4, 0x84, 0x8a, 0xe9, 0x8d, 0x63, 0xcb, 0x4a, 0x82, - 0x08, 0x2e, 0x42, 0x19, 0x72, 0xda, 0xc4, 0x0e, 0x5c, 0x94, 0xa4, 0x28, 0x80, 0xaa, 0x24, 0x2a, - 0x23, 0xcb, 0x01, 0x72, 0x21, 0x46, 0xbb, 0x23, 0x6a, 0xad, 0xe5, 0xce, 0x76, 0x67, 0x69, 0x9b, - 0xbd, 0x17, 0x45, 0x7b, 0x6a, 0x8f, 0x0d, 0x7a, 0xe8, 0xa9, 0xd7, 0x9e, 0x0a, 0x14, 0xbd, 0x16, - 0x05, 0x7c, 0xec, 0x5f, 0xe0, 0xa2, 0xe9, 0x3f, 0xd0, 0x4b, 0x2f, 0x39, 0x15, 0xf3, 0x63, 0x97, - 0x4b, 0x9a, 0xa4, 0x28, 0xc0, 0xcd, 0x85, 0xd8, 0x79, 0xf3, 0x7d, 0x6f, 0xdf, 0xbc, 0x79, 0x33, - 0xef, 0xe3, 0xc2, 0xbd, 0x1e, 0xdb, 0xf2, 0x03, 0x16, 0x32, 0x8b, 0xb9, 0x7c, 0xab, 0x4f, 0x42, - 0x1a, 0x38, 0xc4, 0x75, 0x7e, 0x4e, 0x93, 0xcf, 0x35, 0x89, 0x40, 0xc5, 0x84, 0xc9, 0x5c, 0xb7, - 0x98, 0xc7, 0x07, 0x7d, 0x1a, 0xc4, 0xf4, 0xf8, 0x41, 0xc1, 0xcd, 0x5b, 0x63, 0xae, 0xcf, 0x5c, - 0xf6, 0x42, 0xfe, 0xe8, 0xd9, 0x4a, 0x8f, 0xf5, 0x98, 0x7c, 0xdc, 0x12, 0x4f, 0xca, 0xba, 0xf1, - 0xa7, 0x35, 0xc8, 0x61, 0xfa, 0xb3, 0x01, 0xe5, 0x21, 0xfa, 0x04, 0xd2, 0xdc, 0xa7, 0x56, 0xd5, - 0x58, 0x37, 0x36, 0x8b, 0xdb, 0x37, 0x6b, 0xc9, 0x80, 0x34, 0xa6, 0x76, 0xec, 0x53, 0x0b, 0x4b, - 0x18, 0x7a, 0x08, 0xf9, 0xe7, 0xc4, 0x75, 0x6c, 0x12, 0xd2, 0x6a, 0x4a, 0x52, 0xde, 0x9f, 0x4a, - 0x79, 0xaa, 0x41, 0x38, 0x86, 0xa3, 0xfb, 0x90, 0x21, 0xbe, 0xef, 0x0e, 0xab, 0x4b, 0x92, 0x67, - 0x4e, 0xe5, 0xd5, 0x05, 0x02, 0x2b, 0xa0, 0x88, 0x8d, 0xf9, 0xd4, 0xab, 0xa6, 0xe7, 0xc4, 0xd6, - 0xf1, 0xa9, 0x87, 0x25, 0x4c, 0xc0, 0x5d, 0x46, 0xec, 0x6a, 0x66, 0x0e, 0x7c, 0x9f, 0x11, 0x1b, - 0x4b, 0x98, 0x88, 0xe7, 0xcc, 0x1d, 0xf0, 0xf3, 0x6a, 0x76, 0x4e, 0x3c, 0xbb, 0x02, 0x81, 0x15, - 0x50, 0x30, 0x78, 0xc8, 0x02, 0x5a, 0xcd, 0xcd, 0x61, 0x1c, 0x0b, 0x04, 0x56, 0x40, 0xd4, 0x84, - 0x12, 0x0f, 0x49, 0x10, 0x76, 0x2d, 0xd6, 0xef, 0x3b, 0x61, 0x35, 0x2f, 0x89, 0xeb, 0x33, 0x88, - 0x24, 0x08, 0x9b, 0x12, 0x87, 0x8b, 0x7c, 0x34, 0x40, 0x0d, 0x28, 0x12, 0xeb, 0xc2, 0x63, 0x2f, - 0x5c, 0x6a, 0xf7, 0x68, 0xb5, 0x30, 0xc7, 0x47, 0x7d, 0x84, 0xc3, 0x49, 0x12, 0x7a, 0x0f, 0xf2, - 0x8e, 0x17, 0xd2, 0xc0, 0x23, 0x6e, 0xd5, 0x5e, 0x37, 0x36, 0x4b, 0xb8, 0xf0, 0x61, 0x64, 0x30, - 0x7f, 0x6b, 0x40, 0x5a, 0xec, 0x31, 0x3a, 0x84, 0xeb, 0x16, 0xf3, 0x3c, 0x6a, 0x85, 0x2c, 0xe8, - 0x86, 0x43, 0x9f, 0xca, 0xb2, 0xb8, 0xbe, 0xfd, 0x71, 0x4d, 0xd6, 0xd4, 0x41, 0xfc, 0x46, 0x12, - 0x3a, 0xcc, 0x13, 0x94, 0x5a, 0x33, 0xc2, 0x3f, 0x19, 0xfa, 0x14, 0x2f, 0x5b, 0xc9, 0x21, 0x7a, - 0x08, 0x45, 0x8b, 0x79, 0x67, 0x4e, 0xaf, 0xfb, 0x8c, 0x33, 0x4f, 0x16, 0x4c, 0xa9, 0x71, 0xeb, - 0xbb, 0xd7, 0x77, 0xaa, 0xd4, 0xb3, 0x98, 0xed, 0x78, 0xbd, 0x2d, 0x31, 0x51, 0xc3, 0xe4, 0xc5, - 0x01, 0xe5, 0x9c, 0xf4, 0x28, 0xce, 0x2a, 0x82, 0xf9, 0x97, 0x2c, 0xe4, 0xa3, 0x22, 0x42, 0x5f, - 0x42, 0xda, 0x23, 0x7d, 0x15, 0x4d, 0xa1, 0xf1, 0xf8, 0xbb, 0xd7, 0x77, 0x1e, 0xf6, 0x9c, 0xf0, - 0x7c, 0x70, 0x5a, 0xb3, 0x58, 0x7f, 0x8b, 0xf2, 0x70, 0x40, 0x82, 0xa1, 0x2a, 0xfe, 0x37, 0x8e, - 0xc3, 0x64, 0xd4, 0x58, 0xba, 0x9a, 0xb2, 0xd4, 0xd4, 0xdb, 0x5c, 0xea, 0xd2, 0xe2, 0x4b, 0x45, - 0x75, 0xc8, 0x9f, 0x3a, 0x9e, 0x80, 0xf0, 0x6a, 0x7a, 0x7d, 0x69, 0xb3, 0xb8, 0xfd, 0xd1, 0xdc, - 0x33, 0x55, 0x6b, 0x28, 0x34, 0x8e, 0x69, 0x68, 0x1f, 0x2a, 0x2e, 0xe1, 0x61, 0xb7, 0x3f, 0x1e, - 0x76, 0x7c, 0x14, 0x66, 0xad, 0x09, 0xbf, 0x23, 0x68, 0x13, 0x13, 0xe8, 0x2e, 0x94, 0xa4, 0xb7, - 0xe7, 0x34, 0xe0, 0xc2, 0x8b, 0x38, 0x20, 0x05, 0x5c, 0x14, 0xb6, 0xa7, 0xca, 0x64, 0xfe, 0x6e, - 0x09, 0x72, 0x3a, 0x0c, 0xb4, 0x07, 0x95, 0x80, 0x72, 0x36, 0x08, 0x2c, 0xda, 0x4d, 0xe6, 0xc0, - 0x58, 0x20, 0x07, 0xd7, 0x23, 0x66, 0x53, 0xe5, 0xe2, 0x11, 0x80, 0xc5, 0x5c, 0x97, 0x5a, 0x32, - 0x7c, 0x75, 0xc3, 0x54, 0x54, 0xf8, 0xcd, 0xd8, 0x2e, 0x22, 0x6f, 0xa4, 0x5f, 0xbd, 0xbe, 0x73, - 0x0d, 0x27, 0xd0, 0xe8, 0x97, 0x06, 0xac, 0x9d, 0x39, 0xd4, 0xb5, 0x93, 0x51, 0x74, 0xfb, 0xc4, - 0xaf, 0x2e, 0xc9, 0xac, 0x3e, 0x5e, 0x28, 0xab, 0xb5, 0x5d, 0xe1, 0x42, 0x85, 0xb3, 0xc7, 0x99, - 0x77, 0x40, 0xfc, 0x96, 0x17, 0x06, 0xc3, 0xc6, 0xad, 0x5f, 0xff, 0x73, 0xce, 0x42, 0x8a, 0x67, - 0x23, 0x1a, 0x32, 0x21, 0x7f, 0x4a, 0xac, 0x8b, 0x33, 0xc7, 0x75, 0xe5, 0xe5, 0xb5, 0x8c, 0xe3, - 0x31, 0xba, 0x09, 0xf9, 0x5e, 0xc0, 0x06, 0x7e, 0xf7, 0x74, 0x58, 0xcd, 0xac, 0x2f, 0x6d, 0x16, - 0x70, 0x4e, 0x8e, 0x1b, 0x43, 0xb3, 0x05, 0x37, 0x66, 0xbc, 0x1c, 0x95, 0x61, 0xe9, 0x82, 0x0e, - 0xd5, 0x01, 0xc0, 0xe2, 0x11, 0x55, 0x20, 0xf3, 0x9c, 0xb8, 0x03, 0x55, 0xb7, 0x25, 0xac, 0x06, - 0x8f, 0x52, 0x9f, 0x1b, 0xe6, 0x6f, 0x52, 0x90, 0x91, 0xf7, 0x28, 0x6a, 0xc2, 0xca, 0x64, 0x45, - 0x18, 0x97, 0x55, 0xc4, 0x24, 0x03, 0x55, 0x21, 0x17, 0x15, 0x42, 0x4a, 0xbe, 0x3e, 0x1a, 0xce, - 0xac, 0xba, 0xf4, 0x5b, 0xa9, 0xba, 0xcc, 0x1b, 0x55, 0x87, 0x3e, 0x03, 0xe0, 0x21, 0x09, 0xa9, - 0xaa, 0xaf, 0xec, 0x02, 0xf5, 0x95, 0x91, 0x78, 0xf3, 0xef, 0x06, 0xa4, 0x45, 0xa7, 0xf8, 0x7f, - 0x67, 0xe4, 0x23, 0xc8, 0x04, 0xc4, 0xeb, 0x51, 0xdd, 0xe3, 0x56, 0x94, 0x53, 0x2c, 0x4c, 0xd2, - 0x95, 0x9a, 0x9d, 0x58, 0x47, 0x7a, 0xf1, 0x75, 0x84, 0x90, 0x16, 0x1d, 0x4c, 0x44, 0xa0, 0xcf, - 0xbe, 0x0c, 0x7f, 0x19, 0x47, 0x43, 0xf4, 0x00, 0xf2, 0x17, 0x74, 0xb8, 0xf8, 0x7d, 0x2b, 0x6b, - 0xe9, 0x7d, 0x00, 0x41, 0xf2, 0x89, 0x75, 0x41, 0x6d, 0x75, 0x77, 0xe1, 0xc2, 0x05, 0x1d, 0x1e, - 0x49, 0x83, 0xd9, 0x81, 0x8c, 0xec, 0x83, 0x68, 0x17, 0x90, 0x8a, 0xdb, 0x27, 0xa1, 0x75, 0x4e, - 0xf9, 0xe2, 0xe7, 0xbc, 0x24, 0x79, 0x47, 0x8a, 0x66, 0xfe, 0x35, 0x05, 0x19, 0xd9, 0x27, 0xbf, - 0xdf, 0x85, 0x88, 0x4b, 0x5a, 0x1e, 0x13, 0xbe, 0x78, 0xe2, 0xb3, 0x8a, 0x80, 0x3e, 0x80, 0x65, - 0x4d, 0xd5, 0xce, 0x33, 0xd2, 0x79, 0x49, 0x19, 0xb5, 0xff, 0x07, 0x90, 0xb7, 0x99, 0xb5, 0x78, - 0x75, 0x2e, 0xd9, 0xcc, 0x42, 0xef, 0x42, 0x96, 0xbe, 0x74, 0x78, 0xc8, 0xa5, 0xac, 0xc8, 0x63, - 0x3d, 0x12, 0x76, 0x9b, 0xba, 0x34, 0xa4, 0x52, 0x35, 0xe4, 0xb1, 0x1e, 0x99, 0xdf, 0x18, 0x50, - 0x4c, 0x68, 0x05, 0xd4, 0x04, 0x14, 0x0c, 0xbc, 0xd0, 0xe9, 0xd3, 0xae, 0x75, 0x4e, 0xad, 0x0b, - 0x9f, 0x39, 0x5e, 0xa8, 0xab, 0xba, 0x52, 0x8b, 0x04, 0x64, 0xad, 0x19, 0xcf, 0xe1, 0x55, 0x8d, - 0x1f, 0x99, 0x66, 0xec, 0x6c, 0xea, 0xca, 0x3b, 0x7b, 0x02, 0xc5, 0x84, 0x06, 0x79, 0x5b, 0x05, - 0xb3, 0xf1, 0x0d, 0x82, 0x3c, 0xa6, 0xdc, 0x67, 0x1e, 0xa7, 0xa8, 0x36, 0x26, 0x59, 0x27, 0x55, - 0x98, 0x02, 0x25, 0x35, 0xeb, 0x63, 0x28, 0x44, 0x22, 0xd4, 0xd6, 0x2d, 0xe5, 0xce, 0x74, 0x52, - 0xd4, 0x0b, 0x6c, 0x3c, 0x62, 0xa0, 0xcf, 0x20, 0x27, 0xe4, 0xa8, 0xa3, 0x0b, 0xea, 0x4d, 0xc5, - 0xab, 0xc9, 0x75, 0x05, 0xc2, 0x11, 0x1a, 0x7d, 0x0a, 0x59, 0xa1, 0x4b, 0xa9, 0xad, 0x2f, 0xc4, - 0x5b, 0xd3, 0x79, 0x1d, 0x89, 0xc1, 0x1a, 0x2b, 0x58, 0x42, 0x9e, 0xd2, 0x48, 0xc7, 0xce, 0x60, - 0xed, 0x4b, 0x0c, 0xd6, 0x58, 0x11, 0xa4, 0xd4, 0xa8, 0xd4, 0xd6, 0x72, 0x76, 0x46, 0x90, 0xbb, - 0x0a, 0x84, 0x23, 0x34, 0xda, 0x83, 0xeb, 0x52, 0x6b, 0x52, 0x3b, 0xd2, 0xa8, 0x4a, 0xdc, 0x7e, - 0x30, 0x23, 0xad, 0x0a, 0xab, 0x65, 0xea, 0x32, 0x4f, 0x0e, 0xd1, 0x2e, 0x94, 0x12, 0x9a, 0xd3, - 0xd6, 0x6a, 0x77, 0x63, 0x46, 0xba, 0x12, 0x48, 0x3c, 0xc6, 0x9b, 0x2f, 0x56, 0x7f, 0x9f, 0xd2, - 0x62, 0xd5, 0x84, 0x7c, 0xa4, 0xf4, 0xf4, 0xdd, 0x11, 0x8f, 0x45, 0xdd, 0x69, 0x0d, 0xc0, 0xad, - 0x73, 0xda, 0x27, 0x57, 0x28, 0x67, 0xc5, 0x3b, 0x96, 0x34, 0xf4, 0x15, 0xbc, 0x37, 0x29, 0x6d, - 0x92, 0x0e, 0x17, 0x51, 0x79, 0x95, 0x71, 0x85, 0xa3, 0x1d, 0xff, 0x00, 0x56, 0x6d, 0x66, 0x0d, - 0xfa, 0xd4, 0x0b, 0x65, 0x4f, 0xe9, 0x0e, 0x02, 0x25, 0x15, 0x0a, 0xb8, 0x3c, 0x36, 0x71, 0x12, - 0xb8, 0xe8, 0x43, 0xc8, 0x32, 0x32, 0x08, 0xcf, 0xb7, 0x75, 0x49, 0x94, 0x54, 0x5b, 0xe9, 0xd4, - 0x85, 0x0d, 0xeb, 0xb9, 0xbd, 0x74, 0x3e, 0x5b, 0xce, 0x99, 0xff, 0xcd, 0x41, 0x21, 0x2e, 0x63, - 0xd4, 0x4c, 0x48, 0x4b, 0x43, 0x8a, 0xa0, 0x8f, 0x2f, 0xa9, 0xfc, 0x37, 0xc5, 0xa5, 0xf9, 0x12, - 0x2a, 0x47, 0x01, 0x7b, 0xa6, 0x54, 0x56, 0x93, 0x79, 0x3c, 0x0c, 0x88, 0xb8, 0x33, 0x2a, 0x90, - 0x91, 0xa2, 0x47, 0xab, 0x12, 0x35, 0x40, 0x7b, 0x42, 0xc1, 0x45, 0x18, 0x7d, 0xdc, 0xee, 0x5d, - 0xf6, 0xd2, 0x91, 0x57, 0x9c, 0x60, 0x9b, 0x7f, 0x4e, 0x01, 0x24, 0x5e, 0xd8, 0x84, 0x74, 0x42, - 0xa9, 0x6f, 0x2d, 0xee, 0xb4, 0x26, 0x15, 0xbb, 0x24, 0x8b, 0x6b, 0x35, 0xa0, 0x24, 0xda, 0xbd, - 0x02, 0xd6, 0x23, 0x21, 0x3f, 0xce, 0x98, 0x6b, 0x53, 0xbb, 0xab, 0x16, 0xa5, 0x36, 0xa3, 0xa8, - 0x6c, 0x52, 0x96, 0x6d, 0xfc, 0xd1, 0x80, 0xb4, 0x14, 0xfb, 0x45, 0xc8, 0xb5, 0x0f, 0x9f, 0xd6, - 0xf7, 0xdb, 0x3b, 0xe5, 0x6b, 0x08, 0xc1, 0xf5, 0xdd, 0x76, 0x6b, 0x7f, 0xa7, 0x8b, 0x5b, 0x5f, - 0x9e, 0xb4, 0x71, 0x6b, 0xa7, 0x6c, 0xa0, 0x35, 0x58, 0xdd, 0xef, 0x34, 0xeb, 0x4f, 0xda, 0x9d, - 0xc3, 0x91, 0x39, 0x85, 0xaa, 0x50, 0x49, 0x98, 0x9b, 0x9d, 0x83, 0x83, 0xd6, 0xe1, 0x4e, 0x6b, - 0xa7, 0xbc, 0x34, 0x72, 0xd2, 0x39, 0x12, 0xb3, 0xf5, 0xfd, 0x72, 0x1a, 0xbd, 0x03, 0x2b, 0xca, - 0xb6, 0xdb, 0xc1, 0x8d, 0xf6, 0xce, 0x4e, 0xeb, 0xb0, 0x9c, 0x41, 0x65, 0x28, 0xb5, 0x0f, 0x9b, - 0x9d, 0x83, 0xa3, 0xfa, 0x93, 0x76, 0x63, 0xbf, 0x55, 0xce, 0xa2, 0x55, 0x58, 0x3e, 0x39, 0x3c, - 0xae, 0x3f, 0x69, 0x1f, 0xef, 0xb6, 0xeb, 0xc2, 0x94, 0x33, 0xff, 0x93, 0x50, 0xe7, 0x3f, 0x82, - 0x1b, 0x16, 0xe1, 0xb4, 0xeb, 0x78, 0x9c, 0x7a, 0xdc, 0x09, 0x9d, 0xe7, 0x54, 0xad, 0x90, 0xcb, - 0x6a, 0xca, 0xe3, 0x35, 0x31, 0xdd, 0x1e, 0xcd, 0xca, 0xb5, 0x72, 0xf4, 0xb5, 0xfc, 0x43, 0xa3, - 0x13, 0x18, 0x55, 0xcf, 0xe7, 0x0b, 0x56, 0x4f, 0x22, 0xf7, 0x5c, 0x0a, 0x58, 0x9c, 0x74, 0x26, - 0x9a, 0x69, 0x7c, 0xac, 0x7c, 0x12, 0x9e, 0x57, 0x53, 0x52, 0x08, 0x97, 0x22, 0xe3, 0x11, 0x09, - 0xcf, 0x05, 0xc8, 0xa6, 0x6e, 0x48, 0xba, 0x03, 0x5f, 0xf8, 0xe6, 0x72, 0xbf, 0xf2, 0xb8, 0x24, - 0x8d, 0x27, 0xca, 0x86, 0x6a, 0x00, 0x9c, 0x06, 0x5d, 0x9f, 0xb9, 0x8e, 0x35, 0xd4, 0xf7, 0xac, - 0x56, 0x5d, 0xc7, 0x34, 0x38, 0x92, 0x66, 0x5c, 0xe0, 0xd1, 0x23, 0xba, 0x80, 0x77, 0xfd, 0xb8, - 0x96, 0xbb, 0xc9, 0x05, 0x66, 0xe5, 0x02, 0x3f, 0xbd, 0x6c, 0x81, 0xd3, 0x4e, 0x02, 0x5e, 0xf3, - 0xa7, 0x58, 0xb9, 0xf9, 0x0c, 0xca, 0x93, 0x79, 0x98, 0x22, 0xe4, 0x7f, 0x92, 0x14, 0xf2, 0x57, - 0x3b, 0x2b, 0x09, 0xd1, 0x6f, 0x43, 0x4e, 0x37, 0x20, 0xf4, 0x09, 0x20, 0xa2, 0xd6, 0x67, 0x53, - 0x6e, 0x05, 0x8e, 0x1f, 0xcb, 0xdc, 0x02, 0x5e, 0x55, 0x33, 0x3b, 0xa3, 0x09, 0x74, 0x0f, 0x94, - 0xb8, 0x9c, 0xfc, 0xb7, 0xa5, 0xff, 0xdd, 0x1e, 0x8b, 0xb9, 0x48, 0x7f, 0xfe, 0xca, 0x80, 0xac, - 0xea, 0x57, 0x6f, 0x47, 0x76, 0x3c, 0x82, 0x9b, 0xb6, 0xc3, 0xc9, 0xa9, 0x4b, 0xbb, 0xa2, 0x91, - 0x75, 0x99, 0x1f, 0x3a, 0xfd, 0x48, 0x98, 0xa7, 0xe4, 0x7e, 0xdf, 0xd0, 0x00, 0xd1, 0xf0, 0x3a, - 0x89, 0x69, 0xf3, 0x2b, 0xc8, 0xaa, 0x26, 0x38, 0x5f, 0x44, 0xc6, 0x82, 0x2c, 0xb5, 0xa0, 0x20, - 0x33, 0x7f, 0x08, 0x39, 0xdd, 0x26, 0x47, 0xb9, 0x31, 0x2e, 0xcf, 0xcd, 0x17, 0xb0, 0x3c, 0xd6, - 0x1d, 0xaf, 0x44, 0x7e, 0x04, 0xa5, 0x64, 0x43, 0xbc, 0x0a, 0x77, 0xe3, 0x17, 0x69, 0xc8, 0xb4, - 0x5e, 0x86, 0x01, 0x31, 0xff, 0x66, 0xc0, 0xdd, 0xa8, 0x50, 0x5a, 0x42, 0x45, 0x3a, 0x5e, 0x6f, - 0x54, 0xb0, 0xd1, 0x27, 0xbf, 0x7d, 0x28, 0x53, 0x3d, 0xd9, 0x4d, 0xe6, 0xad, 0xb8, 0x7d, 0x77, - 0xf6, 0xc7, 0x8f, 0xa8, 0x2d, 0xac, 0x44, 0xd4, 0xe8, 0x7e, 0x39, 0x82, 0xb2, 0x1f, 0x30, 0x9f, - 0x71, 0x6a, 0xc7, 0xde, 0x54, 0x25, 0x2d, 0xf8, 0x15, 0x63, 0x25, 0xa2, 0x6b, 0x83, 0xb8, 0xf5, - 0xe3, 0x55, 0x68, 0x5b, 0xbd, 0x47, 0x1c, 0x8f, 0x87, 0x89, 0xd3, 0x84, 0xbe, 0x18, 0xdf, 0xf4, - 0x85, 0x82, 0x8f, 0xeb, 0xa2, 0x37, 0x7e, 0xb9, 0xa5, 0xe4, 0xd9, 0x6f, 0x8d, 0xc5, 0x2b, 0x33, - 0x5a, 0xbb, 0x34, 0x8e, 0xf9, 0x37, 0xdd, 0xf7, 0x79, 0x05, 0x6c, 0xff, 0x14, 0x0a, 0x71, 0x81, - 0xa0, 0x1f, 0x43, 0x71, 0x94, 0x09, 0x8a, 0x2a, 0xd3, 0xf6, 0xc2, 0x5c, 0x9b, 0xfa, 0xa2, 0x4d, - 0xe3, 0xbe, 0xd1, 0x68, 0xbc, 0xfa, 0xd7, 0xed, 0x6b, 0xaf, 0xbe, 0xbd, 0x6d, 0xfc, 0xe3, 0xdb, - 0xdb, 0xc6, 0x1f, 0xfe, 0x7d, 0xdb, 0xf8, 0xfa, 0xfe, 0x42, 0x9f, 0xdc, 0x12, 0x0e, 0x4f, 0xb3, - 0xd2, 0xfc, 0xe0, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3c, 0xcd, 0x0e, 0x34, 0xff, 0x16, 0x00, - 0x00, + // 1917 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4f, 0x6f, 0xdb, 0xc8, + 0xf9, 0x0e, 0x65, 0xfd, 0x7d, 0x25, 0xdb, 0xf2, 0xac, 0xbc, 0x51, 0xb8, 0xd9, 0xc4, 0xf1, 0xee, + 0x62, 0x8d, 0xfc, 0xb0, 0x72, 0xe0, 0xec, 0xaf, 0xdb, 0x64, 0x91, 0xa2, 0x92, 0x2c, 0x03, 0x72, + 0x6d, 0xcb, 0x3b, 0x8e, 0xb3, 0x40, 0x2f, 0xc4, 0x98, 0x1c, 0xcb, 0x8c, 0x29, 0x0e, 0xcb, 0xa1, + 0x92, 0xa8, 0xf7, 0xa2, 0x68, 0x4f, 0xed, 0xb1, 0x45, 0x0f, 0x3d, 0xf5, 0xda, 0x53, 0x81, 0xa2, + 0xbd, 0x14, 0x28, 0x0a, 0xe4, 0xd8, 0x4f, 0x90, 0xa2, 0x5b, 0xf4, 0x4b, 0xec, 0xa9, 0x98, 0x3f, + 0xa4, 0x28, 0x47, 0xb2, 0x65, 0x20, 0xed, 0x45, 0xe0, 0xbc, 0xf3, 0x3c, 0xc3, 0x77, 0x66, 0x9e, + 0x99, 0xf7, 0xa1, 0xe0, 0x7e, 0x9f, 0x6d, 0x06, 0x21, 0x8b, 0x98, 0xcd, 0x3c, 0xbe, 0x39, 0x20, + 0x11, 0x0d, 0x5d, 0xe2, 0xb9, 0x3f, 0xa6, 0xe9, 0xe7, 0x86, 0x44, 0xa0, 0x72, 0x2a, 0x64, 0xae, + 0xd9, 0xcc, 0xe7, 0xc3, 0x01, 0x0d, 0x13, 0x7a, 0xf2, 0xa0, 0xe0, 0xe6, 0xed, 0x89, 0xa1, 0x4f, + 0x3d, 0xf6, 0x52, 0xfe, 0xe8, 0xde, 0x5a, 0x9f, 0xf5, 0x99, 0x7c, 0xdc, 0x14, 0x4f, 0x2a, 0xba, + 0xfe, 0xfb, 0x55, 0x28, 0x60, 0xfa, 0xa3, 0x21, 0xe5, 0x11, 0xfa, 0x0c, 0xb2, 0x3c, 0xa0, 0x76, + 0xdd, 0x58, 0x33, 0x36, 0xca, 0x5b, 0xb7, 0x1a, 0xe9, 0x84, 0x34, 0xa6, 0x71, 0x14, 0x50, 0x1b, + 0x4b, 0x18, 0x7a, 0x04, 0xc5, 0x17, 0xc4, 0x73, 0x1d, 0x12, 0xd1, 0x7a, 0x46, 0x52, 0x3e, 0x9c, + 0x4a, 0x79, 0xa6, 0x41, 0x38, 0x81, 0xa3, 0x07, 0x90, 0x23, 0x41, 0xe0, 0x8d, 0xea, 0x0b, 0x92, + 0x67, 0x4e, 0xe5, 0x35, 0x05, 0x02, 0x2b, 0xa0, 0xc8, 0x8d, 0x05, 0xd4, 0xaf, 0x67, 0x2f, 0xc9, + 0xad, 0x17, 0x50, 0x1f, 0x4b, 0x98, 0x80, 0x7b, 0x8c, 0x38, 0xf5, 0xdc, 0x25, 0xf0, 0x3d, 0x46, + 0x1c, 0x2c, 0x61, 0x22, 0x9f, 0x53, 0x6f, 0xc8, 0xcf, 0xea, 0xf9, 0x4b, 0xf2, 0xd9, 0x11, 0x08, + 0xac, 0x80, 0x82, 0xc1, 0x23, 0x16, 0xd2, 0x7a, 0xe1, 0x12, 0xc6, 0x91, 0x40, 0x60, 0x05, 0x44, + 0x6d, 0xa8, 0xf0, 0x88, 0x84, 0x91, 0x65, 0xb3, 0xc1, 0xc0, 0x8d, 0xea, 0x45, 0x49, 0x5c, 0x9b, + 0x41, 0x24, 0x61, 0xd4, 0x96, 0x38, 0x5c, 0xe6, 0xe3, 0x06, 0x6a, 0x41, 0x99, 0xd8, 0xe7, 0x3e, + 0x7b, 0xe9, 0x51, 0xa7, 0x4f, 0xeb, 0xa5, 0x4b, 0xc6, 0x68, 0x8e, 0x71, 0x38, 0x4d, 0x42, 0x1f, + 0x40, 0xd1, 0xf5, 0x23, 0x1a, 0xfa, 0xc4, 0xab, 0x3b, 0x6b, 0xc6, 0x46, 0x05, 0x97, 0x3e, 0x8e, + 0x03, 0xe6, 0x2f, 0x0d, 0xc8, 0x8a, 0x3d, 0x46, 0x07, 0xb0, 0x64, 0x33, 0xdf, 0xa7, 0x76, 0xc4, + 0x42, 0x2b, 0x1a, 0x05, 0x54, 0xca, 0x62, 0x69, 0xeb, 0xd3, 0x86, 0xd4, 0xd4, 0x7e, 0xf2, 0x46, + 0x12, 0xb9, 0xcc, 0x17, 0x94, 0x46, 0x3b, 0xc6, 0x3f, 0x1d, 0x05, 0x14, 0x2f, 0xda, 0xe9, 0x26, + 0x7a, 0x04, 0x65, 0x9b, 0xf9, 0xa7, 0x6e, 0xdf, 0x7a, 0xce, 0x99, 0x2f, 0x05, 0x53, 0x69, 0xdd, + 0xfe, 0xf6, 0xcd, 0xdd, 0x3a, 0xf5, 0x6d, 0xe6, 0xb8, 0x7e, 0x7f, 0x53, 0x74, 0x34, 0x30, 0x79, + 0xb9, 0x4f, 0x39, 0x27, 0x7d, 0x8a, 0xf3, 0x8a, 0x60, 0xfe, 0x31, 0x0f, 0xc5, 0x58, 0x44, 0xe8, + 0x2b, 0xc8, 0xfa, 0x64, 0xa0, 0xb2, 0x29, 0xb5, 0x9e, 0x7c, 0xfb, 0xe6, 0xee, 0xa3, 0xbe, 0x1b, + 0x9d, 0x0d, 0x4f, 0x1a, 0x36, 0x1b, 0x6c, 0x52, 0x1e, 0x0d, 0x49, 0x38, 0x52, 0xe2, 0x7f, 0xeb, + 0x38, 0x5c, 0xcc, 0x1a, 0xcb, 0xa1, 0xa6, 0x4c, 0x35, 0xf3, 0x2e, 0xa7, 0xba, 0x30, 0xff, 0x54, + 0x51, 0x13, 0x8a, 0x27, 0xae, 0x2f, 0x20, 0xbc, 0x9e, 0x5d, 0x5b, 0xd8, 0x28, 0x6f, 0x7d, 0x72, + 0xe9, 0x99, 0x6a, 0xb4, 0x14, 0x1a, 0x27, 0x34, 0xb4, 0x07, 0x35, 0x8f, 0xf0, 0xc8, 0x1a, 0x4c, + 0xa6, 0x9d, 0x1c, 0x85, 0x59, 0x73, 0xc2, 0xef, 0x09, 0xda, 0x85, 0x0e, 0x74, 0x0f, 0x2a, 0x72, + 0xb4, 0x17, 0x34, 0xe4, 0x62, 0x14, 0x71, 0x40, 0x4a, 0xb8, 0x2c, 0x62, 0xcf, 0x54, 0xc8, 0xfc, + 0xd5, 0x02, 0x14, 0x74, 0x1a, 0x68, 0x17, 0x6a, 0x21, 0xe5, 0x6c, 0x18, 0xda, 0xd4, 0x4a, 0xaf, + 0x81, 0x31, 0xc7, 0x1a, 0x2c, 0xc5, 0xcc, 0xb6, 0x5a, 0x8b, 0xc7, 0x00, 0x36, 0xf3, 0x3c, 0x6a, + 0xcb, 0xf4, 0xd5, 0x0d, 0x53, 0x53, 0xe9, 0xb7, 0x93, 0xb8, 0xc8, 0xbc, 0x95, 0x7d, 0xfd, 0xe6, + 0xee, 0x0d, 0x9c, 0x42, 0xa3, 0x9f, 0x1a, 0xb0, 0x7a, 0xea, 0x52, 0xcf, 0x49, 0x67, 0x61, 0x0d, + 0x48, 0x50, 0x5f, 0x90, 0xab, 0xfa, 0x64, 0xae, 0x55, 0x6d, 0xec, 0x88, 0x21, 0x54, 0x3a, 0xbb, + 0x9c, 0xf9, 0xfb, 0x24, 0xe8, 0xf8, 0x51, 0x38, 0x6a, 0xdd, 0xfe, 0xf9, 0x3f, 0x2e, 0x99, 0x48, + 0xf9, 0x74, 0x4c, 0x43, 0x26, 0x14, 0x4f, 0x88, 0x7d, 0x7e, 0xea, 0x7a, 0x9e, 0xbc, 0xbc, 0x16, + 0x71, 0xd2, 0x46, 0xb7, 0xa0, 0xd8, 0x0f, 0xd9, 0x30, 0xb0, 0x4e, 0x46, 0xf5, 0xdc, 0xda, 0xc2, + 0x46, 0x09, 0x17, 0x64, 0xbb, 0x35, 0x32, 0x3b, 0x70, 0x73, 0xc6, 0xcb, 0x51, 0x15, 0x16, 0xce, + 0xe9, 0x48, 0x1d, 0x00, 0x2c, 0x1e, 0x51, 0x0d, 0x72, 0x2f, 0x88, 0x37, 0x54, 0xba, 0xad, 0x60, + 0xd5, 0x78, 0x9c, 0xf9, 0xae, 0x61, 0xfe, 0x22, 0x03, 0x39, 0x79, 0x8f, 0xa2, 0x36, 0x2c, 0x5f, + 0x54, 0x84, 0x71, 0x95, 0x22, 0x2e, 0x32, 0x50, 0x1d, 0x0a, 0xb1, 0x10, 0x32, 0xf2, 0xf5, 0x71, + 0x73, 0xa6, 0xea, 0xb2, 0xef, 0x44, 0x75, 0xb9, 0xb7, 0x54, 0x87, 0xbe, 0x00, 0xe0, 0x11, 0x89, + 0xa8, 0xd2, 0x57, 0x7e, 0x0e, 0x7d, 0xe5, 0x24, 0xde, 0xfc, 0x9b, 0x01, 0x59, 0x51, 0x29, 0xfe, + 0xdb, 0x2b, 0xf2, 0x09, 0xe4, 0x42, 0xe2, 0xf7, 0xa9, 0xae, 0x71, 0xcb, 0x6a, 0x50, 0x2c, 0x42, + 0x72, 0x28, 0xd5, 0x7b, 0x61, 0x1e, 0xd9, 0xf9, 0xe7, 0x11, 0x41, 0x56, 0x54, 0x30, 0x91, 0x81, + 0x3e, 0xfb, 0x32, 0xfd, 0x45, 0x1c, 0x37, 0xd1, 0x43, 0x28, 0x9e, 0xd3, 0xd1, 0xfc, 0xf7, 0xad, + 0xd4, 0xd2, 0x87, 0x00, 0x82, 0x14, 0x10, 0xfb, 0x9c, 0x3a, 0xea, 0xee, 0xc2, 0xa5, 0x73, 0x3a, + 0x3a, 0x94, 0x01, 0xb3, 0x07, 0x39, 0x59, 0x07, 0xd1, 0x0e, 0x20, 0x95, 0x77, 0x40, 0x22, 0xfb, + 0x8c, 0xf2, 0xf9, 0xcf, 0x79, 0x45, 0xf2, 0x0e, 0x15, 0xcd, 0xfc, 0x53, 0x06, 0x72, 0xb2, 0x4e, + 0xfe, 0x6f, 0x27, 0x22, 0x2e, 0x69, 0x79, 0x4c, 0xf8, 0xfc, 0x0b, 0x9f, 0x57, 0x04, 0xf4, 0x11, + 0x2c, 0x6a, 0xaa, 0x1e, 0x3c, 0x27, 0x07, 0xaf, 0xa8, 0xa0, 0x1e, 0xff, 0x21, 0x14, 0x1d, 0x66, + 0xcf, 0xaf, 0xce, 0x05, 0x87, 0xd9, 0xe8, 0x7d, 0xc8, 0xd3, 0x57, 0x2e, 0x8f, 0xb8, 0xb4, 0x15, + 0x45, 0xac, 0x5b, 0x22, 0xee, 0x50, 0x8f, 0x46, 0x54, 0xba, 0x86, 0x22, 0xd6, 0x2d, 0xf3, 0xd7, + 0x06, 0x94, 0x53, 0x5e, 0x01, 0xb5, 0x01, 0x85, 0x43, 0x3f, 0x72, 0x07, 0xd4, 0xb2, 0xcf, 0xa8, + 0x7d, 0x1e, 0x30, 0xd7, 0x8f, 0xb4, 0xaa, 0x6b, 0x8d, 0xd8, 0x40, 0x36, 0xda, 0x49, 0x1f, 0x5e, + 0xd1, 0xf8, 0x71, 0x68, 0xc6, 0xce, 0x66, 0xae, 0xbd, 0xb3, 0xc7, 0x50, 0x4e, 0x79, 0x90, 0x77, + 0x25, 0x98, 0xf5, 0x7f, 0x57, 0xa1, 0x88, 0x29, 0x0f, 0x98, 0xcf, 0x29, 0x6a, 0x4c, 0x58, 0xd6, + 0x8b, 0x2e, 0x4c, 0x81, 0xd2, 0x9e, 0xf5, 0x09, 0x94, 0x62, 0x13, 0xea, 0xe8, 0x92, 0x72, 0x77, + 0x3a, 0x29, 0xae, 0x05, 0x0e, 0x1e, 0x33, 0xd0, 0x17, 0x50, 0x10, 0x76, 0xd4, 0xd5, 0x82, 0x7a, + 0xdb, 0xf1, 0x6a, 0x72, 0x53, 0x81, 0x70, 0x8c, 0x46, 0x9f, 0x43, 0x5e, 0xf8, 0x52, 0xea, 0xe8, + 0x0b, 0xf1, 0xf6, 0x74, 0x5e, 0x4f, 0x62, 0xb0, 0xc6, 0x0a, 0x96, 0xb0, 0xa7, 0x34, 0xf6, 0xb1, + 0x33, 0x58, 0x7b, 0x12, 0x83, 0x35, 0x56, 0x24, 0x29, 0x3d, 0x2a, 0x75, 0xb4, 0x9d, 0x9d, 0x91, + 0xe4, 0x8e, 0x02, 0xe1, 0x18, 0x8d, 0x76, 0x61, 0x49, 0x7a, 0x4d, 0xea, 0xc4, 0x1e, 0x55, 0x99, + 0xdb, 0x8f, 0x66, 0x2c, 0xab, 0xc2, 0x6a, 0x9b, 0xba, 0xc8, 0xd3, 0x4d, 0xb4, 0x03, 0x95, 0x94, + 0xe7, 0x74, 0xb4, 0xdb, 0x5d, 0x9f, 0xb1, 0x5c, 0x29, 0x24, 0x9e, 0xe0, 0x5d, 0x6e, 0x56, 0x7f, + 0x93, 0xd1, 0x66, 0xd5, 0x84, 0x62, 0xec, 0xf4, 0xf4, 0xdd, 0x91, 0xb4, 0x85, 0xee, 0xb4, 0x07, + 0xe0, 0xf6, 0x19, 0x1d, 0x90, 0x6b, 0xc8, 0x59, 0xf1, 0x8e, 0x24, 0x0d, 0x7d, 0x0d, 0x1f, 0x5c, + 0xb4, 0x36, 0xe9, 0x01, 0xe7, 0x71, 0x79, 0xb5, 0x49, 0x87, 0xa3, 0x07, 0xfe, 0x3f, 0x58, 0x71, + 0x98, 0x3d, 0x1c, 0x50, 0x3f, 0x92, 0x35, 0xc5, 0x1a, 0x86, 0xca, 0x2a, 0x94, 0x70, 0x75, 0xa2, + 0xe3, 0x38, 0xf4, 0xd0, 0xc7, 0x90, 0x67, 0x64, 0x18, 0x9d, 0x6d, 0x69, 0x49, 0x54, 0x54, 0x59, + 0xe9, 0x35, 0x45, 0x0c, 0xeb, 0xbe, 0xdd, 0x6c, 0x31, 0x5f, 0x2d, 0x98, 0x7f, 0xc9, 0x43, 0x29, + 0x91, 0x31, 0x6a, 0xa7, 0xac, 0xa5, 0x21, 0x4d, 0xd0, 0xa7, 0x57, 0x28, 0xff, 0x6d, 0x73, 0x69, + 0xbe, 0x82, 0xda, 0x61, 0xc8, 0x9e, 0x2b, 0x97, 0xd5, 0x66, 0x3e, 0x8f, 0x42, 0x22, 0xee, 0x8c, + 0x1a, 0xe4, 0xa4, 0xe9, 0xd1, 0xae, 0x44, 0x35, 0xd0, 0xae, 0x70, 0x70, 0x31, 0x46, 0x1f, 0xb7, + 0xfb, 0x57, 0xbd, 0x74, 0x3c, 0x2a, 0x4e, 0xb1, 0xcd, 0x3f, 0x64, 0x00, 0x52, 0x2f, 0x6c, 0x43, + 0x36, 0xe5, 0xd4, 0x37, 0xe7, 0x1f, 0xb4, 0x21, 0x1d, 0xbb, 0x24, 0x8b, 0x6b, 0x35, 0xa4, 0x24, + 0xde, 0xbd, 0x12, 0xd6, 0x2d, 0x61, 0x3f, 0x4e, 0x99, 0xe7, 0x50, 0xc7, 0x52, 0x93, 0x52, 0x9b, + 0x51, 0x56, 0x31, 0x69, 0xcb, 0xd6, 0x7f, 0x67, 0x40, 0x56, 0x9a, 0xfd, 0x32, 0x14, 0xba, 0x07, + 0xcf, 0x9a, 0x7b, 0xdd, 0xed, 0xea, 0x0d, 0x84, 0x60, 0x69, 0xa7, 0xdb, 0xd9, 0xdb, 0xb6, 0x70, + 0xe7, 0xab, 0xe3, 0x2e, 0xee, 0x6c, 0x57, 0x0d, 0xb4, 0x0a, 0x2b, 0x7b, 0xbd, 0x76, 0xf3, 0x69, + 0xb7, 0x77, 0x30, 0x0e, 0x67, 0x50, 0x1d, 0x6a, 0xa9, 0x70, 0xbb, 0xb7, 0xbf, 0xdf, 0x39, 0xd8, + 0xee, 0x6c, 0x57, 0x17, 0xc6, 0x83, 0xf4, 0x0e, 0x45, 0x6f, 0x73, 0xaf, 0x9a, 0x45, 0xef, 0xc1, + 0xb2, 0x8a, 0xed, 0xf4, 0x70, 0xab, 0xbb, 0xbd, 0xdd, 0x39, 0xa8, 0xe6, 0x50, 0x15, 0x2a, 0xdd, + 0x83, 0x76, 0x6f, 0xff, 0xb0, 0xf9, 0xb4, 0xdb, 0xda, 0xeb, 0x54, 0xf3, 0x68, 0x05, 0x16, 0x8f, + 0x0f, 0x8e, 0x9a, 0x4f, 0xbb, 0x47, 0x3b, 0xdd, 0xa6, 0x08, 0x15, 0xcc, 0x3f, 0x67, 0xc6, 0xee, + 0xfc, 0x3b, 0x70, 0xd3, 0x26, 0x9c, 0x5a, 0xae, 0xcf, 0xa9, 0xcf, 0xdd, 0xc8, 0x7d, 0x41, 0xd5, + 0x0c, 0xb9, 0x54, 0x53, 0x11, 0xaf, 0x8a, 0xee, 0xee, 0xb8, 0x57, 0xce, 0x55, 0x16, 0xbc, 0x44, + 0xfa, 0x01, 0x89, 0xce, 0xea, 0x19, 0x69, 0x56, 0x2b, 0x71, 0xf0, 0x90, 0x44, 0x67, 0x02, 0xe4, + 0x50, 0x2f, 0x22, 0xd6, 0x30, 0x10, 0x6b, 0xce, 0xe5, 0x9a, 0x16, 0x71, 0x45, 0x06, 0x8f, 0x55, + 0x0c, 0x35, 0x00, 0x38, 0x0d, 0xad, 0x80, 0x79, 0xae, 0x3d, 0xd2, 0x77, 0xa1, 0x76, 0x46, 0x47, + 0x34, 0x3c, 0x94, 0x61, 0x5c, 0xe2, 0xf1, 0x23, 0x3a, 0x87, 0xf7, 0x83, 0x44, 0x6f, 0xd6, 0x58, + 0x0e, 0xbc, 0x9e, 0x97, 0x12, 0xfe, 0xfc, 0xaa, 0x8d, 0x9f, 0xa6, 0x56, 0xbc, 0x1a, 0x4c, 0x89, + 0xf2, 0xdd, 0x6c, 0xd1, 0xa8, 0x66, 0x70, 0x39, 0xf5, 0x16, 0xd3, 0x81, 0x82, 0xbe, 0xcb, 0xd1, + 0x67, 0x80, 0x88, 0x4a, 0xc3, 0xa1, 0xdc, 0x0e, 0xdd, 0x20, 0x71, 0x8c, 0x25, 0xbc, 0xa2, 0x7a, + 0xb6, 0xc7, 0x1d, 0xe8, 0x3e, 0x28, 0x9f, 0x76, 0xf1, 0xc3, 0x45, 0x7f, 0x28, 0x1e, 0x89, 0xbe, + 0xd8, 0xca, 0xfd, 0xcc, 0x80, 0xbc, 0xba, 0xfa, 0xdf, 0x4d, 0x05, 0x7f, 0x0c, 0xb7, 0x1c, 0x97, + 0x93, 0x13, 0x8f, 0x5a, 0xa2, 0x26, 0x58, 0x2c, 0x88, 0xdc, 0x41, 0xec, 0x71, 0x33, 0x72, 0x5b, + 0x6e, 0x6a, 0x80, 0xa8, 0x1d, 0xbd, 0x54, 0xb7, 0xf9, 0x35, 0xe4, 0x55, 0x3d, 0xb9, 0xdc, 0x8f, + 0x25, 0xde, 0x26, 0x33, 0xa7, 0xb7, 0x31, 0xff, 0x1f, 0x0a, 0xba, 0xe2, 0x8c, 0xd7, 0xc6, 0xb8, + 0x7a, 0x6d, 0xbe, 0x84, 0xc5, 0x89, 0x42, 0x73, 0x2d, 0xf2, 0x63, 0xa8, 0xa4, 0x6b, 0xcb, 0x75, + 0xb8, 0xeb, 0x3f, 0xc9, 0x42, 0xae, 0xf3, 0x2a, 0x0a, 0x89, 0xf9, 0x57, 0x03, 0xee, 0xc5, 0x8a, + 0xea, 0x08, 0x43, 0xe6, 0xfa, 0xfd, 0xb1, 0xae, 0xe2, 0x7f, 0xcf, 0xf6, 0xa0, 0x4a, 0x75, 0xa7, + 0x95, 0x5e, 0xb7, 0xf2, 0xd6, 0xbd, 0xd9, 0xff, 0x23, 0xc4, 0x37, 0xec, 0x72, 0x4c, 0x8d, 0x8f, + 0xea, 0x21, 0x54, 0x83, 0x90, 0x05, 0x8c, 0x53, 0x27, 0x19, 0x4d, 0x29, 0x69, 0xce, 0x3f, 0x04, + 0x96, 0x63, 0xba, 0x0e, 0x88, 0x0b, 0x34, 0x99, 0x85, 0x8e, 0x35, 0xfb, 0xc4, 0xf5, 0x79, 0x94, + 0x3a, 0x03, 0xe8, 0xcb, 0xc9, 0x4d, 0x9f, 0x2b, 0xf9, 0x44, 0x17, 0x7d, 0x48, 0x1f, 0x1e, 0x79, + 0x4b, 0x94, 0xb7, 0x3a, 0x13, 0xf9, 0xca, 0x15, 0x6d, 0x5c, 0x99, 0x47, 0xea, 0xc2, 0xe6, 0xf2, + 0xab, 0x77, 0xf2, 0x58, 0x3e, 0x87, 0xea, 0x45, 0xc0, 0x94, 0xcf, 0xe2, 0xef, 0xa7, 0x3f, 0x8b, + 0xaf, 0x57, 0x79, 0xc6, 0x9f, 0xd0, 0x5b, 0x3f, 0x80, 0x52, 0x22, 0x10, 0xf4, 0x3d, 0x28, 0x8f, + 0x57, 0x82, 0xa2, 0xda, 0xb4, 0xbd, 0x30, 0x57, 0xa7, 0xbe, 0x68, 0xc3, 0x78, 0x60, 0xb4, 0x5a, + 0xaf, 0xff, 0x79, 0xe7, 0xc6, 0xeb, 0x6f, 0xee, 0x18, 0x7f, 0xff, 0xe6, 0x8e, 0xf1, 0xdb, 0x7f, + 0xdd, 0x31, 0x7e, 0xf8, 0x60, 0xae, 0x7f, 0xaf, 0x52, 0x03, 0x9e, 0xe4, 0x65, 0xf8, 0xe1, 0x7f, + 0x02, 0x00, 0x00, 0xff, 0xff, 0x32, 0xb5, 0x1f, 0x73, 0x4a, 0x16, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2876,32 +2864,6 @@ func (m *Response_Validated_Binding) MarshalToSizedBuffer(dAtA []byte) (int, err dAtA[i] = 0x12 } } - if len(m.Constraints) > 0 { - for k := range m.Constraints { - v := m.Constraints[k] - baseI := i - if v != nil { - { - size, err := v.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintMaterialize(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintMaterialize(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintMaterialize(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0xa - } - } return len(dAtA) - i, nil } @@ -3779,19 +3741,6 @@ func (m *Response_Validated_Binding) ProtoSize() (n int) { } var l int _ = l - if len(m.Constraints) > 0 { - for k, v := range m.Constraints { - _ = k - _ = v - l = 0 - if v != nil { - l = v.ProtoSize() - l += 1 + sovMaterialize(uint64(l)) - } - mapEntrySize := 1 + len(k) + sovMaterialize(uint64(len(k))) + l - n += mapEntrySize + 1 + sovMaterialize(uint64(mapEntrySize)) - } - } if len(m.ResourcePath) > 0 { for _, s := range m.ResourcePath { l = len(s) @@ -7106,135 +7055,6 @@ func (m *Response_Validated_Binding) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: Binding: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Constraints", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMaterialize - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMaterialize - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthMaterialize - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Constraints == nil { - m.Constraints = make(map[string]*Response_Validated_Constraint) - } - var mapkey string - var mapvalue *Response_Validated_Constraint - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMaterialize - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMaterialize - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthMaterialize - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthMaterialize - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var mapmsglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMaterialize - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapmsglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if mapmsglen < 0 { - return ErrInvalidLengthMaterialize - } - postmsgIndex := iNdEx + mapmsglen - if postmsgIndex < 0 { - return ErrInvalidLengthMaterialize - } - if postmsgIndex > l { - return io.ErrUnexpectedEOF - } - mapvalue = &Response_Validated_Constraint{} - if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { - return err - } - iNdEx = postmsgIndex - } else { - iNdEx = entryPreIndex - skippy, err := skipMaterialize(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthMaterialize - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Constraints[mapkey] = mapvalue - iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ResourcePath", wireType) diff --git a/go/protocols/materialize/materialize.proto b/go/protocols/materialize/materialize.proto index 0b53e04f0c7..0bf9888c840 100644 --- a/go/protocols/materialize/materialize.proto +++ b/go/protocols/materialize/materialize.proto @@ -283,11 +283,11 @@ message Response { // Validated responds to Request.Validate. message Validated { // ProjectionConstraint pairs a projection field name with a single Constraint. - // Unlike the legacy `constraints` map, a repeated list of ProjectionConstraint - // allows multiple constraints to be expressed for the same field simultaneously. - // For example, INCOMPATIBLE and LOCATION_REQUIRED on the same field signals that - // the field is required but the existing destination column has an incompatible - // type, and a backfill is needed. + // A repeated list of ProjectionConstraint allows multiple constraints to be + // expressed for the same field simultaneously. For example, INCOMPATIBLE and + // LOCATION_REQUIRED on the same field signals that the field is required but + // the existing destination column has an incompatible type, and a backfill is + // needed. message ProjectionConstraint { string field = 1; Constraint constraint = 2; @@ -375,15 +375,11 @@ message Response { // want to preserve natural casing of fields, but they will still prohibit // (for example) "myField" and "MyField" from co-existing within a table. bool case_insensitive_fields = 5; - // Constraints imposed by the connector, keyed by field name. - // Projections of the CollectionSpec which are missing from - // constraints are implicitly forbidden. - // - // Deprecated: use projection_constraints instead. When projection_constraints - // is non-empty it is authoritative and this map is ignored by the control - // plane. This field is retained for backward compatibility with connectors - // that have not yet migrated to the list form. - map constraints = 1; + // Field tag 1 was `constraints`, a `map` keyed by + // field name. It was replaced by `projection_constraints`, which can + // express multiple constraints for the same field. + reserved 1; + reserved "constraints"; // Components of the resource path which fully qualify the resource // identified by this binding. // - For an RDBMS, this might be []{dbname, schema, table}. @@ -409,12 +405,11 @@ message Response { // Serialization policy to use for this binding. flow.SerPolicy ser_policy = 4; // Constraints on each projection, as a list that allows multiple constraints - // per projection field. When non-empty, this field is authoritative and the - // legacy `constraints` map is ignored. + // per projection field. Projections of the CollectionSpec which are missing + // from this list are implicitly forbidden. // - // Connectors should populate this field instead of `constraints`. The list - // form allows expressing compound requirements that a single constraint type - // cannot capture. For example, emitting both INCOMPATIBLE and + // The list form allows expressing compound requirements that a single + // constraint type cannot capture. For example, emitting both INCOMPATIBLE and // LOCATION_REQUIRED for the same field signals that the field is required // but the existing destination column is incompatible; a backfill is required. repeated ProjectionConstraint projection_constraints = 6;