Skip to content

Conversation

@scholtzan
Copy link
Collaborator

@scholtzan scholtzan commented Nov 20, 2025

Description

This PR adds a cache to DryRun which caches query and table schemas for a configurable amount of time and as long as the query content is the same. This reduces the schema generation time slightly which is executed before table artifact deploys (locally it saved 3 to 4 minutes).

Reviewer, please follow this checklist

@scholtzan scholtzan force-pushed the query-schema-generation-dryrun-caching branch from e4ec61f to e5f7754 Compare November 20, 2025 23:18
@dataops-ci-bot

This comment has been minimized.

@dataops-ci-bot

This comment has been minimized.

@scholtzan
Copy link
Collaborator Author

dryrun failing due to unrelated error in sql/moz-fx-data-shared-prod/telemetry_derived/newtab_merino_propensity_v1/query.sql

@scholtzan scholtzan force-pushed the query-schema-generation-dryrun-caching branch from af4028d to 8932af9 Compare November 26, 2025 22:26
@dataops-ci-bot

This comment has been minimized.

@scholtzan scholtzan force-pushed the query-schema-generation-dryrun-caching branch 2 times, most recently from 7f0b230 to 163789c Compare November 27, 2025 17:33
@dataops-ci-bot

This comment has been minimized.

@dataops-ci-bot

This comment has been minimized.

@scholtzan
Copy link
Collaborator Author

I won't merge this before next week.

@scholtzan scholtzan marked this pull request as ready for review November 27, 2025 18:11
@scholtzan scholtzan requested a review from a team as a code owner November 27, 2025 18:11
@scholtzan scholtzan changed the title Query schema generation dryrun caching Dryrun caching Nov 27, 2025
Copy link
Contributor

@BenWu BenWu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the test failure is due to reset-stage-env not having a dependency on test_routines so stage udfs are getting deleted

try:
# write to temporary file first, then atomically rename
# this prevents race conditions where readers get partial files
temp_file = Path(str(cache_file) + f".tmp.{os.getpid()}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a possible edge case if this is called from a thread pool where the threads have the same PID an extra random string might be better.

Suggested change
temp_file = Path(str(cache_file) + f".tmp.{os.getpid()}")
temp_file = Path(str(cache_file) + f".tmp.{os.getpid()}.{os.urandom(4).hex()}")

table_cache_key = hashlib.sha256(table_identifier.encode()).hexdigest()
cache_file = cache_dir / f"table_metadata_{table_cache_key}.pkl"

try:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache read and write code is almost identical between the metadata and the dry run result. Moving the common code into a common function would make this easier to maintain

@scholtzan scholtzan force-pushed the query-schema-generation-dryrun-caching branch from 7fbd6bb to 63b77e5 Compare November 27, 2025 21:17
@dataops-ci-bot
Copy link

Integration report for "Address feedback"

sql.diff

Click to expand!
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/fenix/client_deduplication/schema.yaml /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/fenix/client_deduplication/schema.yaml
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/fenix/client_deduplication/schema.yaml	2025-11-27 21:25:31.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/fenix/client_deduplication/schema.yaml	2025-11-27 21:24:24.000000000 +0000
@@ -7,245 +7,182 @@
   mode: NULLABLE
   type: STRING
   description: Normalized channel name
-- name: submission_date
+- mode: NULLABLE
+  name: submission_date
   type: DATE
-  mode: NULLABLE
-- name: normalized_app_id
+- mode: NULLABLE
+  name: normalized_app_id
   type: STRING
-  mode: NULLABLE
-- name: additional_properties
+- mode: NULLABLE
+  name: additional_properties
   type: STRING
-  mode: NULLABLE
-- name: client_info
+- mode: NULLABLE
+  name: client_info
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: android_sdk_version
     type: STRING
-    mode: NULLABLE
   - name: app_build
     type: STRING
-    mode: NULLABLE
   - name: app_channel
     type: STRING
-    mode: NULLABLE
   - name: app_display_version
     type: STRING
-    mode: NULLABLE
   - name: architecture
     type: STRING
-    mode: NULLABLE
   - name: build_date
     type: STRING
-    mode: NULLABLE
   - name: client_id
     type: STRING
-    mode: NULLABLE
   - name: device_manufacturer
     type: STRING
-    mode: NULLABLE
   - name: device_model
     type: STRING
-    mode: NULLABLE
   - name: first_run_date
     type: STRING
-    mode: NULLABLE
   - name: locale
     type: STRING
-    mode: NULLABLE
   - name: os
     type: STRING
-    mode: NULLABLE
   - name: os_version
     type: STRING
-    mode: NULLABLE
   - name: telemetry_sdk_build
     type: STRING
-    mode: NULLABLE
   - name: windows_build_number
     type: INTEGER
-    mode: NULLABLE
   - name: session_count
     type: INTEGER
-    mode: NULLABLE
   - name: session_id
     type: STRING
-    mode: NULLABLE
   - name: attribution
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: campaign
       type: STRING
-      mode: NULLABLE
     - name: content
       type: STRING
-      mode: NULLABLE
     - name: medium
       type: STRING
-      mode: NULLABLE
     - name: source
       type: STRING
-      mode: NULLABLE
     - name: term
       type: STRING
-      mode: NULLABLE
-    - name: ext
+    - mode: NULLABLE
+      name: ext
       type: JSON
-      mode: NULLABLE
   - name: distribution
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: name
       type: STRING
-      mode: NULLABLE
-    - name: ext
+    - mode: NULLABLE
+      name: ext
       type: JSON
-      mode: NULLABLE
-- name: document_id
+- mode: NULLABLE
+  name: document_id
   type: STRING
-  mode: NULLABLE
 - name: events
   type: RECORD
   mode: REPEATED
   fields:
   - name: category
     type: STRING
-    mode: NULLABLE
   - name: extra
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: STRING
-      mode: NULLABLE
   - name: name
     type: STRING
-    mode: NULLABLE
   - name: timestamp
     type: INTEGER
-    mode: NULLABLE
-- name: metadata
+- mode: NULLABLE
+  name: metadata
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: geo
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: city
       type: STRING
-      mode: NULLABLE
     - name: country
       type: STRING
-      mode: NULLABLE
     - name: db_version
       type: STRING
-      mode: NULLABLE
     - name: subdivision1
       type: STRING
-      mode: NULLABLE
     - name: subdivision2
       type: STRING
-      mode: NULLABLE
   - name: header
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: date
       type: STRING
-      mode: NULLABLE
     - name: dnt
       type: STRING
-      mode: NULLABLE
     - name: x_debug_id
       type: STRING
-      mode: NULLABLE
     - name: x_foxsec_ip_reputation
       type: STRING
-      mode: NULLABLE
     - name: x_lb_tags
       type: STRING
-      mode: NULLABLE
     - name: x_pingsender_version
       type: STRING
-      mode: NULLABLE
     - name: x_source_tags
       type: STRING
-      mode: NULLABLE
     - name: x_telemetry_agent
       type: STRING
-      mode: NULLABLE
     - name: parsed_date
       type: TIMESTAMP
-      mode: NULLABLE
     - name: parsed_x_source_tags
       type: STRING
       mode: REPEATED
     - name: parsed_x_lb_tags
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: tls_version
         type: STRING
-        mode: NULLABLE
       - name: tls_cipher_hex
         type: STRING
-        mode: NULLABLE
   - name: isp
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: db_version
       type: STRING
-      mode: NULLABLE
     - name: name
       type: STRING
-      mode: NULLABLE
     - name: organization
       type: STRING
-      mode: NULLABLE
   - name: user_agent
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: browser
       type: STRING
-      mode: NULLABLE
     - name: os
       type: STRING
-      mode: NULLABLE
     - name: version
       type: STRING
-      mode: NULLABLE
-- name: metrics
+- mode: NULLABLE
+  name: metrics
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: boolean
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: client_deduplication_valid_advertising_id
       type: BOOLEAN
-      mode: NULLABLE
   - name: counter
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: events_normal_and_private_uri_count
       type: INTEGER
-      mode: NULLABLE
     - name: metrics_tabs_open_count
       type: INTEGER
-      mode: NULLABLE
   - name: labeled_counter
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: browser_search_ad_clicks
       type: RECORD
@@ -253,185 +190,148 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: browser_search_in_content
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: browser_search_with_ads
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: glean_error_invalid_label
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: glean_error_invalid_overflow
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: glean_error_invalid_state
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: glean_error_invalid_value
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
     - name: metrics_search_count
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
   - name: string
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: activation_identifier
       type: STRING
-      mode: NULLABLE
     - name: client_deduplication_experiment_timeframe
       type: STRING
-      mode: NULLABLE
     - name: search_default_engine_code
       type: STRING
-      mode: NULLABLE
     - name: search_default_engine_name
       type: STRING
-      mode: NULLABLE
     - name: client_deduplication_hashed_gaid
       type: STRING
-      mode: NULLABLE
     - name: glean_client_annotation_experimentation_id
       type: STRING
-      mode: NULLABLE
   - name: string_list
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: glean_ping_uploader_capabilities
       type: STRING
       mode: REPEATED
-- name: normalized_app_name
+- mode: NULLABLE
+  name: normalized_app_name
   type: STRING
-  mode: NULLABLE
-- name: normalized_country_code
+- mode: NULLABLE
+  name: normalized_country_code
   type: STRING
-  mode: NULLABLE
-- name: normalized_os
+- mode: NULLABLE
+  name: normalized_os
   type: STRING
-  mode: NULLABLE
-- name: normalized_os_version
+- mode: NULLABLE
+  name: normalized_os_version
   type: STRING
-  mode: NULLABLE
-- name: ping_info
+- mode: NULLABLE
+  name: ping_info
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: end_time
     type: STRING
-    mode: NULLABLE
   - name: experiments
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: branch
         type: STRING
-        mode: NULLABLE
       - name: extra
         type: RECORD
-        mode: NULLABLE
         fields:
         - name: type
           type: STRING
-          mode: NULLABLE
         - name: enrollment_id
           type: STRING
-          mode: NULLABLE
   - name: ping_type
     type: STRING
-    mode: NULLABLE
   - name: reason
     type: STRING
-    mode: NULLABLE
   - name: seq
     type: INTEGER
-    mode: NULLABLE
   - name: start_time
     type: STRING
-    mode: NULLABLE
   - name: parsed_start_time
     type: TIMESTAMP
-    mode: NULLABLE
   - name: parsed_end_time
     type: TIMESTAMP
-    mode: NULLABLE
-- name: sample_id
+- mode: NULLABLE
+  name: sample_id
   type: INTEGER
-  mode: NULLABLE
-- name: submission_timestamp
+- mode: NULLABLE
+  name: submission_timestamp
   type: TIMESTAMP
-  mode: NULLABLE
-- name: app_version_major
+- mode: NULLABLE
+  name: app_version_major
   type: NUMERIC
-  mode: NULLABLE
-- name: app_version_minor
+- mode: NULLABLE
+  name: app_version_minor
   type: NUMERIC
-  mode: NULLABLE
-- name: app_version_patch
+- mode: NULLABLE
+  name: app_version_patch
   type: NUMERIC
-  mode: NULLABLE
-- name: is_bot_generated
+- mode: NULLABLE
+  name: is_bot_generated
   type: BOOLEAN
-  mode: NULLABLE
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/schema.yaml /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/schema.yaml
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/schema.yaml	2025-11-27 21:25:31.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/schema.yaml	2025-11-27 21:24:18.000000000 +0000
@@ -7,142 +7,79 @@
   mode: NULLABLE
   type: STRING
   description: Normalized channel name
-- name: submission_date
+- mode: NULLABLE
+  name: submission_date
   type: DATE
-  mode: NULLABLE
-- name: additional_properties
+- mode: NULLABLE
+  name: additional_properties
   type: STRING
-  mode: NULLABLE
   description: A JSON string containing any payload properties not present in the
     schema
-- name: client_info
+- mode: NULLABLE
+  name: client_info
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: android_sdk_version
     type: STRING
-    mode: NULLABLE
-    description: The optional Android specific SDK version of the software running
-      on this hardware device.
   - name: app_build
     type: STRING
-    mode: NULLABLE
-    description: The build identifier generated by the CI system (e.g. "1234/A").
-      For language bindings that provide automatic detection for this value, (e.g.
-      Android/Kotlin), in the unlikely event that the build identifier can not be
-      retrieved from the OS, it is set to "inaccessible". For other language bindings,
-      if the value was not provided through configuration, this metric gets set to
-      `Unknown`.
   - name: app_channel
     type: STRING
-    mode: NULLABLE
-    description: The channel the application is being distributed on.
   - name: app_display_version
     type: STRING
-    mode: NULLABLE
-    description: The user visible version string (e.g. "1.0.3").  In the unlikely
-      event that the display version can not be retrieved, it is set to "inaccessible".
   - name: architecture
     type: STRING
-    mode: NULLABLE
-    description: The architecture of the device, (e.g. "arm", "x86").
   - name: client_id
     type: STRING
-    mode: NULLABLE
-    description: A UUID uniquely identifying the client.
   - name: device_manufacturer
     type: STRING
-    mode: NULLABLE
-    description: The manufacturer of the device the application is running on. Not
-      set if the device manufacturer can't be determined (e.g. on Desktop).
   - name: device_model
     type: STRING
-    mode: NULLABLE
-    description: The model of the device the application is running on. On Android,
-      this is Build.MODEL, the user-visible marketing name, like "Pixel 2 XL". Not
-      set if the device model can't be determined (e.g. on Desktop).
   - name: first_run_date
     type: STRING
-    mode: NULLABLE
-    description: The date of the first run of the application.
   - name: locale
     type: STRING
-    mode: NULLABLE
-    description: The locale of the application during initialization (e.g. "es-ES").
-      If the locale can't be determined on the system, the value is ["und"](https://unicode.org/reports/tr35/#Unknown_or_Invalid_Identifiers),
-      to indicate "undetermined".
   - name: os
     type: STRING
-    mode: NULLABLE
-    description: 'The name of the operating system. Possible values: Android, iOS,
-      Linux, Darwin, Windows, FreeBSD, NetBSD, OpenBSD, Solaris, unknown'
   - name: os_version
     type: STRING
-    mode: NULLABLE
-    description: The user-visible version of the operating system (e.g. "1.2.3").
-      If the version detection fails, this metric gets set to `Unknown`.
   - name: telemetry_sdk_build
     type: STRING
-    mode: NULLABLE
-    description: The version of the Glean SDK
   - name: build_date
     type: STRING
-    mode: NULLABLE
-    description: The date & time the application was built
   - name: windows_build_number
     type: INTEGER
-    mode: NULLABLE
-    description: The optional Windows build number, reported by Windows (e.g. 22000)
-      and not set for other platforms
   - name: session_count
     type: INTEGER
-    mode: NULLABLE
-    description: An optional running counter of the number of sessions for a client.
   - name: session_id
     type: STRING
-    mode: NULLABLE
-    description: An optional UUID uniquely identifying the client's current session.
   - name: attribution
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: campaign
       type: STRING
-      mode: NULLABLE
-      description: The attribution campaign (e.g. 'mozilla-org').
     - name: content
       type: STRING
-      mode: NULLABLE
-      description: The attribution content (e.g. 'firefoxview').
     - name: medium
       type: STRING
-      mode: NULLABLE
-      description: The attribution medium (e.g. 'organic' for a search engine).
     - name: source
       type: STRING
-      mode: NULLABLE
-      description: The attribution source (e.g. 'google-play').
     - name: term
       type: STRING
-      mode: NULLABLE
-      description: The attribution term (e.g. 'browser with developer tools for android').
     - name: ext
       type: JSON
       mode: NULLABLE
   - name: distribution
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: name
       type: STRING
-      mode: NULLABLE
-      description: The distribution name (e.g. 'MozillaOnline').
     - name: ext
       type: JSON
       mode: NULLABLE
-- name: document_id
+- mode: NULLABLE
+  name: document_id
   type: STRING
-  mode: NULLABLE
   description: The document ID specified in the URI when the client sent this message
 - name: events
   type: RECORD
@@ -150,88 +87,54 @@
   fields:
   - name: category
     type: STRING
-    mode: NULLABLE
   - name: extra
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: STRING
-      mode: NULLABLE
   - name: name
     type: STRING
-    mode: NULLABLE
   - name: timestamp
     type: INTEGER
-    mode: NULLABLE
-- name: metadata
+- mode: NULLABLE
+  name: metadata
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: geo
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: city
       type: STRING
-      mode: NULLABLE
     - name: country
       type: STRING
-      mode: NULLABLE
-      description: An ISO 3166-1 alpha-2 country code
     - name: db_version
       type: STRING
-      mode: NULLABLE
-      description: The specific geo database version used for this lookup
     - name: subdivision1
       type: STRING
-      mode: NULLABLE
-      description: First major country subdivision, typically a state, province, or
-        county
     - name: subdivision2
       type: STRING
-      mode: NULLABLE
-      description: Second major country subdivision; not applicable for most countries
-    description: Results of a geographic lookup based on the client's IP address
   - name: header
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: date
       type: STRING
-      mode: NULLABLE
-      description: Date HTTP header
     - name: dnt
       type: STRING
-      mode: NULLABLE
-      description: DNT (Do Not Track) HTTP header
     - name: x_debug_id
       type: STRING
-      mode: NULLABLE
-      description: X-Debug-Id HTTP header
     - name: x_pingsender_version
       type: STRING
-      mode: NULLABLE
-      description: X-PingSender-Version HTTP header
     - name: x_source_tags
       type: STRING
-      mode: NULLABLE
-      description: X-Source-Tags HTTP header
     - name: x_telemetry_agent
       type: STRING
-      mode: NULLABLE
-      description: X-Telemetry-Agent HTTP header
     - name: x_foxsec_ip_reputation
       type: STRING
-      mode: NULLABLE
-      description: X-Foxsec-IP-Reputation header
     - name: x_lb_tags
       type: STRING
-      mode: NULLABLE
-      description: X-LB-Tags HTTP header
     - name: parsed_date
       type: TIMESTAMP
       mode: NULLABLE
@@ -248,344 +151,136 @@
       - name: tls_cipher_hex
         type: STRING
         mode: NULLABLE
-    description: Headers included in the client's HTTP request
   - name: user_agent
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: browser
       type: STRING
-      mode: NULLABLE
     - name: os
       type: STRING
-      mode: NULLABLE
     - name: version
       type: STRING
-      mode: NULLABLE
-    description: Parsed components of the client's user agent string
   - name: isp
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: db_version
       type: STRING
-      mode: NULLABLE
-      description: The specific geo ISP database version used for this lookup
     - name: name
       type: STRING
-      mode: NULLABLE
-      description: The name of the ISP associated with the client's IP address
     - name: organization
       type: STRING
-      mode: NULLABLE
-      description: The name of a specific business entity associated with the client's
-        IP address when available; otherwise the ISP name
-    description: Results of ISP lookup based on the client's IP address
-- name: metrics
+- mode: NULLABLE
+  name: metrics
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: boolean
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_addons_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_bookmarks_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_fxa_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_fxa_has_custom_idp_server
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Is custom idp server configured?
-
-        '
     - name: migration_fxa_has_custom_token_server
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Is custom token server configured?
-
-        '
     - name: migration_gecko_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_history_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_logins_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_open_tabs_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_settings_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_settings_telemetry_enabled
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Is telemetry enabled after this migration?
-
-        '
     - name: migration_telemetry_identifiers_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_search_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
     - name: migration_pinned_sites_any_failures
       type: BOOLEAN
-      mode: NULLABLE
-      description: 'Did this migration encounter any failures (exceptions)?
-
-        '
   - name: counter
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_addons_failed_addons
       type: INTEGER
-      mode: NULLABLE
-      description: 'How many addons failed to migrate?
-
-        '
     - name: migration_addons_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_addons_migrated_addons
       type: INTEGER
-      mode: NULLABLE
-      description: 'How many addons were migrated?
-
-        '
     - name: migration_addons_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_bookmarks_detected
       type: INTEGER
-      mode: NULLABLE
-      description: 'Total number of detected bookmarks prior to a migration.
-
-        '
     - name: migration_fxa_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_fxa_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_history_detected
       type: INTEGER
-      mode: NULLABLE
-      description: 'Total number of detected history items prior to a migration.
-
-        '
     - name: migration_logins_detected
       type: INTEGER
-      mode: NULLABLE
-      description: 'How many logins were detected?
-
-        '
     - name: migration_logins_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_logins_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_logins_unsupported_db_version
       type: INTEGER
-      mode: NULLABLE
-      description: 'If we couldn''t migrate due to an unsupported db version, what
-        was it?
-
-        '
     - name: migration_open_tabs_detected
       type: INTEGER
-      mode: NULLABLE
-      description: 'Number of detected open tabs.
-
-        '
     - name: migration_open_tabs_migrated
       type: INTEGER
-      mode: NULLABLE
-      description: 'Number of migrated open tabs.
-
-        '
     - name: migration_settings_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_settings_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_bookmarks_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_bookmarks_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_gecko_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_gecko_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_history_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_history_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_open_tabs_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_open_tabs_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_telemetry_identifiers_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_telemetry_identifiers_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_search_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_search_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_pinned_sites_detected_pinned_sites
       type: INTEGER
-      mode: NULLABLE
-      description: 'How many pinned sites were detected?
-
-        '
     - name: migration_pinned_sites_failure_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration failed. See codes in TelemetryHelpers.kt
-
-        '
     - name: migration_pinned_sites_migrated_pinned_sites
       type: INTEGER
-      mode: NULLABLE
-      description: 'How many pinned sites were migrated?
-
-        '
     - name: migration_pinned_sites_success_reason
       type: INTEGER
-      mode: NULLABLE
-      description: 'Why this migration succeeded. See codes in TelemetryHelpers.kt
-
-        '
   - name: datetime
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_telemetry_identifiers_fennec_profile_creation_date
       type: STRING
-      mode: NULLABLE
-      description: 'Records when the migrated Fennec profile was created.
-
-        '
     - name: raw_migration_telemetry_identifiers_fennec_profile_creation_date
       type: STRING
       mode: NULLABLE
   - name: labeled_counter
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: glean_error_invalid_label
       type: RECORD
@@ -593,10 +288,8 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'Counts the number of times a metric was set with an invalid label.
 
         The labels are the `category.name` identifier of the metric.
@@ -608,10 +301,8 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'Counts the number of times a metric was set a value that overflowed.
 
         The labels are the `category.name` identifier of the metric.
@@ -623,10 +314,8 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'Counts the number of times a timing metric was used incorrectly.
 
         The labels are the `category.name` identifier of the metric.
@@ -638,10 +327,8 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'Counts the number of times a metric was set to an invalid value.
 
         The labels are the `category.name` identifier of the metric.
@@ -653,10 +340,8 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'Counters describing how bookmark migration went; should add-up
         to ''migration.bookmarks.detected''.
 
@@ -667,10 +352,8 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'Counters describing how history migration went; should add-up
         to ''migration.history.detected''.
 
@@ -681,16 +364,13 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
       description: 'How many logins failed to be migrated, and in which ways?
 
         '
   - name: labeled_string
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_migration_versions
       type: RECORD
@@ -698,371 +378,235 @@
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: STRING
-        mode: NULLABLE
       description: 'Versions of the migrations which were executed.
 
         '
   - name: string
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_fxa_bad_auth_state
       type: STRING
-      mode: NULLABLE
-      description: 'If we couldn''t migrate due to a bad auth state, what was it?
-
-        '
     - name: migration_fxa_failure_reason_rust
       type: STRING
-      mode: NULLABLE
-      description: 'Why this migration failed (rust exception).
-
-        '
     - name: migration_fxa_unsupported_account_version
       type: STRING
-      mode: NULLABLE
-      description: 'What is the detected unsupported account version?
-
-        '
     - name: migration_fxa_unsupported_pickle_version
       type: STRING
-      mode: NULLABLE
-      description: 'What is the detected unsupported pickle version?
-
-        '
     - name: migration_fxa_unsupported_state_version
       type: STRING
-      mode: NULLABLE
-      description: 'What is the detected unsupported state version?
-
-        '
     - name: glean_client_annotation_experimentation_id
       type: STRING
-      mode: NULLABLE
-      description: 'An experimentation identifier derived and provided by the application
-
-        for the purpose of experimentation enrollment.
-
-        '
   - name: timespan
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_bookmarks_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Duration of the bookmarks migration (Rust operations).
-
-        '
     - name: migration_history_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Duration of the history migration (Rust operations).
-
-        '
     - name: migration_addons_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_bookmarks_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_fxa_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_gecko_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_history_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_logins_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_open_tabs_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_pinned_sites_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_search_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_settings_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
     - name: migration_telemetry_identifiers_total_duration
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: time_unit
         type: STRING
-        mode: NULLABLE
       - name: value
         type: INTEGER
-        mode: NULLABLE
-      description: 'Total duration of this migration.
-
-        '
   - name: uuid
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: migration_telemetry_identifiers_fennec_client_id
       type: STRING
-      mode: NULLABLE
-      description: 'Records clientID of the migrated Fennec profile.
-
-        '
   - name: jwe
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: STRING
-      mode: NULLABLE
   - name: labeled_rate
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: RECORD
       mode: REPEATED
       fields:
       - name: key
         type: STRING
-        mode: NULLABLE
       - name: value
         type: RECORD
-        mode: NULLABLE
         fields:
         - name: denominator
           type: INTEGER
-          mode: NULLABLE
         - name: numerator
           type: INTEGER
-          mode: NULLABLE
   - name: url
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: STRING
-      mode: NULLABLE
   - name: text
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: STRING
-      mode: NULLABLE
   - name: string_list
     type: RECORD
-    mode: NULLABLE
     fields:
     - name: glean_ping_uploader_capabilities
       type: STRING
       mode: REPEATED
-- name: normalized_app_name
+- mode: NULLABLE
+  name: normalized_app_name
   type: STRING
-  mode: NULLABLE
   description: Set to "Other" if this message contained an unrecognized app name
-- name: normalized_country_code
+- mode: NULLABLE
+  name: normalized_country_code
   type: STRING
-  mode: NULLABLE
   description: An ISO 3166-1 alpha-2 country code
-- name: normalized_os
+- mode: NULLABLE
+  name: normalized_os
   type: STRING
-  mode: NULLABLE
   description: Set to "Other" if this message contained an unrecognized OS name
-- name: normalized_os_version
+- mode: NULLABLE
+  name: normalized_os_version
   type: STRING
-  mode: NULLABLE
-- name: ping_info
+- mode: NULLABLE
+  name: ping_info
   type: RECORD
-  mode: NULLABLE
   fields:
   - name: end_time
     type: STRING
-    mode: NULLABLE
   - name: experiments
     type: RECORD
     mode: REPEATED
     fields:
     - name: key
       type: STRING
-      mode: NULLABLE
     - name: value
       type: RECORD
-      mode: NULLABLE
       fields:
       - name: branch
         type: STRING
-        mode: NULLABLE
       - name: extra
         type: RECORD
-        mode: NULLABLE
         fields:
         - name: type
           type: STRING
-          mode: NULLABLE
         - name: enrollment_id
           type: STRING
-          mode: NULLABLE
   - name: ping_type
     type: STRING
-    mode: NULLABLE
   - name: reason
     type: STRING
-    mode: NULLABLE
   - name: seq
     type: INTEGER
-    mode: NULLABLE
   - name: start_time
     type: STRING
-    mode: NULLABLE
   - name: parsed_start_time
     type: TIMESTAMP
     mode: NULLABLE
   - name: parsed_end_time
     type: TIMESTAMP
     mode: NULLABLE
-- name: sample_id
+- mode: NULLABLE
+  name: sample_id
   type: INTEGER
-  mode: NULLABLE
   description: Hashed version of client_id (if present) useful for partitioning; ranges
     from 0 to 99
-- name: submission_timestamp
+- mode: NULLABLE
+  name: submission_timestamp
   type: TIMESTAMP
-  mode: NULLABLE
   description: Time when the ingestion edge server accepted this message
 - name: app_version_major
   type: NUMERIC
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/view.sql /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/view.sql
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/view.sql	2025-11-27 21:25:31.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/fenix/migration/view.sql	2025-11-27 21:24:18.000000000 +0000
@@ -41,13 +41,7 @@
   document_id,
   events,
   STRUCT(
-    STRUCT(
-      metadata.geo.city,
-      metadata.geo.country,
-      metadata.geo.db_version,
-      metadata.geo.subdivision1,
-      metadata.geo.subdivision2
-    ) AS `geo`,
+    metadata.geo,
     STRUCT(
       metadata.header.date,
       metadata.header.dnt,
@@ -62,7 +56,7 @@
       CAST(NULL AS STRUCT<`tls_version` STRING, `tls_cipher_hex` STRING>) AS `parsed_x_lb_tags`
     ) AS `header`,
     metadata.user_agent,
-    STRUCT(metadata.isp.db_version, metadata.isp.name, metadata.isp.organization) AS `isp`
+    metadata.isp
   ) AS `metadata`,
   STRUCT(
     STRUCT(
@@ -195,10 +189,79 @@
   ).channel AS normalized_channel,
   CAST(NULL AS DATE) AS `submission_date`,
   additional_properties,
-  client_info,
+  STRUCT(
+    client_info.android_sdk_version,
+    client_info.app_build,
+    client_info.app_channel,
+    client_info.app_display_version,
+    client_info.architecture,
+    client_info.client_id,
+    client_info.device_manufacturer,
+    client_info.device_model,
+    client_info.first_run_date,
+    client_info.locale,
+    client_info.os,
+    client_info.os_version,
+    client_info.telemetry_sdk_build,
+    client_info.build_date,
+    client_info.windows_build_number,
+    client_info.session_count,
+    client_info.session_id,
+    STRUCT(
+      client_info.attribution.campaign,
+      client_info.attribution.content,
+      client_info.attribution.medium,
+      client_info.attribution.source,
+      client_info.attribution.term,
+      client_info.attribution.ext
+    ) AS `attribution`,
+    STRUCT(client_info.distribution.name, client_info.distribution.ext) AS `distribution`
+  ) AS `client_info`,
   document_id,
-  events,
-  metadata,
+  ARRAY(
+    SELECT
+      STRUCT(
+        events.category,
+        ARRAY(
+          SELECT
+            STRUCT(extra.key, extra.value)
+          FROM
+            UNNEST(events.extra) AS `extra`
+        ) AS `extra`,
+        events.name,
+        events.timestamp
+      )
+    FROM
+      UNNEST(events) AS `events`
+  ) AS `events`,
+  STRUCT(
+    STRUCT(
+      metadata.geo.city,
+      metadata.geo.country,
+      metadata.geo.db_version,
+      metadata.geo.subdivision1,
+      metadata.geo.subdivision2
+    ) AS `geo`,
+    STRUCT(
+      metadata.header.date,
+      metadata.header.dnt,
+      metadata.header.x_debug_id,
+      metadata.header.x_pingsender_version,
+      metadata.header.x_source_tags,
+      metadata.header.x_telemetry_agent,
+      metadata.header.x_foxsec_ip_reputation,
+      metadata.header.x_lb_tags,
+      metadata.header.parsed_date,
+      metadata.header.parsed_x_source_tags,
+      metadata.header.parsed_x_lb_tags
+    ) AS `header`,
+    STRUCT(
+      metadata.user_agent.browser,
+      metadata.user_agent.os,
+      metadata.user_agent.version
+    ) AS `user_agent`,
+    STRUCT(metadata.isp.db_version, metadata.isp.name, metadata.isp.organization) AS `isp`
+  ) AS `metadata`,
   STRUCT(
     STRUCT(
       metrics.boolean.migration_addons_any_failures,
@@ -255,15 +318,65 @@
       metrics.datetime.raw_migration_telemetry_identifiers_fennec_profile_creation_date
     ) AS `datetime`,
     STRUCT(
-      metrics.labeled_counter.glean_error_invalid_label,
-      metrics.labeled_counter.glean_error_invalid_overflow,
-      metrics.labeled_counter.glean_error_invalid_state,
-      metrics.labeled_counter.glean_error_invalid_value,
-      metrics.labeled_counter.migration_bookmarks_migrated,
-      metrics.labeled_counter.migration_history_migrated,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_label.key, glean_error_invalid_label.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_label) AS `glean_error_invalid_label`
+      ) AS `glean_error_invalid_label`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_overflow.key, glean_error_invalid_overflow.value)
+        FROM
+          UNNEST(
+            metrics.labeled_counter.glean_error_invalid_overflow
+          ) AS `glean_error_invalid_overflow`
+      ) AS `glean_error_invalid_overflow`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_state.key, glean_error_invalid_state.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_state) AS `glean_error_invalid_state`
+      ) AS `glean_error_invalid_state`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_value.key, glean_error_invalid_value.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_value) AS `glean_error_invalid_value`
+      ) AS `glean_error_invalid_value`,
+      ARRAY(
+        SELECT
+          STRUCT(migration_bookmarks_migrated.key, migration_bookmarks_migrated.value)
+        FROM
+          UNNEST(
+            metrics.labeled_counter.migration_bookmarks_migrated
+          ) AS `migration_bookmarks_migrated`
+      ) AS `migration_bookmarks_migrated`,
+      ARRAY(
+        SELECT
+          STRUCT(migration_history_migrated.key, migration_history_migrated.value)
+        FROM
+          UNNEST(metrics.labeled_counter.migration_history_migrated) AS `migration_history_migrated`
+      ) AS `migration_history_migrated`,
+      ARRAY(
+        SELECT
+          STRUCT(migration_logins_failure_counts.key, migration_logins_failure_counts.value)
+        FROM
+          UNNEST(
       metrics.labeled_counter.migration_logins_failure_counts
+          ) AS `migration_logins_failure_counts`
+      ) AS `migration_logins_failure_counts`
     ) AS `labeled_counter`,
-    STRUCT(metrics.labeled_string.migration_migration_versions) AS `labeled_string`,
+    STRUCT(
+      ARRAY(
+        SELECT
+          STRUCT(migration_migration_versions.key, migration_migration_versions.value)
+        FROM
+          UNNEST(
+            metrics.labeled_string.migration_migration_versions
+          ) AS `migration_migration_versions`
+      ) AS `migration_migration_versions`
+    ) AS `labeled_string`,
     STRUCT(
       metrics.string.migration_fxa_bad_auth_state,
       metrics.string.migration_fxa_failure_reason_rust,
@@ -273,19 +386,58 @@
       metrics.string.glean_client_annotation_experimentation_id
     ) AS `string`,
     STRUCT(
-      metrics.timespan.migration_bookmarks_duration,
-      metrics.timespan.migration_history_duration,
-      metrics.timespan.migration_addons_total_duration,
-      metrics.timespan.migration_bookmarks_total_duration,
-      metrics.timespan.migration_fxa_total_duration,
-      metrics.timespan.migration_gecko_total_duration,
-      metrics.timespan.migration_history_total_duration,
-      metrics.timespan.migration_logins_total_duration,
-      metrics.timespan.migration_open_tabs_total_duration,
-      metrics.timespan.migration_pinned_sites_total_duration,
-      metrics.timespan.migration_search_total_duration,
-      metrics.timespan.migration_settings_total_duration,
-      metrics.timespan.migration_telemetry_identifiers_total_duration
+      STRUCT(
+        metrics.timespan.migration_bookmarks_duration.time_unit,
+        metrics.timespan.migration_bookmarks_duration.value
+      ) AS `migration_bookmarks_duration`,
+      STRUCT(
+        metrics.timespan.migration_history_duration.time_unit,
+        metrics.timespan.migration_history_duration.value
+      ) AS `migration_history_duration`,
+      STRUCT(
+        metrics.timespan.migration_addons_total_duration.time_unit,
+        metrics.timespan.migration_addons_total_duration.value
+      ) AS `migration_addons_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_bookmarks_total_duration.time_unit,
+        metrics.timespan.migration_bookmarks_total_duration.value
+      ) AS `migration_bookmarks_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_fxa_total_duration.time_unit,
+        metrics.timespan.migration_fxa_total_duration.value
+      ) AS `migration_fxa_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_gecko_total_duration.time_unit,
+        metrics.timespan.migration_gecko_total_duration.value
+      ) AS `migration_gecko_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_history_total_duration.time_unit,
+        metrics.timespan.migration_history_total_duration.value
+      ) AS `migration_history_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_logins_total_duration.time_unit,
+        metrics.timespan.migration_logins_total_duration.value
+      ) AS `migration_logins_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_open_tabs_total_duration.time_unit,
+        metrics.timespan.migration_open_tabs_total_duration.value
+      ) AS `migration_open_tabs_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_pinned_sites_total_duration.time_unit,
+        metrics.timespan.migration_pinned_sites_total_duration.value
+      ) AS `migration_pinned_sites_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_search_total_duration.time_unit,
+        metrics.timespan.migration_search_total_duration.value
+      ) AS `migration_search_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_settings_total_duration.time_unit,
+        metrics.timespan.migration_settings_total_duration.value
+      ) AS `migration_settings_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_telemetry_identifiers_total_duration.time_unit,
+        metrics.timespan.migration_telemetry_identifiers_total_duration.value
+      ) AS `migration_telemetry_identifiers_total_duration`
     ) AS `timespan`,
     STRUCT(metrics.uuid.migration_telemetry_identifiers_fennec_client_id) AS `uuid`,
     CAST(NULL AS ARRAY<STRUCT<`key` STRING, `value` STRING>>) AS `jwe`,
@@ -309,7 +461,27 @@
   normalized_country_code,
   normalized_os,
   normalized_os_version,
-  ping_info,
+  STRUCT(
+    ping_info.end_time,
+    ARRAY(
+      SELECT
+        STRUCT(
+          experiments.key,
+          STRUCT(
+            experiments.value.branch,
+            STRUCT(experiments.value.extra.type, experiments.value.extra.enrollment_id) AS `extra`
+          ) AS `value`
+        )
+      FROM
+        UNNEST(ping_info.experiments) AS `experiments`
+    ) AS `experiments`,
+    ping_info.ping_type,
+    ping_info.reason,
+    ping_info.seq,
+    ping_info.start_time,
+    ping_info.parsed_start_time,
+    ping_info.parsed_end_time
+  ) AS `ping_info`,
   sample_id,
   submission_timestamp,
   app_version_major,
@@ -327,10 +499,79 @@
   ).channel AS normalized_channel,
   CAST(NULL AS DATE) AS `submission_date`,
   additional_properties,
-  client_info,
+  STRUCT(
+    client_info.android_sdk_version,
+    client_info.app_build,
+    client_info.app_channel,
+    client_info.app_display_version,
+    client_info.architecture,
+    client_info.client_id,
+    client_info.device_manufacturer,
+    client_info.device_model,
+    client_info.first_run_date,
+    client_info.locale,
+    client_info.os,
+    client_info.os_version,
+    client_info.telemetry_sdk_build,
+    client_info.build_date,
+    client_info.windows_build_number,
+    client_info.session_count,
+    client_info.session_id,
+    STRUCT(
+      client_info.attribution.campaign,
+      client_info.attribution.content,
+      client_info.attribution.medium,
+      client_info.attribution.source,
+      client_info.attribution.term,
+      client_info.attribution.ext
+    ) AS `attribution`,
+    STRUCT(client_info.distribution.name, client_info.distribution.ext) AS `distribution`
+  ) AS `client_info`,
   document_id,
-  events,
-  metadata,
+  ARRAY(
+    SELECT
+      STRUCT(
+        events.category,
+        ARRAY(
+          SELECT
+            STRUCT(extra.key, extra.value)
+          FROM
+            UNNEST(events.extra) AS `extra`
+        ) AS `extra`,
+        events.name,
+        events.timestamp
+      )
+    FROM
+      UNNEST(events) AS `events`
+  ) AS `events`,
+  STRUCT(
+    STRUCT(
+      metadata.geo.city,
+      metadata.geo.country,
+      metadata.geo.db_version,
+      metadata.geo.subdivision1,
+      metadata.geo.subdivision2
+    ) AS `geo`,
+    STRUCT(
+      metadata.header.date,
+      metadata.header.dnt,
+      metadata.header.x_debug_id,
+      metadata.header.x_pingsender_version,
+      metadata.header.x_source_tags,
+      metadata.header.x_telemetry_agent,
+      metadata.header.x_foxsec_ip_reputation,
+      metadata.header.x_lb_tags,
+      metadata.header.parsed_date,
+      metadata.header.parsed_x_source_tags,
+      metadata.header.parsed_x_lb_tags
+    ) AS `header`,
+    STRUCT(
+      metadata.user_agent.browser,
+      metadata.user_agent.os,
+      metadata.user_agent.version
+    ) AS `user_agent`,
+    STRUCT(metadata.isp.db_version, metadata.isp.name, metadata.isp.organization) AS `isp`
+  ) AS `metadata`,
   STRUCT(
     STRUCT(
       metrics.boolean.migration_addons_any_failures,
@@ -387,15 +628,65 @@
       metrics.datetime.raw_migration_telemetry_identifiers_fennec_profile_creation_date
     ) AS `datetime`,
     STRUCT(
-      metrics.labeled_counter.glean_error_invalid_label,
-      metrics.labeled_counter.glean_error_invalid_overflow,
-      metrics.labeled_counter.glean_error_invalid_state,
-      metrics.labeled_counter.glean_error_invalid_value,
-      metrics.labeled_counter.migration_bookmarks_migrated,
-      metrics.labeled_counter.migration_history_migrated,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_label.key, glean_error_invalid_label.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_label) AS `glean_error_invalid_label`
+      ) AS `glean_error_invalid_label`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_overflow.key, glean_error_invalid_overflow.value)
+        FROM
+          UNNEST(
+            metrics.labeled_counter.glean_error_invalid_overflow
+          ) AS `glean_error_invalid_overflow`
+      ) AS `glean_error_invalid_overflow`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_state.key, glean_error_invalid_state.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_state) AS `glean_error_invalid_state`
+      ) AS `glean_error_invalid_state`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_value.key, glean_error_invalid_value.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_value) AS `glean_error_invalid_value`
+      ) AS `glean_error_invalid_value`,
+      ARRAY(
+        SELECT
+          STRUCT(migration_bookmarks_migrated.key, migration_bookmarks_migrated.value)
+        FROM
+          UNNEST(
+            metrics.labeled_counter.migration_bookmarks_migrated
+          ) AS `migration_bookmarks_migrated`
+      ) AS `migration_bookmarks_migrated`,
+      ARRAY(
+        SELECT
+          STRUCT(migration_history_migrated.key, migration_history_migrated.value)
+        FROM
+          UNNEST(metrics.labeled_counter.migration_history_migrated) AS `migration_history_migrated`
+      ) AS `migration_history_migrated`,
+      ARRAY(
+        SELECT
+          STRUCT(migration_logins_failure_counts.key, migration_logins_failure_counts.value)
+        FROM
+          UNNEST(
       metrics.labeled_counter.migration_logins_failure_counts
+          ) AS `migration_logins_failure_counts`
+      ) AS `migration_logins_failure_counts`
     ) AS `labeled_counter`,
-    STRUCT(metrics.labeled_string.migration_migration_versions) AS `labeled_string`,
+    STRUCT(
+      ARRAY(
+        SELECT
+          STRUCT(migration_migration_versions.key, migration_migration_versions.value)
+        FROM
+          UNNEST(
+            metrics.labeled_string.migration_migration_versions
+          ) AS `migration_migration_versions`
+      ) AS `migration_migration_versions`
+    ) AS `labeled_string`,
     STRUCT(
       metrics.string.migration_fxa_bad_auth_state,
       metrics.string.migration_fxa_failure_reason_rust,
@@ -405,19 +696,58 @@
       metrics.string.glean_client_annotation_experimentation_id
     ) AS `string`,
     STRUCT(
-      metrics.timespan.migration_bookmarks_duration,
-      metrics.timespan.migration_history_duration,
-      metrics.timespan.migration_addons_total_duration,
-      metrics.timespan.migration_bookmarks_total_duration,
-      metrics.timespan.migration_fxa_total_duration,
-      metrics.timespan.migration_gecko_total_duration,
-      metrics.timespan.migration_history_total_duration,
-      metrics.timespan.migration_logins_total_duration,
-      metrics.timespan.migration_open_tabs_total_duration,
-      metrics.timespan.migration_pinned_sites_total_duration,
-      metrics.timespan.migration_search_total_duration,
-      metrics.timespan.migration_settings_total_duration,
-      metrics.timespan.migration_telemetry_identifiers_total_duration
+      STRUCT(
+        metrics.timespan.migration_bookmarks_duration.time_unit,
+        metrics.timespan.migration_bookmarks_duration.value
+      ) AS `migration_bookmarks_duration`,
+      STRUCT(
+        metrics.timespan.migration_history_duration.time_unit,
+        metrics.timespan.migration_history_duration.value
+      ) AS `migration_history_duration`,
+      STRUCT(
+        metrics.timespan.migration_addons_total_duration.time_unit,
+        metrics.timespan.migration_addons_total_duration.value
+      ) AS `migration_addons_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_bookmarks_total_duration.time_unit,
+        metrics.timespan.migration_bookmarks_total_duration.value
+      ) AS `migration_bookmarks_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_fxa_total_duration.time_unit,
+        metrics.timespan.migration_fxa_total_duration.value
+      ) AS `migration_fxa_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_gecko_total_duration.time_unit,
+        metrics.timespan.migration_gecko_total_duration.value
+      ) AS `migration_gecko_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_history_total_duration.time_unit,
+        metrics.timespan.migration_history_total_duration.value
+      ) AS `migration_history_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_logins_total_duration.time_unit,
+        metrics.timespan.migration_logins_total_duration.value
+      ) AS `migration_logins_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_open_tabs_total_duration.time_unit,
+        metrics.timespan.migration_open_tabs_total_duration.value
+      ) AS `migration_open_tabs_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_pinned_sites_total_duration.time_unit,
+        metrics.timespan.migration_pinned_sites_total_duration.value
+      ) AS `migration_pinned_sites_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_search_total_duration.time_unit,
+        metrics.timespan.migration_search_total_duration.value
+      ) AS `migration_search_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_settings_total_duration.time_unit,
+        metrics.timespan.migration_settings_total_duration.value
+      ) AS `migration_settings_total_duration`,
+      STRUCT(
+        metrics.timespan.migration_telemetry_identifiers_total_duration.time_unit,
+        metrics.timespan.migration_telemetry_identifiers_total_duration.value
+      ) AS `migration_telemetry_identifiers_total_duration`
     ) AS `timespan`,
     STRUCT(metrics.uuid.migration_telemetry_identifiers_fennec_client_id) AS `uuid`,
     CAST(NULL AS ARRAY<STRUCT<`key` STRING, `value` STRING>>) AS `jwe`,
@@ -441,7 +771,27 @@
   normalized_country_code,
   normalized_os,
   normalized_os_version,
-  ping_info,
+  STRUCT(
+    ping_info.end_time,
+    ARRAY(
+      SELECT
+        STRUCT(
+          experiments.key,
+          STRUCT(
+            experiments.value.branch,
+            STRUCT(experiments.value.extra.type, experiments.value.extra.enrollment_id) AS `extra`
+          ) AS `value`
+        )
+      FROM
+        UNNEST(ping_info.experiments) AS `experiments`
+    ) AS `experiments`,
+    ping_info.ping_type,
+    ping_info.reason,
+    ping_info.seq,
+    ping_info.start_time,
+    ping_info.parsed_start_time,
+    ping_info.parsed_end_time
+  ) AS `ping_info`,
   sample_id,
   submission_timestamp,
   app_version_major,
@@ -459,10 +809,79 @@
   ).channel AS normalized_channel,
   CAST(NULL AS DATE) AS `submission_date`,
   additional_properties,
-  client_info,
+  STRUCT(
+    client_info.android_sdk_version,
+    client_info.app_build,
+    client_info.app_channel,
+    client_info.app_display_version,
+    client_info.architecture,
+    client_info.client_id,
+    client_info.device_manufacturer,
+    client_info.device_model,
+    client_info.first_run_date,
+    client_info.locale,
+    client_info.os,
+    client_info.os_version,
+    client_info.telemetry_sdk_build,
+    client_info.build_date,
+    client_info.windows_build_number,
+    client_info.session_count,
+    client_info.session_id,
+    STRUCT(
+      client_info.attribution.campaign,
+      client_info.attribution.content,
+      client_info.attribution.medium,
+      client_info.attribution.source,
+      client_info.attribution.term,
+      client_info.attribution.ext
+    ) AS `attribution`,
+    STRUCT(client_info.distribution.name, client_info.distribution.ext) AS `distribution`
+  ) AS `client_info`,
   document_id,
-  events,
-  metadata,
+  ARRAY(
+    SELECT
+      STRUCT(
+        events.category,
+        ARRAY(
+          SELECT
+            STRUCT(extra.key, extra.value)
+          FROM
+            UNNEST(events.extra) AS `extra`
+        ) AS `extra`,
+        events.name,
+        events.timestamp
+      )
+    FROM
+      UNNEST(events) AS `events`
+  ) AS `events`,
+  STRUCT(
+    STRUCT(
+      metadata.geo.city,
+      metadata.geo.country,
+      metadata.geo.db_version,
+      metadata.geo.subdivision1,
+      metadata.geo.subdivision2
+    ) AS `geo`,
+    STRUCT(
+      metadata.header.date,
+      metadata.header.dnt,
+      metadata.header.x_debug_id,
+      metadata.header.x_pingsender_version,
+      metadata.header.x_source_tags,
+      metadata.header.x_telemetry_agent,
+      metadata.header.x_foxsec_ip_reputation,
+      metadata.header.x_lb_tags,
+      metadata.header.parsed_date,
+      metadata.header.parsed_x_source_tags,
+      metadata.header.parsed_x_lb_tags
+    ) AS `header`,
+    STRUCT(
+      metadata.user_agent.browser,
+      metadata.user_agent.os,
+      metadata.user_agent.version
+    ) AS `user_agent`,
+    STRUCT(metadata.isp.db_version, metadata.isp.name, metadata.isp.organization) AS `isp`
+  ) AS `metadata`,
   STRUCT(
     STRUCT(
       metrics.boolean.migration_addons_any_failures,
@@ -519,15 +938,65 @@
       metrics.datetime.raw_migration_telemetry_identifiers_fennec_profile_creation_date
     ) AS `datetime`,
     STRUCT(
-      metrics.labeled_counter.glean_error_invalid_label,
-      metrics.labeled_counter.glean_error_invalid_overflow,
-      metrics.labeled_counter.glean_error_invalid_state,
-      metrics.labeled_counter.glean_error_invalid_value,
-      metrics.labeled_counter.migration_bookmarks_migrated,
-      metrics.labeled_counter.migration_history_migrated,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_label.key, glean_error_invalid_label.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_label) AS `glean_error_invalid_label`
+      ) AS `glean_error_invalid_label`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_overflow.key, glean_error_invalid_overflow.value)
+        FROM
+          UNNEST(
+            metrics.labeled_counter.glean_error_invalid_overflow
+          ) AS `glean_error_invalid_overflow`
+      ) AS `glean_error_invalid_overflow`,
+      ARRAY(
+        SELECT
+          STRUCT(glean_error_invalid_state.key, glean_error_invalid_state.value)
+        FROM
+          UNNEST(metrics.labeled_counter.glean_error_invalid_state) AS `glean_error_invalid_state`
+      ) AS `glean_error_invalid_

⚠️ Only part of the diff is displayed.

Link to full diff

@scholtzan scholtzan added this pull request to the merge queue Dec 1, 2025
Merged via the queue into main with commit a995b92 Dec 1, 2025
22 checks passed
@scholtzan scholtzan deleted the query-schema-generation-dryrun-caching branch December 1, 2025 18:46
Comment on lines +2303 to +2305
project=project_name,
dataset=dataset_name,
table=table_name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out this particular change is causing problems with deploying ETLs to stage:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants