// Seal the model itself: static { COLLECTOR_MANAGEMENT_MODEL.seal(); }
@Override public void apply(SQLRecordStore<?, ?, ?> recordStore, UpgradeOperations upgradeOps) throws Exception { // Retrieve all known Models as RecordReferences: List<RecordReference> modelRecRefs = recordStore.retrieveRecordReferences(new RecordsQuery(Model.MODEL_SCHEMA)); /* The RecordReferences contain enough information (all we really need is the modelID) and retrieving them does * *not* involve Model deserialisation (which would fail because the currently stored compressed serialised Java * objects are no longer compatible with the current Model, Schema, Column, etc. classes) */ List<Record> newModelRecs = new ArrayList<Record>(); List<Schema> schemata = new ArrayList<Schema>(); // Get Model instances for the modelRecRefs and loop over them: for (Model model : getModels(recordStore, upgradeOps, modelRecRefs)) { // Create new Model record (to be stored in Models table below): newModelRecs.add(model.getModelRecord(client)); // Remember schema for use below: schemata.addAll(model.getSchemata()); } // Drop existing Models & Schemata tables (will be recreated below): upgradeOps.dropTable(recordStore, getOldTableName(Model.MODEL_SCHEMA), /*force:*/ true); upgradeOps.dropTable(recordStore, getOldTableName(Model.SCHEMA_SCHEMA), /*force:*/ true); // Create new Models table and insert new records: recordStore.store( newModelRecs); // this also achieves renaming the "compressedSerialisedObject" column to // "serialisation" // List for the names of all tables that should be kept: Set<String> keepTables = new HashSet<String>(recordStore.getProtectedTableNames()); // Loop over all schemata: for (Schema schema : schemata) { // Check if there is a table, with the old name (which is not necessarily different from the // new name), for the schema: if (!upgradeOps.doesTableExist(recordStore, getOldTableName(schema))) continue; // if there is no table we are done with this Schema // Remember (new) table so we don't delete the table below: keepTables.add(schema.tableName); // !!! // Store new schemata (for new tablename) record: recordStore.store( schema.getMetaRecord()); // this also achieves adding new "flags" and "tableName" columns // Rename table if necessary: String oldName = getOldTableName(schema); if (!oldName.equals(schema.tableName)) upgradeOps.renameTable(recordStore, oldName, schema.tableName); // Get a TableConverter for the schema: TableConverter tableConverter = new TableConverter( schema, schema.flags & ~StorageClient .SCHEMA_FLAG_TRACK_LOSSLESSNESS); // un-set the lossless flag on the old // schema // Add a column replacer to to deal with the added LosslessFlagColumn in schemas that have // that flag: if (schema.hasFlags(StorageClient.SCHEMA_FLAG_TRACK_LOSSLESSNESS)) tableConverter.addColumnReplacer( new DefaultValueColumnAdder( LosslessFlagColumn.INSTANCE)); // (this makes the tableConverter non-transparent) // Check if the schema requires other conversions: boolean hasValueSetColWithAllOptionalSubCols = false; boolean hasListColumnThatNeedsConversion = false; boolean containsStringListColumns = false; for (Column<?> col : tableConverter.getOldSchema().getColumns(false)) { if (col instanceof ValueSetColumn<?, ?> && ((ValueSetColumn<?, ?>) col).hasAllOptionalSubColumns()) hasValueSetColWithAllOptionalSubCols = true; else if (col instanceof ListColumn && !(col instanceof ByteArrayListColumn)) { hasListColumnThatNeedsConversion = true; if (((ListColumn<?, ?>) col).getSingleColumn() instanceof StringColumn) containsStringListColumns = true; } } /* Deal with changed number of bytes per character (4 instead of 3) in UTF-8 StringColumns * This is not relevant to StringColumns themselves because those remain backed by String-based SQLColumns. * Yet StringListColumn/ListColumn.Simple<String> are affected by the change as those used to be backed by * BLOB-bases SQLColumns. */ if (containsStringListColumns) tableConverter.addColumnReplacer( new StringListColumnReplacer()); // (this makes the tableConverter non-transparent) // Give subclass the change to further tweak the table converter: customiseTableConverter(schema, tableConverter); // Check if we need to do anything: if (tableConverter.isTransparent() && !hasValueSetColWithAllOptionalSubCols && !hasListColumnThatNeedsConversion) // this schema/table does not need conversion. continue; if (hasValueSetColWithAllOptionalSubCols) // Temporarily disable the use of boolean columns to represent optional ValueSetColumns: upgradeOps.getTableFactory(recordStore).setInsertBoolColsForAllOptionalValueSetCols(false); /* This is required because the tables currently existing in the db are incompatible with * the SQLRecordStore#SQLTable instance we would get for the schema if we wouldn't disable * this behaviour. Disabling the behaviour ensures we get a SQLTable that is compatible with * the table as it exists in the db, enabling us to ... */ if (hasListColumnThatNeedsConversion) // Temporarily switch to using BLOB-base SQLColumns for all ListColumns, // so we can read from the existing BLOB-backed ListColumns: upgradeOps.getTableFactory(recordStore).setUseBLOBsForAllListColumns(true); // Make sure a new SQLTable instance will be constructed based on the old schema: upgradeOps.forgetTable(recordStore, schema.tableName); // Get all current records by querying the db with the oldSchema: List<Record> oldRecords = recordStore.retrieveRecords(tableConverter.getOldSchema()); // Drop table (this will also get rid of the above-mentioned SQLTable instance): upgradeOps.dropTable(recordStore, schema.tableName, false); if (hasValueSetColWithAllOptionalSubCols) // Re-enable the use of boolean columns to represent optional ValueSetColumns: upgradeOps.getTableFactory(recordStore).setInsertBoolColsForAllOptionalValueSetCols(true); if (hasListColumnThatNeedsConversion) // Switch off use of BLOB-based SQLColumn for all ListColumns: upgradeOps.getTableFactory(recordStore).setUseBLOBsForAllListColumns(false); // Re-insert all converted records in new table (which will have the boolean column // representing the ValueSetColumn): recordStore.store(tableConverter.convertRecords(oldRecords)); } // Delete unknown/unupgradable tables: for (String tableName : upgradeOps.getAllTableNames(recordStore)) if (!keepTables.contains(tableName)) { upgradeOps.addWarning("Deleting unknown table \'" + tableName + "\'"); upgradeOps.dropTable(recordStore, tableName, false); } // Upgrade step done! }