Ok, I see what you suggest but I can't see how it will work. Your solution, regardless of the code you provided, would be to infer the changes needed to perform to the DB based on the DB structure (eg: If you need to add a column on a plugin update then make sure the column is not there). This might become very complex (and I guess in some situations even impossible) because it is not just a matter of schema structure (DDL) but also table data (DML).
Now, I'm not sure if we're on the same page on the fact that a plugin version number tracker is a must. Even if you manage to successfully apply the previous paragraph to your update process you will need to know from which version to update, right? (Correct me here if you consider there is another way to know which version updates should be applied without tracking a version number. I believe it is not possible to infer the plugin version based on structure or data). So if you do need a plugin version, then why would you bother in applying the complex stuff from the previous paragraph if you could only check the version number and see what queries to run? From now on I will assume the version number approach is used.
So the issue that I mention in the question arises here which is: How to keep version_number and actual table structure and data in sync? That is actually the issue I'm trying to solve. Now, let's take a look at your code and see why it won't work (don't take it the wrong way, please, just working out a solution :)
Without applying the assumption I mentioned in my previous comment (the array / string variable to store the needed queries) you will lose version sync if you DDL statement succeeds. This is because you have RETURNed the string and have not run the qa_opt. You will need to run the init queries again to fix that.
Suppose you fixed that using the array / string variable and just append the DDL statement to it and return it just after the qa_opt. That seems to be bullet proof but you will lose sync if any SQL statement fails. Basically, you queue the queries in an array (let's take the array approach to handle many queries) and then, you update the version to the new one. The queries get, at a later point, executed and they fail. You now have a newer version number set but an old database schema or data.
Also, something missing in your code would be to actually check for the version number. You're only setting it.
Following a similar approach to the one gideon used, I just checked on the version number and started the upgrade progress from that point (a switch case without breaks helps remove the loop he uses). Now, he cheats :) He does not queue statements in there. He executes them directly so if any fails everything after it fails. The plugin just queues it doesn't execute anything. So in the plugin context... how do you know the queries have actually run properly?
Well, I cheated too. I queued the version variable update too into an SQL statement. So the version variable won't be updated if any previous SQL statement fails. Only issue is that the cache will not be in sync and should be updated or invalidated. And I couldn't find a better way to do that than the one explained in question #30899. Hope this is clearer now.