Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add BigQuery routine operators and existence sensor - #65499

Merged
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators
May 12, 2026
Merged

Add BigQuery routine operators and existence sensor#65499
shahar1 merged 14 commits into
apache:mainfrom
alamashir:feat/bigquery-routine-operators

Conversation

@alamashir

@alamashiralamashir commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class Airflow operators for BigQuery routines (user-defined functions, stored procedures, table-valued and aggregate functions), so DAGs can own their routine definitions declaratively instead of embedding CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.

New operators (providers/google/src/airflow/providers/google/cloud/operators/bigquery.py):

  • BigQueryCreateRoutineOperator — creates any routine type; if_exists of fail | skip | replace
  • BigQueryUpdateRoutineOperator — updates selected fields on an existing routine. Because BigQuery's routines.update REST API is a full-resource PUT (not a PATCH), the hook fetches the current routine, merges the requested changes, and writes the complete resource back.
  • BigQueryDeleteRoutineOperatorignore_if_missing toggle
  • BigQueryGetRoutineOperator — pushes the serialized Routine resource to XCom
  • BigQueryListRoutinesOperator — dataset-scoped list via XCom

New sensor (sensors/bigquery.py):

  • BigQueryRoutineExistenceSensor

New hook methods on BigQueryHook: create_routine, update_routine, delete_routine, get_routine, list_routines. They wrap google-cloud-bigquery's existing routine client APIs — no new provider dependency.

Docs: new bigquery_routines.rst guide with per-routine-type examples, cross-linked from bigquery.rst.

System test: example_bigquery_routines.py exercises the full lifecycle (create scalar UDF / procedure / TVF → sensor → update → get → list → delete).

Unit tests: 24 tests covering success, failure and edge cases (e.g. if_exists branches, missing routineReference, not_found_ok).

closes: #65467
related: #47579

Test plan

  • uv run --project providers/google pytest providers/google/tests/unit/google/cloud/{hooks,operators,sensors}/test_bigquery.py -k Routine — 24 passed
  • ruff format + ruff check clean on all touched files
  • System test (example_bigquery_routines.py) run end-to-end against a live GCP project — passed (DAG state success, ~5 min run). The system run exercised every operator and the sensor against real BigQuery:
    • create_dataset → created a fresh dataset
    • create_scalar_routine / create_procedure / create_tvf → created SCALAR_FUNCTION, PROCEDURE, and TABLE_VALUED_FUNCTION routines via BigQueryCreateRoutineOperator
    • wait_for_routineBigQueryRoutineExistenceSensor polled and returned True once the scalar UDF was visible
    • update_routine → mutated the scalar UDF's description via BigQueryUpdateRoutineOperator (this is what flushed out the PUT-vs-PATCH semantic and the retry-sentinel bug fixed in this PR)
    • get_routine / list_routines → fetched the updated routine and listed all three routines
    • delete_scalar_routine / delete_procedure / delete_tvf → deleted each routine via BigQueryDeleteRoutineOperator
    • delete_dataset → tore down the dataset
    • The DAG's watcher task (trigger_rule=ONE_FAILED) did not fire, confirming no upstream failures

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (Claude Code)

Generated-by: Claude Opus 4.7 (Claude Code) following the guidelines

Introduces first-class Airflow operators for BigQuery routines (UDFs,
stored procedures, table-valued and aggregate functions), so DAGs can
own their routine definitions declaratively instead of embedding
CREATE FUNCTION / CREATE PROCEDURE DDL inside BigQueryInsertJobOperator.
New operators:
* BigQueryCreateRoutineOperator (with if_exists: fail|skip|replace)
* BigQueryUpdateRoutineOperator (explicit field mask)
* BigQueryDeleteRoutineOperator (ignore_if_missing)
* BigQueryGetRoutineOperator (routine resource via XCom)
* BigQueryListRoutinesOperator (dataset-scoped list via XCom)
New sensor:
* BigQueryRoutineExistenceSensor
New BigQueryHook methods wrap the google-cloud-bigquery client's
routine APIs. No new provider dependency is added.
Related: apache#47579
Covers success, failure and edge cases for the five new routine
operators, the existence sensor, and the underlying hook methods
(including if_exists fail/skip/replace, not_found_ok and missing
routineReference validation).
Declare template_ext on BigQueryCreateRoutineOperator (.sql) and
BigQueryUpdateRoutineOperator (.json, .sql) so users can pass file
paths like "routines/add_one.sql" to templated fields and have Airflow
render the file contents at runtime.
Restores the pre-existing duplicate license header in bigquery.rst so
this PR's diff contains only the routine-operator additions.
BigQuery's routines.update REST API is a full-resource PUT (not a PATCH),
so sending only the changed fields fails with "Routine type must be
specified". The hook now fetches the existing routine, merges the
requested field changes, and writes the complete resource back.
Also replace the gapic _MethodDefault retry sentinel with the
BigQuery client's DEFAULT_RETRY callable on all five routine
operators -- the handwritten BQ client calls retry(call) directly
and cannot accept the sentinel.
Tested end-to-end against a live GCP project with the
example_bigquery_routines system DAG (all 11 lifecycle tasks pass).
@alamashir
alamashirforce-pushed the feat/bigquery-routine-operators branch from f6e5cba to f71f50bCompareApril 19, 2026 21:33
CI's provider-docs consistency check failed because the new
how-to guide page was added to the docs directory but not
listed in provider.yaml.
@alamashir
alamashir marked this pull request as ready for review April 19, 2026 23:17
@alamashir
alamashir requested a review from shahar1 as a code ownerApril 19, 2026 23:17
Generated from provider.yaml by the update-providers-build-files
prek hook after adding the bigquery_routines.rst how-to guide.
Comment threadproviders/google/provider.yaml
@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

@alamashir Converting to draft — this PR doesn't yet meet our Pull Request quality criteria.

  • Unresolved review comments (3 threads from maintainers): please walk through each unresolved review thread. Even if a suggestion looks incorrect or irrelevant — and some of them will be, especially any comments left by automated reviewers like GitHub Copilot — it is still the author's responsibility to respond: apply the fix, reply in-thread with a brief explanation of why the suggestion does not apply, or resolve the thread if the feedback is no longer relevant. Leaving threads unaddressed for weeks blocks the PR from moving forward.

See the linked criteria for how to fix each item, then mark the PR "Ready for review". This is not a rejection — just an invitation to bring the PR up to standard. No rush.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk

potiuk commented Apr 22, 2026

Copy link
Copy Markdown
Member

Quick follow-up to the triage comment above — one clarification on the "Unresolved review comments" item:

Once you believe a thread has been addressed — whether by pushing a fix, or by replying in-thread with an explanation of why the suggestion doesn't apply — please mark the thread as resolved yourself by clicking the "Resolve conversation" button at the bottom of each thread. Reviewers don't auto-close their own threads, so an addressed-but-unresolved thread reads as "still waiting on the author" and keeps the PR from moving forward. The author doing the resolve-click is the expected convention on this project.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@alamashir

Copy link
Copy Markdown
ContributorAuthor

Hi @potiuk — thanks for the triage and the clarification on thread-resolution convention.

Addressed:

  • All 3 review threads resolved (each had an in-thread reply).
  • prek run --from-ref main --stage pre-commit run locally — all hooks passed.

Marking ready for review.

@alamashir
alamashir marked this pull request as ready for review April 23, 2026 02:45
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Apr 27, 2026
@shahar1

Copy link
Copy Markdown
Contributor

Overall: nearly there, two things to address before merge.


Bug: BigQueryUpdateRoutineOperatorfields uses the wrong namespace, causing silent no-ops

The fields parameter is documented and tested with Python SDK attribute names ("body", "type_"), but the merge loop in update_routine operates on the camelCase REST API repr (updates_repr):

forfieldinfields:
iffieldinupdates_repr: # camelCase keys only ("definitionBody", not "body")merged_repr[field] =updates_repr[field]
else:
merged_repr.pop(field, None) # also camelCase; Python names are no-ops here

A user following the operator docstring example and passing fields=["body"] will get a silent no-op: the key "body" isn't in updates_repr (the correct key is "definitionBody"), so neither branch does anything, and the routine is unchanged. The API call succeeds with no error.

The system test uses only fields=["description"], which is identical in both namespaces and therefore masks the issue. The operator unit test uses fields=["description", "body"] — the "body" entry would silently do nothing on a real hook.

Fix: the fields parameter should use camelCase REST names throughout ("definitionBody", "routineType", etc.). Update the operator docstring, the operator unit test, and the hook docstring to reflect this. _ROUTINE_WRITABLE_PROPERTIES is an internal constant passed to client.update_routine() and correctly uses Python attribute names — that part is fine.


Missing changelog entry

AGENTS.md asks that new user-visible provider changes be recorded directly in providers/google/docs/changelog.rst. This PR adds 5 operators and a sensor — no changelog entry is present.


Minor (non-blocking)

  • first, second = MagicMock(), MagicMock() in TestBigQueryListRoutinesOperator.test_execute lacks spec=. Project guidelines require spec/autospec on all mocks.

Drafted-by: GitHub Copilot (Claude Sonnet 4.6); reviewed by @shahar1 before posting

- Use camelCase REST API field names (e.g. "definitionBody") instead of
Python SDK names (e.g. "body") in operator docstring and tests, matching
the hook's update_routine merge loop which operates on camelCase keys.
- Add spec=Routine to MagicMock instances in TestBigQueryListRoutinesOperator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alamashir

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review @shahar1!

Fields namespace bug — Fixed in 4fbc662. Updated the operator docstring to use ["definitionBody", "description"] instead of ["body", "description"], and updated the unit test accordingly.

Changelog entry — Added in 810e499.

Missing spec= — Fixed in 4fbc662. Added spec=Routine to the mocks in TestBigQueryListRoutinesOperator.

Comment threadproviders/google/docs/changelog.rst Outdated

@shahar1shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, good to merge once CI is green

@shahar1
shahar1 merged commit 5714158 into apache:mainMay 12, 2026
143 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providerskind:documentationprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add BigQuery routine operators (procedures, UDFs, TVFs, remote & Spark)

4 participants

@alamashir@potiuk@shahar1@phanikumv