These slides provide a general overview of the upgrade process, specifically "internal upgrade", as it is now implemented in ASE 15. They cover:
This presentation is a collection of information that server developers and debuggers will need. It is not an explanation of how customers upgrade their installations. It talks about specific files that exist in the server layer, but not the user application layer that invoke them.
"Upgrade" is the process of changing the underlying schema of a database so that its structure is correct for the modern server.
We can't predict what changes will have to be made to make that happen.
Upgrade runs against databases that DO NOT HAVE the new features.
Upgrade runs after recovery is complete. We want it to run before any customer activity in the databases, but there are no guarantees.
The only feature set you can depend on is whatever exists in the oldest customer installation anywhere in the field.
Upgrade runs against databases loaded from older servers, as well as against complete installations.
There IS NOT an "upgrade group" that creates a particular version's upgrade -- each developer does that for their own new features.
Before a feature is "done", developers must prove that older servers can successfully upgrade to the new feature.
Upgrade and preupgrade have two basic parts: the utilities themselves
and the "internal upgrade" portion. The utilities do very little
work; instead, they check things and tell ASE to perform actions internally.
The bulk of the work happens in internal upgrade, which is implemented as
part of ASE's
The utility files used for this are in
The internal upgrade portion are in various directories:
All upgrade files depend on quite a lot of generated source, all of which
is generated by awk scripts in the
It checks for name conflicts against new reserved words, checks databases to see that they are large enough to meet the new server's requirements, and checks the server's configuration to see that memory is adequate. It will print warning messages about the things it finds, alerting the customer about changes they may have to make.
Feature developers are responsible for modifying preupgrade to make the appropriate checks for their new features.
Note that preupgrade runs against the old server, not the new one! The old server has no knowledge of any new feature; all it's doing is checking for anything obvious that would cause a feature not to install correctly.
It has two basic parts: the "upgrade utility", which is a standalone program that once was the method we used to perform upgrades, and "internal upgrade", which is the method we now use. Between them, these two methods perform all the steps necessary to convert the old installation for the new server's use.
The "upgrade utility" consists mostly of upgrade/upgrade.c, its associated header files, and generated source.
"Internal upgrade" consists of utils/intupgd.c, utils/attrib.lst, include/intupgd.h, and generated source.
That program is outdated, quite limited, and dangerous. It will eventually go away. (Exception: there are no upgrade methods to handle configuration changes yet; so if you retire config parameters or add new ones, you will probably have to modify the upgrade utility. This is accomplished by modifying utils/cfg_options, then rebuilding the utils generated files.)
We refer to the actions upgrade does as "upgrade items". To add upgrade items, modify internal upgrade. Doing this modification is relatively simple:
Assign an unused number to the item. (You may have to renumber the current version's "checkpoint" ID in order to fit your item in.) You can scan for the tag "END of UPGRADE attributes" to find the approximate place to add the new item.
Place it in numeric order within the array. For ease of maintenance and debugging, add a comment showing its number. Add any text strings that may be required: SQL statement text, for example, or table names.
You can scan for the tag
This may be a stub, whose only job is to call functions in other parts of the code.
This is the "no special status" attribute. This item applies in all databases.
This status only applies when upgrading the master database.
This status only applies when upgrading the sybsecurity database.
This status only applies when upgrading the sybsystemdb database.
This upgrade item is optional: a failure to install it does not cause upgrade to fail, unless this item is being installed as a dependent item for another item that is not optional. This bit can be OR'ed with other status bits.
Before we even get started on how to do things using internal upgrade, there are steps that happen for every upgrade item. Those steps are listed here, to give us a convenient reference for them:
Find the end of the list of upgrade attributes. (In that file, you can search for the tag "END of UPGRADE attributes", or you can use the array Upgrade_items[] in utils/intupgd.c to help - the attribute numbers are in comments above the items. Searching attrib.lst for that number usually works.) Add a definition for your item, using the existing items as a model. Give the item a mnemonic that will help others understand what it is doing.
As required, modify the number of the "checkpoint" attribute for the version you're working on so that your item has a lower number than it does.
In the array Upgrade_items[], add an upgrade item to identify the new item. Choose the appropriate action ID for your item: you can select from the list in array Upgd_fns[]. (You may have to add your own ID; we'll talk about that in section 5, below.)
If your item depends on other upgrade items, add a dependency list for it; you can use item 1133 and array Ckpt_11_9_3_attr_list as models for how to create a dependency list.
Add your item as a dependency in the present server version's dependency list.
The "data" fields (one integer and one string) vary widely in how they are used. If you are reusing a previously existing upgrade function, use an example of that type to see what information you must provide. If you are adding a new upgrade function, you can design the inputs as needed for your application; but design carefully: other developers may want to reuse your work.
Note that it is not possible to change the data storage design of items already in the field - they can be extended, but not changed! The data is stored in customer databases, and there is no facility to change it.
This will create the required new header and source files containing your new upgrade item definition.
Upgrade action ID
This structure definition will give you the required data types and offsets for the table's column definitions. As a model, use an example showing the appropriate locking style for your table: include/syssn.h is an example for row-locked tables, and include/lock.h is an example for page-locked tables. TAKE NOTE of the required row overhead in each table style, including the row-length word at the end of the fixed-length columns!
Add an array of SYSCOLs for your table, showing all the columns that table should have. (The SYSCOL is defined in include/catalog.h.) All fixed and all variable-length columns must be represented in this array, including any padding space in the fixed part of the row. The only parts of the row that are not represented are the row overhead:
Each slot is tagged to show its ID. If this is a fake table, be sure
to mark it as
Note that the array indexes are not the same as column IDs! The SYSCOL entries themselves hold the column ID. The definitions you provide are indexes into the array of SYSCOLs. The server uses these indexes for setting up internal scans of system tables. You are only required to provide mnemonics for the columns that code will actually search.
Upgrade action ID
(This step may be unnecessary.) If you are adding a column that code will search on, add the appropriate mnemonic for its syscol.c array index. See "add a new system table", above, for more information. If you added the new syscol.c entry above any previously existing entries, adjust the mnemonics as required for the columns that have moved.
Add an array entry to the appropriate SYSCOL array for the table you are modifying. This entry need not be in any particular order; you can add it to the end of the array.
Upgrade action ID
Add the appropriate mnemonic for the new index's ID. This is the index ID, not the array index. It is not used by upgrade directly, only by code that will do scans on that index.
Add an array of INDCOLs for the index you are adding, listing the index columns. Place that array below the SYSCOL array for that table. Add an entry to the array of INDDEFs for that table, pointing to the new array and giving the index name.
Upgrade action ID
No, really: that's it. No other setup required. However ...
Any SQL statement used in upgrade must first check whether the work has been done, and only do it if it hasn't been done. The only exception to this is for statements that don't have any bad side effects from being done more than once. This is required because we don't know when a crash may happen: if the server should happen to crash between executing a statement and recording that it was done, it could be executed twice.
Upgrade executes SQL statements with a special flag,
Our error handling mechanism places an error code into the PSS whenever an error above a certain severity is printed. This happens because the error is printed, without regard for whether the error was raised through ex_raise() or simply printed through ex_callprint(), and without regard for whether the operation ultimately succeeds or fails.
Internal SQL inspects that error code, and presumes that if it is non-zero then there was an error. This is required because internal SQL depends on sequencer(), which always returns "success" if it exits normally; and that can mask certain error conditions that were caught by code lower down. Fixing this will require some amount of redesign within the server.
Many upgrade items can't be accomplished using SQL statements: for example, anything that requires modifying page headers, allocation pages, and similar - no SQL exists to do that.
These actions require special functions to do the work. Internal upgrade
establishes a special function type,
This function must be able to extract all the data it needs from the arguments that are provided to it in the DBTABLE and the Upgrade_items[] entry you will provide. This function may be no more than a stub that calls other functions. Make it as general-purpose as you can, so that others might also be able to use it for future upgrade tasks.
Be very sure that its action ID addresses the correct array entry. Otherwise, the upgrade driver will encounter errors when it tries to invoke that method by its action ID.
Each upgrade step announces when it begins an upgrade step. The function's action is identified by a server phrase, so unless there is an already existing phrase that explains exactly what your function is doing, you will need to add one. Remember to get that phrase reviewed.
Check output from upgrade, and check the error log.
When things go wrong during upgrade, the upgrade utility prints the error messages the server sends it. Depending on their severity, those messages may also appear in the error log. There will probably be several messages, because upgrade comments on failures at every step of the way. The first message is the cause of the failure.
The upgrade code is quite aggressive about dumping the transaction log, so if the server runs out of space in a database, that database probably really is out of space. If the log ran out of space, you can try "dump tran with no_log" but it is quite likely not to work. You will probably need to use alter database to add space. This command does work on offline databases.
You will need to figure out what the dependent item was, and why it failed. Upgrade reports the ID of the failing item; inspect that item to find out what might have gone wrong. That item might have been "optional", so a failure to install didn't cause upgrade to fail; but when another item depends on an optional item, the optional item becomes required.
Upgrade uses "dependency lists" to ensure that everything is in place for the next item it's trying to install. Further, dependencies cascade: one item can depend on another that depends on yet another, as far down the chain as necessary. The server checks dependencies until it finds the bottom of the list or finds an item that is already installed. (Installed items are assumed to have satisfied all their dependencies.)
Did this item use internal SQL to install it? If so, check for messages that may have been printed by ex_callprint(), even if no error was raised. Internal SQL checks pss->plasterror to determine whether any errors occurred. The server's error printing mechanism sets that field depending on error severity, without regard for whether the message is part of an error that's being raised. Thus, depending on severity, informational messages can cause internal SQL to think an operation failed when it actually succeeded. The only fix for this is to change the severity of the printed message, or not to print it at all.
Upgrade stops after the first failure to install a required item. Any function that returns a failing status to the upgrade driver causes upgrade to fail.
Probably, the function that did the work returned a failure status; maybe within a loop, one operation failed, so the function stopped. Unless that really constitutes a failure, that loop should be fixed so that it does all the processing it needs, and simply reports or remembers its failures.
If the server could work without an item, even though it might work slowly, consider making that item "optional". Customers depend on upgrade working correctly the first time, so it should be as close to bullet-proof as we can make it. If an item can be gotten along without, however temporarily, consider letting it be optional and having the customer (or, better, the server itself) go back later and retry the installation.
The upgrade utility may or may not be restartable, depending where the failure happened. If it fails before the message "Setting upgrade version to XXXXX", you can probably re-run the utility. (That will sometimes fail. The upgrade utility is fragile, and prone to failure when faced with unexpected circumstances.) If it fails after that message, you need to reset the server's idea of what version it's at. In SQL:
1>
where "
Otherwise, the utility will not run, saying that the server is already at the target version.
(A side note: internal upgrade is simple to restart: the