migrateScheme method

  1. @protected
void migrateScheme(
  1. StdMap stored,
  2. StdMap actual
)

Implementation

@protected
void migrateScheme(StdMap stored, StdMap actual) {
  var plan = SchemeMigrationPlan(stored, actual, scheme);
  log.info(
    "Scheme ${stored.isEmpty ? "creation" : "migration"} "
    "plan:\n${plan.dump()}",
  );

  begin();
  try {
    var dropTable = MessageFormat("DROP TABLE {0}");
    var dropIndex = MessageFormat("DROP INDEX {0}");
    var dropColumn = MessageFormat(
      "ALTER TABLE $tableMapping DROP COLUMN {0}",
    );

    for (var si in plan.dropSearchIndexes)
      _database!.execute(dropTable.xFormat([sqlIdentifier(si)]));
    for (var pi in plan.dropPartialIndexes)
      _database!.execute(dropIndex.xFormat([sqlIdentifier(pi)]));
    for (var vc in plan.dropVirtualColumns.xToList().reversed)
      _database!.execute(dropColumn.xFormat([sqlIdentifier(vc)]));
    if (plan.dropKindColumnAndIndex) {
      _database!.execute(dropIndex.xFormat([columnKind]));
      _database!.execute(dropColumn.xFormat([columnKind]));
    }

    if (plan.performRevisionUpgrade)
      scheme.upgrader?.call(plan.storedRevision!, plan.actualRevision, this);

    var makeColumn = MessageFormat(
      "ALTER TABLE $tableMapping ADD COLUMN {0} AS ({1}) VIRTUAL",
    );
    var makeIndex = MessageFormat(
      "CREATE INDEX {0} ON $tableMapping ({1}) WHERE {2}",
    );
    var makeTable = MessageFormat(
      "CREATE VIRTUAL TABLE {0} "
      "USING FTS5($columnKeyId UNINDEXED, {1}, tokenize = "
      "'unicode61 remove_diacritics 2 tokenchars {2}')",
    );

    if (plan.makeKindColumnAndIndex) {
      _database!.execute(
        formatMakeColumn(
          makeColumn,
          columnKind,
          [],
          SqlJX(plan.actualKindPath),
        ),
      );
      _database!.execute(
        formatMakeIndex(makeIndex, columnKind, [IndexColumn(columnKind)]),
      );
    }
    for (var vc in plan.makeVirtualColumns) {
      var column = scheme.getVirtualColumn(vc);
      _database!.execute(
        formatMakeColumn(makeColumn, vc, column.kinds, column.expression),
      );
    }
    for (var pi in plan.makePartialIndexes) {
      var index = scheme.getPartialIndex(pi);
      _database!.execute(formatMakeIndex(makeIndex, pi, index.columns));
    }
    var escapedTokenChars = escapeSingleQuotes(sqlLiteral(extraTokenChars));
    for (var si in plan.makeSearchIndexes) {
      var index = scheme.getSearchIndex(si);
      _database!.execute(
        makeTable.xFormat([
          sqlIdentifier(si),
          index.columnsDefStr,
          escapedTokenChars,
        ]),
      );
    }

    scheme.initializeUpdatersLookup(this);

    if (plan.makeSearchIndexes.isNotEmpty) {
      var where = SqlI(columnKind).equal(SqlP("@$columnKind"));
      QueryId qid = prepare(
        columns: [SqlJT(SqlI(columnValue))],
        where: where,
        limit: queryDefaultLimit,
      );
      finalize(() {
        for (var si in plan.makeSearchIndexes) {
          var index = scheme.getSearchIndex(si);
          for (var MapEntry(key: kind, value: srGetter)
              in index.getters.entries) {
            QueryRowPosition? start;
            do {
              QueryRows rows = query(
                qid,
                parameters: {"@$columnKind": kind},
                start: start,
              );
              for (var row in rows) {
                FullId id = row.id;
                SearchRow searchRow = srGetter(
                  jsonDecode(row.columns.single! as String) as Object,
                );
                index.updater.insert.executeWith(
                  StatementParameters.named({
                    ...searchRowToParameters(searchRow),
                    ":$columnRowId": id.rowid,
                    ":$columnKeyId": id.keyid,
                  }),
                );
                if (!updated())
                  log.severe(
                    "Search index migration failed for rowid ${id.rowid}, "
                    "kind \"$kind\", index name \"${index.name}\"",
                  );
              }
              start = rows.length == queryDefaultLimit
                  ? queryRowGetPosition(rows.last)
                  : null;
            } while (start != null);
          }
        }
      }, finale: () => free(qid));
    }

    _database!.execute(
      "INSERT OR REPLACE INTO $tableScheme "
      "($columnKeyId, $columnValue) VALUES (?, jsonb(?))",
      [keyDefinition, jsonEncode(actual)],
    );
    commit();
    ////
  } on Exception {
    rollback();
    rethrow;
  }
}