-
Notifications
You must be signed in to change notification settings - Fork 1.1k
sp_Blitz: make it install and run on Azure SQL Database (#4040) #4045
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
BrentOzar
wants to merge
16
commits into
dev
Choose a base branch
from
claude/issue-4040-azure-sp_blitz
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4d41439
sp_Blitz: make it install and run on Azure SQL Database
BrentOzar 8b9c783
sp_Blitz: fix two boxed-SQL breakages in the Azure dynamic-SQL rewrite
BrentOzar 6ed18f9
sp_Blitz: hold CheckID 271 tempdb file count in @CrossDBCount, not @C…
BrentOzar abe4996
Merge dev into the Azure sp_Blitz branch
BrentOzar 3d41ac5
TEMP: one-off findings diff for PR #4045
BrentOzar 5023786
sp_Blitz: stop skipping CheckID 232 on Azure, it already works there
BrentOzar 9f6ca5f
CI: test sp_Blitz against a real Azure SQL Database; fix the one-off …
BrentOzar 2964561
CI: read the Azure finding count from the count, not the version warning
claude 1390544
TEMP: fail the one-off diff when a capture records nothing
claude 891710d
TEMP: exclude the uptime-normalized wait checks from the one-off diff
claude 8cf1535
Remove the one-off findings diff now that it has answered its question
claude ee2e7b7
sp_Blitz: keep hoisted probes behind their skip guards; scope #TempDB…
claude a0f1b8b
CI: report which checks fired on Azure, not just how many
claude 5fad8de
CI: drop the Azure procedures before installing them
claude 4bb07c5
CI: also run the Azure smoke test on dev after merge
claude a5837d9
CI: assert Azure found real checks, not sentinel rows
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # Installs and runs sp_Blitz against a real Azure SQL Database. | ||
| # | ||
| # Azure SQL DB refuses to compile a module that names another database, which is | ||
| # the whole subject of issue #4040: sp_Blitz's ALTER PROCEDURE aborted on the | ||
| # first of 88 cross-database references and the procedure was never created. | ||
| # Worse, the CREATE stub in the preceding batch succeeded, so users were left | ||
| # with an sp_Blitz that ran, returned nothing, and raised no error. | ||
| # | ||
| # That failure mode is invisible to a boxed SQL Server test, and invisible to | ||
| # "did it install" -- the install genuinely succeeds. So this checks three | ||
| # things: the procedure installs without error, it runs without error, and it | ||
| # actually returns findings. | ||
|
|
||
| set -Eeuo pipefail | ||
|
|
||
| : "${AZURE_SQL_SERVER:?AZURE_SQL_SERVER must be set}" | ||
| : "${AZURE_SQL_DATABASE:?AZURE_SQL_DATABASE must be set}" | ||
| : "${AZURE_SQL_USER:?AZURE_SQL_USER must be set}" | ||
| : "${AZURE_SQL_PASSWORD:?AZURE_SQL_PASSWORD must be set}" | ||
| : "${SQLCMD:=sqlcmd}" | ||
|
|
||
| REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | ||
| WORK_DIR="$(mktemp -d)" | ||
| trap 'rm -rf "$WORK_DIR"' EXIT | ||
|
|
||
| # -I for QUOTED_IDENTIFIER ON, matching every real client. No -C: Azure presents | ||
| # a valid certificate, so the connection is verified rather than trusted blindly. | ||
| SQLCMD_ARGS=( | ||
| -S "tcp:$AZURE_SQL_SERVER,1433" | ||
| -d "$AZURE_SQL_DATABASE" | ||
| -U "$AZURE_SQL_USER" | ||
| -P "$AZURE_SQL_PASSWORD" | ||
| -N | ||
| -b | ||
| -I | ||
| -l 60 | ||
| -t 600 | ||
| ) | ||
|
|
||
| run_query() { "$SQLCMD" "${SQLCMD_ARGS[@]}" -Q "$1"; } | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Read a single integer out of a batch. | ||
| # | ||
| # Not "the first line of output": sqlcmd prints every result set the batch | ||
| # produced, and sp_Blitz emits a version-check result set ("Component ... is | ||
| # outdated") ahead of its real output. Taking the first line picked up that | ||
| # warning text and reported it as the finding count. So take the last line that | ||
| # is nothing but digits, which the warning text can never be. | ||
| # | ||
| # `|| true` because grep exits 1 when it matches nothing, and pipefail would | ||
| # turn that into an abort instead of letting the caller report a clear error. | ||
| # --------------------------------------------------------------------------- | ||
| run_scalar_int() { | ||
| "$SQLCMD" "${SQLCMD_ARGS[@]}" -h -1 -W -Q "$1" \ | ||
| | sed 's/[[:space:]]//g' \ | ||
| | grep -E '^[0-9]+$' \ | ||
| | tail -1 || true | ||
| } | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Serverless Azure SQL DB auto-pauses when idle, and the connection that wakes it | ||
| # is itself rejected while it resumes. A first failure means nothing; only a | ||
| # sustained one does. | ||
| # --------------------------------------------------------------------------- | ||
| wake_database() { | ||
| echo "Connecting to Azure SQL Database (it auto-pauses; early failures are expected)..." | ||
| for attempt in {1..30}; do | ||
| if run_query "SET NOCOUNT ON; SELECT 1;" >/dev/null 2>&1; then | ||
| echo "Connected on attempt $attempt." | ||
| return 0 | ||
| fi | ||
| echo " attempt $attempt: not up yet, waiting..." | ||
| sleep 20 | ||
| done | ||
|
|
||
| echo "::error::Could not connect to Azure SQL Database after 30 attempts (10 minutes)." >&2 | ||
| echo "::error::Check that the server is reachable and that its firewall admits GitHub runner IPs." >&2 | ||
| return 1 | ||
| } | ||
|
|
||
| wake_database | ||
|
|
||
| echo | ||
| echo "=== Engine confirms this really is Azure SQL Database ===" | ||
| # EngineEdition 5 is Azure SQL Database. If this is anything else, the test is | ||
| # passing for the wrong reasons and should say so rather than look green. | ||
| edition="$(run_scalar_int "SET NOCOUNT ON; SELECT CONVERT(INT, SERVERPROPERTY('EngineEdition'));")" | ||
|
|
||
| echo "EngineEdition: $edition" | ||
| if [[ "$edition" != "5" ]]; then | ||
| echo "::error::Expected EngineEdition 5 (Azure SQL Database), got '$edition'. Refusing to report success." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo | ||
| echo "=== Installing ===" | ||
| # sp_ineachdb first: sp_Blitz calls it to iterate databases. | ||
| for script in sp_ineachdb sp_Blitz; do | ||
|
BrentOzar marked this conversation as resolved.
|
||
| echo " installing $script" | ||
| if ! "$SQLCMD" "${SQLCMD_ARGS[@]}" -i "$REPO_ROOT/$script.sql" > "$WORK_DIR/$script.log" 2>&1; then | ||
| echo "::error::$script failed to install on Azure SQL Database" | ||
| grep -E -A2 '^(Msg [0-9]+,|Sqlcmd: Error)' "$WORK_DIR/$script.log" | head -30 || tail -30 "$WORK_DIR/$script.log" | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| # The install "succeeding" is exactly what made #4040 invisible: the CREATE stub | ||
| # is its own batch and always works, while only the ALTER carrying the real body | ||
| # fails. So confirm the procedure has a body, not merely a name. | ||
| echo | ||
| echo "=== Confirming sp_Blitz is more than the stub ===" | ||
| body_length="$(run_scalar_int "SET NOCOUNT ON; | ||
| SELECT LEN(OBJECT_DEFINITION(OBJECT_ID('dbo.sp_Blitz')));")" | ||
|
|
||
| echo "sp_Blitz definition length: $body_length characters" | ||
| if ! [[ "$body_length" =~ ^[0-9]+$ ]] || (( body_length < 10000 )); then | ||
| echo "::error::sp_Blitz exists but its body is '$body_length' characters -- that is the RETURN 0 stub, not the real procedure." >&2 | ||
| echo "::error::This is the #4040 failure: the CREATE stub succeeded and the ALTER carrying the body did not." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo | ||
| echo "=== Running sp_Blitz ===" | ||
| if ! run_query "EXEC dbo.sp_Blitz;" > "$WORK_DIR/run.log" 2>&1; then | ||
| echo "::error::sp_Blitz raised an error on Azure SQL Database" | ||
| grep -E -A2 '^(Msg [0-9]+,|Sqlcmd: Error)' "$WORK_DIR/run.log" | head -40 || tail -40 "$WORK_DIR/run.log" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo " ran without error" | ||
|
|
||
| # A silent clean bill of health is the bug, not the goal. Any real server has | ||
| # something to say, so zero findings means the checks are not running. | ||
| echo | ||
| echo "=== Counting findings ===" | ||
| findings="$(run_scalar_int "SET NOCOUNT ON; EXEC dbo.sp_Blitz @OutputType = 'COUNT';")" | ||
|
BrentOzar marked this conversation as resolved.
Outdated
|
||
|
|
||
| echo "sp_Blitz returned $findings finding(s)" | ||
| if ! [[ "$findings" =~ ^[0-9]+$ ]] || (( findings < 1 )); then | ||
| echo "::error::sp_Blitz returned no findings ('$findings'). Running clean with nothing to say is the #4040 symptom, not a pass." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| { | ||
| echo "### Azure SQL Database" | ||
| echo | ||
| echo "- EngineEdition \`5\` confirmed" | ||
| echo "- \`sp_Blitz\` installed, definition $body_length characters (not the stub)" | ||
| echo "- ran with no errors" | ||
| echo "- returned **$findings findings**" | ||
| } >> "${GITHUB_STEP_SUMMARY:-/dev/null}" | ||
|
|
||
| echo | ||
| echo "Azure SQL Database smoke test passed." | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| name: Azure SQL Database smoke test | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, ready_for_review] | ||
|
|
||
| # One at a time. The target is a single shared Azure SQL Database, and this | ||
| # installs procedures into it; concurrent runs would overwrite each other | ||
| # mid-test. Queue rather than cancel, so a run already talking to the database | ||
| # is allowed to finish. | ||
| concurrency: | ||
| group: azure-sql-smoke-test | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| azure-smoke-test: | ||
| name: Azure SQL Database | ||
| runs-on: ubuntu-22.04 | ||
| # Generous: serverless Azure SQL DB auto-pauses, and waking it can take | ||
| # several minutes before the first successful connection. | ||
| timeout-minutes: 30 | ||
|
|
||
| # Skipped rather than failed on forks, where secrets are not available. | ||
| # A missing secret would otherwise look like a broken Azure database. | ||
| if: github.event.pull_request.head.repo.full_name == github.repository | ||
|
BrentOzar marked this conversation as resolved.
Outdated
|
||
|
|
||
| env: | ||
| AZURE_SQL_SERVER: ${{ secrets.AZURE_SQL_SERVER }} | ||
| AZURE_SQL_DATABASE: ${{ secrets.AZURE_SQL_DATABASE }} | ||
| AZURE_SQL_USER: ${{ secrets.AZURE_SQL_USER }} | ||
| AZURE_SQL_PASSWORD: ${{ secrets.AZURE_SQL_PASSWORD }} | ||
|
|
||
| steps: | ||
| - name: Check out repository | ||
| uses: actions/checkout@v5 | ||
|
|
||
| - name: Confirm the Azure credentials are present | ||
| run: | | ||
| missing="" | ||
| [[ -n "$AZURE_SQL_SERVER" ]] || missing+="AZURE_SQL_SERVER " | ||
| [[ -n "$AZURE_SQL_DATABASE" ]] || missing+="AZURE_SQL_DATABASE " | ||
| [[ -n "$AZURE_SQL_USER" ]] || missing+="AZURE_SQL_USER " | ||
| [[ -n "$AZURE_SQL_PASSWORD" ]] || missing+="AZURE_SQL_PASSWORD " | ||
| if [[ -n "$missing" ]]; then | ||
| echo "::error::Missing repository secrets: $missing" | ||
| echo "::error::Set them under Settings -> Secrets and variables -> Actions." | ||
| exit 1 | ||
| fi | ||
| echo "All four Azure secrets are present." | ||
|
|
||
| - name: Install sqlcmd | ||
| run: | | ||
| curl -sSL -O https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb | ||
| sudo dpkg -i packages-microsoft-prod.deb | ||
| sudo apt-get update | ||
| sudo env ACCEPT_EULA=Y apt-get install -y mssql-tools18 unixodbc-dev | ||
| echo "/opt/mssql-tools18/bin" >> "$GITHUB_PATH" | ||
|
|
||
| - name: Install and run sp_Blitz on Azure SQL Database | ||
| run: bash .github/scripts/run-azure-smoke-test.sh | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.