Showing 1 changed files with 87 additions and 18 deletions
+87 -18
HealthProbe/Services/SQLiteHealthArchiveStore.swift
@@ -2093,6 +2093,41 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
2093 2093
             db: db,
2094 2094
             statementCache: statementCache
2095 2095
         )
2096
+
2097
+        if sampleResult.inserted {
2098
+            let versionID = try insertSampleVersion(
2099
+                row,
2100
+                sampleID: sampleResult.id,
2101
+                sourceRevisionID: sourceRevisionID,
2102
+                deviceID: deviceID,
2103
+                metadataID: metadataID,
2104
+                observationID: observationID,
2105
+                db: db,
2106
+                statementCache: statementCache
2107
+            )
2108
+            try insertObservationEvent(
2109
+                observationID: observationID,
2110
+                sampleID: sampleResult.id,
2111
+                versionID: versionID,
2112
+                eventKind: "appeared",
2113
+                evidenceKind: "healthkit_sample",
2114
+                observedAt: row.observedAt,
2115
+                db: db,
2116
+                statementCache: statementCache,
2117
+                allowConflict: false
2118
+            )
2119
+            try insertOpenVisibilityRange(
2120
+                sampleID: sampleResult.id,
2121
+                versionID: versionID,
2122
+                observationID: observationID,
2123
+                observedAt: row.observedAt,
2124
+                db: db,
2125
+                statementCache: statementCache,
2126
+                allowConflict: false
2127
+            )
2128
+            return ArchiveV2SampleWriteResult(sampleTypeID: sampleTypeID, kind: .inserted)
2129
+        }
2130
+
2096 2131
         let versionResult = try upsertSampleVersion(
2097 2132
             row,
2098 2133
             sampleID: sampleResult.id,
@@ -2106,10 +2141,7 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
2106 2141
 
2107 2142
         let writeKind: ArchiveV2SampleWriteKind
2108 2143
         let eventKind: String
2109
-        if sampleResult.inserted {
2110
-            writeKind = .inserted
2111
-            eventKind = "appeared"
2112
-        } else if versionResult.inserted {
2144
+        if versionResult.inserted {
2113 2145
             writeKind = .updated
2114 2146
             eventKind = "representationChanged"
2115 2147
         } else {
@@ -2127,16 +2159,7 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
2127 2159
             db: db,
2128 2160
             statementCache: statementCache
2129 2161
         )
2130
-        if sampleResult.inserted {
2131
-            try insertOpenVisibilityRange(
2132
-                sampleID: sampleResult.id,
2133
-                versionID: versionResult.id,
2134
-                observationID: observationID,
2135
-                observedAt: row.observedAt,
2136
-                db: db,
2137
-                statementCache: statementCache
2138
-            )
2139
-        } else if versionResult.inserted {
2162
+        if versionResult.inserted {
2140 2163
             try closeOpenVisibilityRanges(
2141 2164
                 sampleID: sampleResult.id,
2142 2165
                 excludingVersionID: versionResult.id,
@@ -2743,6 +2766,48 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
2743 2766
         return (id, inserted)
2744 2767
     }
2745 2768
 
2769
+    private func insertSampleVersion(
2770
+        _ row: ArchiveSampleRow,
2771
+        sampleID: Int64,
2772
+        sourceRevisionID: Int64?,
2773
+        deviceID: Int64?,
2774
+        metadataID: Int64?,
2775
+        observationID: Int64,
2776
+        db: OpaquePointer?,
2777
+        statementCache: SQLiteStatementCache? = nil
2778
+    ) throws -> Int64 {
2779
+        try withStatement(
2780
+            """
2781
+            INSERT INTO sample_versions (
2782
+                sample_id, payload_hash, start_date, end_date, value_kind, numeric_value,
2783
+                unit, category_value, workout_activity_type, duration_seconds,
2784
+                source_revision_id, hk_device_id, metadata_id, created_observation_id
2785
+            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2786
+            """,
2787
+            db: db,
2788
+            statementCache: statementCache
2789
+        ) { statement in
2790
+            bindInt64(sampleID, to: 1, in: statement)
2791
+            bindText(row.payloadHash, to: 2, in: statement)
2792
+            sqlite3_bind_double(statement, 3, row.startDate.timeIntervalSince1970)
2793
+            sqlite3_bind_double(statement, 4, row.endDate.timeIntervalSince1970)
2794
+            bindText(row.valueKind, to: 5, in: statement)
2795
+            bindDouble(row.value, to: 6, in: statement)
2796
+            bindText(row.unit, to: 7, in: statement)
2797
+            bindInt(row.categoryValue, to: 8, in: statement)
2798
+            bindInt(row.workoutActivityType, to: 9, in: statement)
2799
+            bindDouble(row.durationSeconds, to: 10, in: statement)
2800
+            bindInt64(sourceRevisionID, to: 11, in: statement)
2801
+            bindInt64(deviceID, to: 12, in: statement)
2802
+            bindInt64(metadataID, to: 13, in: statement)
2803
+            bindInt64(observationID, to: 14, in: statement)
2804
+            guard sqlite3_step(statement) == SQLITE_DONE else {
2805
+                throw SQLiteHealthArchiveStoreError.stepFailed(lastErrorMessage(db))
2806
+            }
2807
+        }
2808
+        return sqlite3_last_insert_rowid(db)
2809
+    }
2810
+
2746 2811
     private func insertObservationEvent(
2747 2812
         observationID: Int64,
2748 2813
         sampleID: Int64,
@@ -2751,11 +2816,13 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
2751 2816
         evidenceKind: String,
2752 2817
         observedAt: Date,
2753 2818
         db: OpaquePointer?,
2754
-        statementCache: SQLiteStatementCache? = nil
2819
+        statementCache: SQLiteStatementCache? = nil,
2820
+        allowConflict: Bool = true
2755 2821
     ) throws {
2822
+        let insertMode = allowConflict ? "INSERT OR IGNORE" : "INSERT"
2756 2823
         try withStatement(
2757 2824
             """
2758
-            INSERT OR IGNORE INTO sample_observation_events (
2825
+            \(insertMode) INTO sample_observation_events (
2759 2826
                 observation_id, sample_id, version_id, event_kind, observed_at, evidence_kind
2760 2827
             ) VALUES (?, ?, ?, ?, ?, ?)
2761 2828
             """,
@@ -2841,11 +2908,13 @@ actor SQLiteHealthArchiveStore: HealthArchiveStore {
2841 2908
         observationID: Int64,
2842 2909
         observedAt: Date,
2843 2910
         db: OpaquePointer?,
2844
-        statementCache: SQLiteStatementCache? = nil
2911
+        statementCache: SQLiteStatementCache? = nil,
2912
+        allowConflict: Bool = true
2845 2913
     ) throws {
2914
+        let insertMode = allowConflict ? "INSERT OR IGNORE" : "INSERT"
2846 2915
         try withStatement(
2847 2916
             """
2848
-            INSERT OR IGNORE INTO sample_visibility_ranges (
2917
+            \(insertMode) INTO sample_visibility_ranges (
2849 2918
                 sample_id, version_id, first_observation_id, last_observation_id, first_seen_at, last_seen_at
2850 2919
             ) VALUES (?, ?, ?, NULL, ?, NULL)
2851 2920
             """,