Appendix A

Script Index & Gotchas

Lab:Ubuntu 24.04 VM on AWS+Docker · docker-compose+SQL Server 2022 on Linux (container)+Airflow 2.7 (containers)Every gotcha below was hit on this Linux + Docker stack.

Every file in the lab, where it runs and what it does, followed by the surprises found while building it. Each one cost real debugging time, so they're collected here.

Course build: ~/sbecoltddw on the Linux host

FileRuns asDoes
run_sbecoltddw.shshell → docker exec → sqlcmdRuns the course scripts in order inside the SQL Server Linux container; REBUILD=1 drops the database first
reset_source.shshell → docker exec → sqlcmdUndoes the simulator's changes in AdventureWorks2022 so the scenarios can be replayed
00_CreateDatabasesaCreates SBEcoLTDDW (simple recovery)
01_CreateDW · 02_Dimension · 03_FactsaCourse Ch. 2: schemas, Date + Customer, Orders + foreign keys (Year fixed to smallint)
05_View · 06_Columnstore · 08_RebuildsaCourse Ch. 2–3: indexed view, nonclustered columnstore, online rebuild
09_ClassifyWorkloadssaCourse Ch. 5: DataLoader login + Resource Governor pool, group and classifier
13_GrantSourceReadsaDataLoader gets db_datareader on AdventureWorks2022
99_VerifysaRow counts, indexes, foreign keys, orphan check, Resource Governor stats

Pipeline: ~/airflow, bind-mounted into the Airflow containers

FileAirflow taskChapter
dags/sbecoltddw_e2e_pipeline.pythe 26-task DAG11
dags/sbecoltddw_source_changes.pythe source simulator12
plugins/sbecoltddw_operators.pyMsSqlFileOperator11
00_control/ start_run · end_run · fail_runstart_run, end_run, fail_run11, 12
01_deploy/ 01–11deploy.*3, 5, 6, 8, 13
02_extract/ customers · sales_ordersextract.*5
03_stage_checks/check_staging.sqlstage_checks5
04_dimensions/ date · customer_scd2dimensions.*6
05_facts/load_fact_orders.sqlfacts.load_fact_orders7
06_post_load/ columnstore · statistics · indexed_viewpost_load.*8
07_dq/dq_gate.sqldq_gate10
08_publish/ advance_watermarks · run_summarypublish.*5, 12
90_source_changes/ 5 filessimulator tasks12
setup_airflow_sbecoltddw.sh · sbdw_run.sh · sbdw_clear.sh · sbdw_capture.sh · sbdw_scenarios.shhelpers (shell, on the Linux host)2, 12

Rebuild from nothing

bash ~/sbecoltddw/reset_source.sh                                   # original AdventureWorks2022 data
REBUILD=1 COURSE_ONLY=1 bash ~/sbecoltddw/run_sbecoltddw.sh       # course objects, empty tables
bash ~/sbecoltddw/airflow/setup_airflow_sbecoltddw.sh               # compose v11, files, containers, connection
~/sbecoltddw/airflow/sbdw_run.sh sbecoltddw_e2e_pipeline             # initial load
~/sbecoltddw/airflow/sbdw_scenarios.sh move                          # SCD2 scenario (then: bad, fix, reload)

Gotchas

SQL Server 2022 on Linux, in Docker

SymptomCauseFix
No sqlcmd on the Linux hostThe client tools live inside the container image (/opt/mssql-tools18)docker exec -i labenv_mssql_1 /opt/mssql-tools18/bin/sqlcmd -C …, piping the SQL file on stdin
Msg 1934 … SET options have incorrect settings: 'QUOTED_IDENTIFIER' on AdventureWorks tablessqlcmd defaults QUOTED_IDENTIFIER to OFF; the source tables have indexes on computed columnssqlcmd -I
Container uses all of the host's RAMSQL Server sizes its memory from what it can seeMSSQL_MEMORY_LIMIT_MB = 2048 in the compose file
Data lost when the container is recreatedFiles written inside the container layerA named Docker volume on /var/opt/mssql

T-SQL and the engine

SymptomCauseFix
Arithmetic overflow loading yearsCourse Year tinyint (max 255)smallint
Indexed view creation fails from sqlcmdQUOTED_IDENTIFIER is OFF by default in sqlcmdsqlcmd -I (ODBC clients are fine)
Incorrect syntax near the keyword 'statistics'Reserved word as a column aliasRename (stats_objects) or bracket it
Columnstore stays OPEN, about 3× largerFewer than 102,400 rows per insert threadREORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON) after the load
Can't change the Resource Governor classifierThe active classifier function can't be alteredALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL) first
Readers of rpt denied on the tablesSchemas created by DataLoader are owned by DataLoader, which breaks the ownership chainCREATE SCHEMA … AUTHORIZATION dbo, or ALTER AUTHORIZATION
Columns added in the same batch "don't exist"A batch compiles before ALTER TABLE ADD runsPut dependent statements in the next file, or in EXEC()

Airflow in Docker and the lab

SymptomCauseFix
A failing THROW reported as successThe error arrives in a later result set (SQLMoreResults)Loop over cursor.nextset() (the operator does)
New SQL folder not visible in tasksBind mounts are fixed when the container is createdCompose Version10/11, then docker-compose rm -sf + up -d (compose v1)
Python sent via docker exec … python - <<EOF does nothingNo -i, so stdin isn't attacheddocker exec -i
JSONDecodeError on dags trigger -cThe JSON's double quotes were stripped by an extra shell layer (PowerShell → ssh)Put the call in a script on the server (sbdw_scenarios.sh)
Re-running a scenario does nothingA DAG run with that run_id already exists, so dags trigger -r failsUse a new run ID, or airflow dags delete first (and remove its log folders)
New deploy file, no new taskDAG not re-parsed yetWait for the parse interval or run airflow dags reserialize