Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

watch steam just fake death after get empty dataset #3406

Closed
chdhy opened this issue Jan 12, 2025 · 8 comments
Closed

watch steam just fake death after get empty dataset #3406

chdhy opened this issue Jan 12, 2025 · 8 comments

Comments

@chdhy
Copy link

chdhy commented Jan 12, 2025

Hi diift teams.

I have a trigger to record a bill update, it will write data to bill_log. And watch the bill_log, then process it and delete the bill_log record.

the watch stream just fake death once it gets empty bill_log, it never get any more bill_log. the stream neither call done or error.

the code sample likes this:

  Stream<BillLog?> watchBillLog() => (select(billLogs)
        ..orderBy([(t) => OrderingTerm(expression: t.createdTime, mode: OrderingMode.asc)])
        ..limit(1)
  )
      .watchSingleOrNull()
      .where((billLog) => billLog != null)
      .cast<BillLog>(); 

or this:

  Stream<BillLog?> watchBillLog() {
    return billLogs.select().watch().switchMap((_) {
      return (select(billLogs)
        ..orderBy([(t) => OrderingTerm(expression: t.createdTime, mode: OrderingMode.desc)])
        ..limit(1))
          .watch()
          .map((rows) => rows.isNotEmpty ? rows.first : null);
    });

version:
drift: ^2.21.0
drift_flutter: ^0.2.1

thanks

@simolus3
Copy link
Owner

simolus3 commented Jan 12, 2025

the watch stream just fake death once it gets empty bill_log, it never get any more bill_log. the stream neither call done or error.

That's definitely not the expected behavior, drift should keep on listening until you cancel the subscription. Could you also show how you're listening to the stream?

@chdhy
Copy link
Author

chdhy commented Jan 13, 2025

thanks for your reply @simolus3 , here is my test code:

    test("watchBillLog 1", () async {
      var expectedValues = [
        // insert
        billLog.copyWith(
            newSourceAssetAccountId: const drift.Value("assetAccount1"), newDate: const drift.Value("2025-01-10")),
        billLog.copyWith(
            newSourceAssetAccountId: const drift.Value("assetAccount1"), newDate: const drift.Value("2025-01-09")),
        billLog.copyWith(
            newSourceAssetAccountId: const drift.Value("assetAccount2"), newDate: const drift.Value("2025-01-09")),
        // update
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount1"),
            oldDate: const drift.Value("2025-01-10"),
            newSourceAssetAccountId: const drift.Value("assetAccount1"),
            newDate: const drift.Value("2024-12-09")),
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount1"),
            oldDate: const drift.Value("2025-01-09"),
            newSourceAssetAccountId: const drift.Value("assetAccount12"),
            newDate: const drift.Value("2025-01-09")),
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount2"),
            oldDate: const drift.Value("2025-01-09"),
            newSourceAssetAccountId: const drift.Value("assetAccount23"),
            newDate: const drift.Value("2024-11-11")),
        // softDelete
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount1"),
            oldDate: const drift.Value("2024-12-09"),
            newSourceAssetAccountId: const drift.Value("assetAccount1"),
            newDate: const drift.Value("2024-12-09")),
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount12"),
            oldDate: const drift.Value("2025-01-09"),
            newSourceAssetAccountId: const drift.Value("assetAccount12"),
            newDate: const drift.Value("2025-01-09")),
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount23"),
            oldDate: const drift.Value("2024-11-11"),
            newSourceAssetAccountId: const drift.Value("assetAccount23"),
            newDate: const drift.Value("2024-11-11")),
        // hardDelete
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount1"),
            oldDate: const drift.Value("2024-12-09"),
            newSourceAssetAccountId: const drift.Value.absent(),
            newDate: const drift.Value.absent()),
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount12"),
            oldDate: const drift.Value("2025-01-09"),
            newSourceAssetAccountId: const drift.Value.absent(),
            newDate: const drift.Value.absent()),
        billLog.copyWith(
            oldSourceAssetAccountId: const drift.Value("assetAccount23"),
            oldDate: const drift.Value("2024-11-11"),
            newSourceAssetAccountId: const drift.Value.absent(),
            newDate: const drift.Value.absent()),
      ];
      var collectedData = <BillLog>[];
      final completer = Completer<void>();

      var subscription = billLogDao.watchBillLog().listen((data) async {
        if (data == null) return;

        await billLogDao.hardDeleteBillLog(data);
        collectedData.add(data);
        print("collectedData: ${collectedData.length} expectedValues: ${expectedValues.length}");
        if (collectedData.length == expectedValues.length) {
          completer.complete();
        }
      });

      // insert
      var bill1 = await billDao.insertBill(bill.copyWith(date: "2025-01-10", sourceAssetAccountId: "assetAccount1"));
      var bill2 = await billDao.insertBill(bill.copyWith(date: "2025-01-09", sourceAssetAccountId: "assetAccount1"));
      var bill3 = await billDao.insertBill(bill.copyWith(date: "2025-01-09", sourceAssetAccountId: "assetAccount2"));

      // update
      var updatedBill1 = await billDao.updateBill(bill1!.copyWith(date: "2024-12-09"));
      var updatedBill2 = await billDao.updateBill(bill2!.copyWith(sourceAssetAccountId: "assetAccount12"));
      var updatedBill3 =
          await billDao.updateBill(bill3!.copyWith(date: "2024-11-11", sourceAssetAccountId: "assetAccount23"));

      // once I remove this await, this test pass, but in real scenario, the billLog won't constantly produced
      await Future.delayed(const Duration(milliseconds: 1));
      // softDelete
      var softDeletedBill1 = await billDao.softDeleteBill(updatedBill1.first);
      var softDeletedBill2 = await billDao.softDeleteBill(updatedBill2.first);
      var softDeletedBill3 = await billDao.softDeleteBill(updatedBill3.first);

      // hardDelete
      await database.delete(database.bills).delete(softDeletedBill1.first);
      await database.delete(database.bills).delete(softDeletedBill2.first);
      await database.delete(database.bills).delete(softDeletedBill3.first);

      print(await database.billLogs.select().get());
      await completer.future;

      collectedData.forEachIndexed((index, element) {
        expect(element.equals(expectedValues[index]), true);
      });

      var billLogs = await database.billLogs.select().get();
      expect(billLogs.length, 0);

      subscription.cancel();
    });

the test keep running forever. and the log sample like this:

Drift: Sent           CREATE TRIGGER insert_bill_count
          AFTER INSERT ON bills
          BEGIN
            INSERT OR REPLACE INTO bill_logs (old_source_asset_account_id, new_source_asset_account_id, old_dest_asset_account_id, new_dest_asset_account_id, old_date, new_date, created_time)
            VALUES (NULL, NEW.source_asset_account_id, NULL, NEW.dest_asset_account_id, NULL, NEW.date, datetime('now'));
          END;
          
          CREATE TRIGGER update_bill_count
          AFTER UPDATE ON bills
          BEGIN
            INSERT OR REPLACE INTO bill_logs (old_source_asset_account_id, new_source_asset_account_id, old_dest_asset_account_id, new_dest_asset_account_id, old_date, new_date, created_time)
            VALUES (OLD.source_asset_account_id, NEW.source_asset_account_id, OLD.dest_asset_account_id, NEW.dest_asset_account_id, OLD.date, NEW.date, datetime('now'));
          END;
          
          CREATE TRIGGER delete_bill_count
          AFTER DELETE ON bills
          BEGIN
            INSERT OR REPLACE INTO bill_logs (old_source_asset_account_id, new_source_asset_account_id, old_dest_asset_account_id, new_dest_asset_account_id, old_date, new_date, created_time)
            VALUES (OLD.source_asset_account_id, NULL, OLD.dest_asset_account_id, NULL, OLD.date, NULL, datetime('now'));
          END;
         with args []
Drift: Sent INSERT OR IGNORE INTO "bills" ("id", "transaction_type", "category_id", "account_book_id", "source_asset_account_id", "amount", "other_amount", "date", "exclude_in_budget", "created_time", "updated_time", "is_deleted") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING * with args [45
53190f-16af-4716-952a-5aaa7942a953, 0, categoryId, accountBookId, assetAccount1, 1.0, 1.0, 2025-01-10, 0, 2025-01-13T09:45:45.840276 +08:00, 2025-01-13T09:45:45.840276 +08:00, 0]
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
Drift: Sent INSERT OR IGNORE INTO "bills" ("id", "transaction_type", "category_id", "account_book_id", "source_asset_account_id", "amount", "other_amount", "date", "exclude_in_budget", "created_time", "updated_time", "is_deleted") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING * with args [1d
bba1f5-c90d-4b14-91fa-a5aecc694b7c, 0, categoryId, accountBookId, assetAccount1, 1.0, 1.0, 2025-01-09, 0, 2025-01-13T09:45:46.003910 +08:00, 2025-01-13T09:45:46.003910 +08:00, 0]
watchBillLog: [BillLog(id: 1, oldSourceAssetAccountId: null, newSourceAssetAccountId: assetAccount1, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: null, newDate: 2025-01-10, isDeleted: false, createdTime: 2025-01-13 01:45:45.000Z)]
Drift: Sent INSERT OR IGNORE INTO "bills" ("id", "transaction_type", "category_id", "account_book_id", "source_asset_account_id", "amount", "other_amount", "date", "exclude_in_budget", "created_time", "updated_time", "is_deleted") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING * with args [cd
925892-1c76-4f9f-8a41-7c12552860a4, 0, categoryId, accountBookId, assetAccount2, 1.0, 1.0, 2025-01-09, 0, 2025-01-13T09:45:46.012957 +08:00, 2025-01-13T09:45:46.012957 +08:00, 0]
Drift: Sent DELETE FROM "bill_logs" WHERE "id" = ?; with args [1]
collectedData: 1 expectedValues: 12
Drift: Sent UPDATE "bills" SET "id" = ?, "transaction_type" = ?, "category_id" = ?, "account_book_id" = ?, "source_asset_account_id" = ?, "amount" = ?, "other_amount" = ?, "date" = ?, "exclude_in_budget" = ?, "created_time" = ?, "updated_time" = ?, "is_deleted" = ? WHERE "id" = ? RETURNING *; with a
rgs [4553190f-16af-4716-952a-5aaa7942a953, 0, categoryId, accountBookId, assetAccount1, 1.0, 1.0, 2024-12-09, 0, 2025-01-13T09:45:45.840276 +08:00, 2025-01-13T09:45:46.025184 +08:00, 0, 4553190f-16af-4716-952a-5aaa7942a953]
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
Drift: Sent UPDATE "bills" SET "id" = ?, "transaction_type" = ?, "category_id" = ?, "account_book_id" = ?, "source_asset_account_id" = ?, "amount" = ?, "other_amount" = ?, "date" = ?, "exclude_in_budget" = ?, "created_time" = ?, "updated_time" = ?, "is_deleted" = ? WHERE "id" = ? RETURNING *; with a
rgs [1dbba1f5-c90d-4b14-91fa-a5aecc694b7c, 0, categoryId, accountBookId, assetAccount12, 1.0, 1.0, 2025-01-09, 0, 2025-01-13T09:45:46.003910 +08:00, 2025-01-13T09:45:46.034763 +08:00, 0, 1dbba1f5-c90d-4b14-91fa-a5aecc694b7c]
watchBillLog: [BillLog(id: 2, oldSourceAssetAccountId: null, newSourceAssetAccountId: assetAccount1, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: null, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 3, oldSourceAssetAccountId: null, newSourceAssetAccountId: assetAccount2, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: null, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 4, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount1, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-10, newDate: 2024-12-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z)]
Drift: Sent DELETE FROM "bill_logs" WHERE "id" = ?; with args [2]
collectedData: 2 expectedValues: 12
Drift: Sent UPDATE "bills" SET "id" = ?, "transaction_type" = ?, "category_id" = ?, "account_book_id" = ?, "source_asset_account_id" = ?, "amount" = ?, "other_amount" = ?, "date" = ?, "exclude_in_budget" = ?, "created_time" = ?, "updated_time" = ?, "is_deleted" = ? WHERE "id" = ? RETURNING *; with a
rgs [cd925892-1c76-4f9f-8a41-7c12552860a4, 0, categoryId, accountBookId, assetAccount23, 1.0, 1.0, 2024-11-11, 0, 2025-01-13T09:45:46.012957 +08:00, 2025-01-13T09:45:46.038439 +08:00, 0, cd925892-1c76-4f9f-8a41-7c12552860a4]
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
watchBillLog: [BillLog(id: 3, oldSourceAssetAccountId: null, newSourceAssetAccountId: assetAccount2, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: null, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 4, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount1, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-10, newDate: 2024-12-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 5, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount12, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 6, oldSourceAssetAccountId: assetAccount2, newSourceAssetAccountId: assetAccount23, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2024-11-11, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z)]
Drift: Sent DELETE FROM "bill_logs" WHERE "id" = ?; with args [3]
collectedData: 3 expectedValues: 12
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
watchBillLog: [BillLog(id: 4, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount1, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-10, newDate: 2024-12-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 5, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount12, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 6, oldSourceAssetAccountId: assetAccount2, newSourceAssetAccountId: assetAccount23, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2024-11-11, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z)]
Drift: Sent DELETE FROM "bill_logs" WHERE "id" = ?; with args [4]
collectedData: 4 expectedValues: 12
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
watchBillLog: [BillLog(id: 5, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount12, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 6, oldSourceAssetAccountId: assetAccount2, newSourceAssetAccountId: assetAccount23, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2024-11-11, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z)]
Drift: Sent DELETE FROM "bill_logs" WHERE "id" = ?; with args [5]
collectedData: 5 expectedValues: 12
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
watchBillLog: [BillLog(id: 6, oldSourceAssetAccountId: assetAccount2, newSourceAssetAccountId: assetAccount23, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2024-11-11, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z)]
Drift: Sent DELETE FROM "bill_logs" WHERE "id" = ?; with args [6]
collectedData: 6 expectedValues: 12
Drift: Sent SELECT * FROM "bill_logs" WHERE "is_deleted" = ? ORDER BY "created_time" ASC; with args [0]
watchBillLog: []
Drift: Sent UPDATE "bills" SET "is_deleted" = 1 WHERE "id" = ? RETURNING *; with args [4553190f-16af-4716-952a-5aaa7942a953]
Drift: Sent UPDATE "bills" SET "is_deleted" = 1 WHERE "id" = ? RETURNING *; with args [1dbba1f5-c90d-4b14-91fa-a5aecc694b7c]
Drift: Sent UPDATE "bills" SET "is_deleted" = 1 WHERE "id" = ? RETURNING *; with args [cd925892-1c76-4f9f-8a41-7c12552860a4]
Drift: Sent DELETE FROM "bills" WHERE "id" = ?; with args [4553190f-16af-4716-952a-5aaa7942a953]
Drift: Sent DELETE FROM "bills" WHERE "id" = ?; with args [1dbba1f5-c90d-4b14-91fa-a5aecc694b7c]
Drift: Sent DELETE FROM "bills" WHERE "id" = ?; with args [cd925892-1c76-4f9f-8a41-7c12552860a4]
Drift: Sent SELECT * FROM "bill_logs"; with args []


[BillLog(id: 7, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: assetAccount1, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2024-12-09, newDate: 2024-12-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 8, oldSourceAssetAccountId: assetAccount12, newSourceAssetAccountId: assetAccount12, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: 2025-01-09, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 9, oldSourceAssetAccountId: assetAccount23, newSourceAssetAccountId: assetAccount23, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2024-11-11, newDate: 2024-11-11, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 10, oldSourceAssetAccountId: assetAccount1, newSourceAssetAccountId: null, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2024-12-09, newDate: null, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 11, oldSourceAssetAccountId: assetAccount12, newSourceAssetAccountId: null, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2025-01-09, newDate: null, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z), BillLog(id: 12, oldSourceAssetAccountId: assetAccount23, newSourceAssetAccountId: null, oldDestAssetAccountId: null, newDestAssetAccountId: null, oldDate: 2024-11-11, newDate: null, isDeleted: false, createdTime: 2025-01-13 01:45:46.000Z)]

I just received 6 billLogs, the expected is 12. In the last part of the log, I checked the billLog table, it contains the remaining 6 billLogs, but didn't get it from the stream.

If I remove the await Future.delayed(const Duration(milliseconds: 1));, the test pass. If I move this line to the next line after this, I receive 7 billLogs, it seems the streams fake death anywhere I await.

If more information is needed, please let me know

@chdhy
Copy link
Author

chdhy commented Jan 13, 2025

FYI, the issue seems related to the trigger, the test above inserts bill data, and the trigger inserts bill_log data. I changed the test to insert bill_log directly, and the test also passed:

      await database.into(database.billLogs).insert(expectedValues[0]);
      await database.into(database.billLogs).insert(expectedValues[1]);
      await database.into(database.billLogs).insert(expectedValues[2]);
      await database.into(database.billLogs).insert(expectedValues[3]);
      await database.into(database.billLogs).insert(expectedValues[4]);
      await Future.delayed(const Duration(milliseconds: 1000));
      await database.into(database.billLogs).insert(expectedValues[5]);
      await database.into(database.billLogs).insert(expectedValues[6]);
      await database.into(database.billLogs).insert(expectedValues[7]);
      await database.into(database.billLogs).insert(expectedValues[8]);
      await database.into(database.billLogs).insert(expectedValues[9]);
      await database.into(database.billLogs).insert(expectedValues[10]);
      await database.into(database.billLogs).insert(expectedValues[11]);

@simolus3
Copy link
Owner

Sorry for the slow response. Could you share how you're defining the trigger? Drift needs information about how changes in one table might affect rows in another table. If you define triggers in a drift file that's no problem, but if you add the trigger manually with a customStatement('create trigger ...') you also need to tell drift about it. I can provide better hints after seeing how the trigger was defined.

@chdhy
Copy link
Author

chdhy commented Jan 17, 2025

sure. I define the trigger in database class, override the migration and define the trigger in the onCrete param:

  @override
  MigrationStrategy get migration => MigrationStrategy(onCreate: (Migrator m) async {
        await m.createAll();

        await customStatement('''
          CREATE TRIGGER insert_bill_count
          AFTER INSERT ON bills
          BEGIN
            INSERT OR REPLACE INTO bill_logs (old_source_asset_account_id, new_source_asset_account_id, old_dest_asset_account_id, new_dest_asset_account_id, old_date, new_date, created_time)
            VALUES (NULL, NEW.source_asset_account_id, NULL, NEW.dest_asset_account_id, NULL, NEW.date, datetime('now'));
          END;

          CREATE TRIGGER update_bill_count
          AFTER UPDATE ON bills
          BEGIN
            INSERT OR REPLACE INTO bill_logs (old_source_asset_account_id, new_source_asset_account_id, old_dest_asset_account_id, new_dest_asset_account_id, old_date, new_date, created_time)
            VALUES (OLD.source_asset_account_id, NEW.source_asset_account_id, OLD.dest_asset_account_id, NEW.dest_asset_account_id, OLD.date, NEW.date, datetime('now'));
          END;

          CREATE TRIGGER delete_bill_count
          AFTER DELETE ON bills
          BEGIN
            INSERT OR REPLACE INTO bill_logs (old_source_asset_account_id, new_source_asset_account_id, old_dest_asset_account_id, new_dest_asset_account_id, old_date, new_date, created_time)
            VALUES (OLD.source_asset_account_id, NULL, OLD.dest_asset_account_id, NULL, OLD.date, NULL, datetime('now'));
          END;
        ''');
      });

@simolus3
Copy link
Owner

You can add the following override to your database class to fix this. It tells drift that changes to the bills table can cause inserts on the bill_logs table.

  @override
  StreamQueryUpdateRules get streamUpdateRules => StreamQueryUpdateRules([
        ...super.streamUpdateRules.rules,
        WritePropagation(
          on: TableUpdateQuery.onTableName('bills'),
          result: [
            TableUpdate('bill_logs', kind: UpdateKind.insert),
          ],
        ),
      ]);

@chdhy
Copy link
Author

chdhy commented Jan 17, 2025

It works well, thanks a lot! Since I am still unfamiliar with streamUpdateRules, I don't find many docs about it. even in the drift doc site: https://drift.simonbinder.eu/. could you please give me more information or references about streamUpdateRules, thanks.

@simolus3
Copy link
Owner

simolus3 commented Jan 18, 2025

These aren't mentioned on the documentation site because they're not typically relevant to most users. You can read more on the dart API docs, although I admit they're a bit sparse. Let me know if you're interested about something specifically.

The basic idea is that drift implements stream queries by:

  1. Keeping track of which stream reads from which tables.
  2. Tracking update/insert/deletion statements to learn when a table might have changed.

This only works when all affected writes are known to drift, but triggers change tables on a lower level. So drift needs to know about how updates to one table (bills) might affect another one (bill_logs). This information is encoded in stream update rules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants