I'm writing a tool to update my bigquery tables to reflect my locally defined schemas. Let's say I'm updating the table users. The steps for doing this are:

Create a new table with the new schema, called users_update.
Create a SELECT query where values are cast (e.g. TIMESTAMP to DATETIME), and new columns added (e.g. CURRENT_TIMESTAMP() as date_updated)
Run a QueryJob (python) to execute the query with the new table as the destination.
Delete the table users
Copy users_update to users
Delete the table users_update

The problem is that this query converts every field in the new table to NULLABLE. I've stepped through my script, and I've verified that the new table has correct modes before the QueryJob. I also provide the new schema (with correct modes) to the QueryJob.
Are there some parameters to the QueryJob I need to set, or what am I missing here?
I had set the job_config.write_disposition to WRITE_TRUNCATE. Removing this solved my issue.
Google BigQuery: SELECT with destination table overrides field modes to NULLABLE