1 package org
.asamk
.signal
.manager
.storage
;
3 import com
.zaxxer
.hikari
.HikariDataSource
;
5 import org
.asamk
.signal
.manager
.api
.Pair
;
6 import org
.asamk
.signal
.manager
.storage
.groups
.GroupStore
;
7 import org
.asamk
.signal
.manager
.storage
.identities
.IdentityKeyStore
;
8 import org
.asamk
.signal
.manager
.storage
.keyValue
.KeyValueStore
;
9 import org
.asamk
.signal
.manager
.storage
.prekeys
.KyberPreKeyStore
;
10 import org
.asamk
.signal
.manager
.storage
.prekeys
.PreKeyStore
;
11 import org
.asamk
.signal
.manager
.storage
.prekeys
.SignedPreKeyStore
;
12 import org
.asamk
.signal
.manager
.storage
.recipients
.CdsiStore
;
13 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientStore
;
14 import org
.asamk
.signal
.manager
.storage
.sendLog
.MessageSendLogStore
;
15 import org
.asamk
.signal
.manager
.storage
.senderKeys
.SenderKeyRecordStore
;
16 import org
.asamk
.signal
.manager
.storage
.senderKeys
.SenderKeySharedStore
;
17 import org
.asamk
.signal
.manager
.storage
.sessions
.SessionStore
;
18 import org
.asamk
.signal
.manager
.storage
.stickers
.StickerStore
;
19 import org
.slf4j
.Logger
;
20 import org
.slf4j
.LoggerFactory
;
21 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
;
22 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
.ACI
;
23 import org
.whispersystems
.signalservice
.api
.util
.UuidUtil
;
26 import java
.sql
.Connection
;
27 import java
.sql
.SQLException
;
28 import java
.sql
.Statement
;
29 import java
.util
.HashMap
;
30 import java
.util
.Optional
;
31 import java
.util
.UUID
;
33 public class AccountDatabase
extends Database
{
35 private static final Logger logger
= LoggerFactory
.getLogger(AccountDatabase
.class);
36 private static final long DATABASE_VERSION
= 27;
38 private AccountDatabase(final HikariDataSource dataSource
) {
39 super(logger
, DATABASE_VERSION
, dataSource
);
42 public static AccountDatabase
init(File databaseFile
) throws SQLException
{
43 return initDatabase(databaseFile
, AccountDatabase
::new);
47 protected void createDatabase(final Connection connection
) throws SQLException
{
48 RecipientStore
.createSql(connection
);
49 MessageSendLogStore
.createSql(connection
);
50 StickerStore
.createSql(connection
);
51 PreKeyStore
.createSql(connection
);
52 SignedPreKeyStore
.createSql(connection
);
53 KyberPreKeyStore
.createSql(connection
);
54 GroupStore
.createSql(connection
);
55 SessionStore
.createSql(connection
);
56 IdentityKeyStore
.createSql(connection
);
57 SenderKeyRecordStore
.createSql(connection
);
58 SenderKeySharedStore
.createSql(connection
);
59 KeyValueStore
.createSql(connection
);
60 CdsiStore
.createSql(connection
);
61 UnknownStorageIdStore
.createSql(connection
);
65 protected void upgradeDatabase(final Connection connection
, final long oldVersion
) throws SQLException
{
67 logger
.debug("Updating database: Creating recipient table");
68 try (final var statement
= connection
.createStatement()) {
69 statement
.executeUpdate("""
70 CREATE TABLE recipient (
71 _id INTEGER PRIMARY KEY AUTOINCREMENT,
75 profile_key_credential BLOB,
81 expiration_time INTEGER NOT NULL DEFAULT 0,
82 blocked INTEGER NOT NULL DEFAULT FALSE,
83 archived INTEGER NOT NULL DEFAULT FALSE,
84 profile_sharing INTEGER NOT NULL DEFAULT FALSE,
86 profile_last_update_timestamp INTEGER NOT NULL DEFAULT 0,
87 profile_given_name TEXT,
88 profile_family_name TEXT,
90 profile_about_emoji TEXT,
91 profile_avatar_url_path TEXT,
92 profile_mobile_coin_address BLOB,
93 profile_unidentified_access_mode TEXT,
94 profile_capabilities TEXT
100 logger
.debug("Updating database: Creating sticker table");
101 try (final var statement
= connection
.createStatement()) {
102 statement
.executeUpdate("""
103 CREATE TABLE sticker (
104 _id INTEGER PRIMARY KEY,
105 pack_id BLOB UNIQUE NOT NULL,
106 pack_key BLOB NOT NULL,
107 installed INTEGER NOT NULL DEFAULT FALSE
112 if (oldVersion
< 4) {
113 logger
.debug("Updating database: Creating pre key tables");
114 try (final var statement
= connection
.createStatement()) {
115 statement
.executeUpdate("""
116 CREATE TABLE signed_pre_key (
117 _id INTEGER PRIMARY KEY,
118 account_id_type INTEGER NOT NULL,
119 key_id INTEGER NOT NULL,
120 public_key BLOB NOT NULL,
121 private_key BLOB NOT NULL,
122 signature BLOB NOT NULL,
123 timestamp INTEGER DEFAULT 0,
124 UNIQUE(account_id_type, key_id)
126 CREATE TABLE pre_key (
127 _id INTEGER PRIMARY KEY,
128 account_id_type INTEGER NOT NULL,
129 key_id INTEGER NOT NULL,
130 public_key BLOB NOT NULL,
131 private_key BLOB NOT NULL,
132 UNIQUE(account_id_type, key_id)
137 if (oldVersion
< 5) {
138 logger
.debug("Updating database: Creating group tables");
139 try (final var statement
= connection
.createStatement()) {
140 statement
.executeUpdate("""
141 CREATE TABLE group_v2 (
142 _id INTEGER PRIMARY KEY,
143 group_id BLOB UNIQUE NOT NULL,
144 master_key BLOB NOT NULL,
146 distribution_id BLOB UNIQUE NOT NULL,
147 blocked INTEGER NOT NULL DEFAULT FALSE,
148 permission_denied INTEGER NOT NULL DEFAULT FALSE
150 CREATE TABLE group_v1 (
151 _id INTEGER PRIMARY KEY,
152 group_id BLOB UNIQUE NOT NULL,
153 group_id_v2 BLOB UNIQUE,
156 expiration_time INTEGER NOT NULL DEFAULT 0,
157 blocked INTEGER NOT NULL DEFAULT FALSE,
158 archived INTEGER NOT NULL DEFAULT FALSE
160 CREATE TABLE group_v1_member (
161 _id INTEGER PRIMARY KEY,
162 group_id INTEGER NOT NULL REFERENCES group_v1 (_id) ON DELETE CASCADE,
163 recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
164 UNIQUE(group_id, recipient_id)
169 if (oldVersion
< 6) {
170 logger
.debug("Updating database: Creating session tables");
171 try (final var statement
= connection
.createStatement()) {
172 statement
.executeUpdate("""
173 CREATE TABLE session (
174 _id INTEGER PRIMARY KEY,
175 account_id_type INTEGER NOT NULL,
176 recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
177 device_id INTEGER NOT NULL,
178 record BLOB NOT NULL,
179 UNIQUE(account_id_type, recipient_id, device_id)
184 if (oldVersion
< 7) {
185 logger
.debug("Updating database: Creating identity table");
186 try (final var statement
= connection
.createStatement()) {
187 statement
.executeUpdate("""
188 CREATE TABLE identity (
189 _id INTEGER PRIMARY KEY,
190 recipient_id INTEGER UNIQUE NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
191 identity_key BLOB NOT NULL,
192 added_timestamp INTEGER NOT NULL,
193 trust_level INTEGER NOT NULL
198 if (oldVersion
< 8) {
199 logger
.debug("Updating database: Creating sender key tables");
200 try (final var statement
= connection
.createStatement()) {
201 statement
.executeUpdate("""
202 CREATE TABLE sender_key (
203 _id INTEGER PRIMARY KEY,
204 recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
205 device_id INTEGER NOT NULL,
206 distribution_id BLOB NOT NULL,
207 record BLOB NOT NULL,
208 created_timestamp INTEGER NOT NULL,
209 UNIQUE(recipient_id, device_id, distribution_id)
211 CREATE TABLE sender_key_shared (
212 _id INTEGER PRIMARY KEY,
213 recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
214 device_id INTEGER NOT NULL,
215 distribution_id BLOB NOT NULL,
216 timestamp INTEGER NOT NULL,
217 UNIQUE(recipient_id, device_id, distribution_id)
222 if (oldVersion
< 9) {
223 logger
.debug("Updating database: Adding urgent field");
224 try (final var statement
= connection
.createStatement()) {
225 statement
.executeUpdate("""
226 ALTER TABLE message_send_log_content ADD COLUMN urgent INTEGER NOT NULL DEFAULT TRUE;
230 if (oldVersion
< 10) {
231 logger
.debug("Updating database: Key tables on serviceId instead of recipientId");
232 try (final var statement
= connection
.createStatement()) {
233 statement
.executeUpdate("""
234 CREATE TABLE identity2 (
235 _id INTEGER PRIMARY KEY,
236 uuid BLOB UNIQUE NOT NULL,
237 identity_key BLOB NOT NULL,
238 added_timestamp INTEGER NOT NULL,
239 trust_level INTEGER NOT NULL
241 INSERT INTO identity2 (_id, uuid, identity_key, added_timestamp, trust_level)
242 SELECT i._id, r.uuid, i.identity_key, i.added_timestamp, i.trust_level
243 FROM identity i LEFT JOIN recipient r ON i.recipient_id = r._id
244 WHERE uuid IS NOT NULL;
246 ALTER TABLE identity2 RENAME TO identity;
248 DROP INDEX msl_recipient_index;
249 ALTER TABLE message_send_log ADD COLUMN uuid BLOB;
250 UPDATE message_send_log
252 FROM message_send_log i, (SELECT _id, uuid FROM recipient) AS r
253 WHERE i.recipient_id = r._id;
254 DELETE FROM message_send_log WHERE uuid IS NULL;
255 ALTER TABLE message_send_log DROP COLUMN recipient_id;
256 CREATE INDEX msl_recipient_index ON message_send_log (uuid, device_id, content_id);
258 CREATE TABLE sender_key2 (
259 _id INTEGER PRIMARY KEY,
261 device_id INTEGER NOT NULL,
262 distribution_id BLOB NOT NULL,
263 record BLOB NOT NULL,
264 created_timestamp INTEGER NOT NULL,
265 UNIQUE(uuid, device_id, distribution_id)
267 INSERT INTO sender_key2 (_id, uuid, device_id, distribution_id, record, created_timestamp)
268 SELECT s._id, r.uuid, s.device_id, s.distribution_id, s.record, s.created_timestamp
269 FROM sender_key s LEFT JOIN recipient r ON s.recipient_id = r._id
270 WHERE uuid IS NOT NULL;
271 DROP TABLE sender_key;
272 ALTER TABLE sender_key2 RENAME TO sender_key;
274 CREATE TABLE sender_key_shared2 (
275 _id INTEGER PRIMARY KEY,
277 device_id INTEGER NOT NULL,
278 distribution_id BLOB NOT NULL,
279 timestamp INTEGER NOT NULL,
280 UNIQUE(uuid, device_id, distribution_id)
282 INSERT INTO sender_key_shared2 (_id, uuid, device_id, distribution_id, timestamp)
283 SELECT s._id, r.uuid, s.device_id, s.distribution_id, s.timestamp
284 FROM sender_key_shared s LEFT JOIN recipient r ON s.recipient_id = r._id
285 WHERE uuid IS NOT NULL;
286 DROP TABLE sender_key_shared;
287 ALTER TABLE sender_key_shared2 RENAME TO sender_key_shared;
289 CREATE TABLE session2 (
290 _id INTEGER PRIMARY KEY,
291 account_id_type INTEGER NOT NULL,
293 device_id INTEGER NOT NULL,
294 record BLOB NOT NULL,
295 UNIQUE(account_id_type, uuid, device_id)
297 INSERT INTO session2 (_id, account_id_type, uuid, device_id, record)
298 SELECT s._id, s.account_id_type, r.uuid, s.device_id, s.record
299 FROM session s LEFT JOIN recipient r ON s.recipient_id = r._id
300 WHERE uuid IS NOT NULL;
302 ALTER TABLE session2 RENAME TO session;
306 if (oldVersion
< 11) {
307 logger
.debug("Updating database: Adding pni field");
308 try (final var statement
= connection
.createStatement()) {
309 statement
.executeUpdate("""
310 ALTER TABLE recipient ADD COLUMN pni BLOB;
314 if (oldVersion
< 12) {
315 logger
.debug("Updating database: Adding username field");
316 try (final var statement
= connection
.createStatement()) {
317 statement
.executeUpdate("""
318 ALTER TABLE recipient ADD COLUMN username TEXT;
322 if (oldVersion
< 13) {
323 logger
.debug("Updating database: Cleanup unknown service ids");
326 DELETE FROM identity AS i
329 try (final var statement
= connection
.prepareStatement(sql
)) {
330 statement
.setBytes(1, ACI
.UNKNOWN
.toByteArray());
331 statement
.executeUpdate();
336 DELETE FROM sender_key_shared AS i
339 try (final var statement
= connection
.prepareStatement(sql
)) {
340 statement
.setBytes(1, ACI
.UNKNOWN
.toByteArray());
341 statement
.executeUpdate();
345 if (oldVersion
< 14) {
346 logger
.debug("Updating database: Creating kyber_pre_key table");
348 try (final var statement
= connection
.createStatement()) {
349 statement
.executeUpdate("""
350 CREATE TABLE kyber_pre_key (
351 _id INTEGER PRIMARY KEY,
352 account_id_type INTEGER NOT NULL,
353 key_id INTEGER NOT NULL,
354 serialized BLOB NOT NULL,
355 is_last_resort INTEGER NOT NULL,
356 UNIQUE(account_id_type, key_id)
362 if (oldVersion
< 15) {
363 logger
.debug("Updating database: Store serviceId as TEXT");
364 try (final var statement
= connection
.createStatement()) {
365 createUuidMappingTable(connection
, statement
);
367 statement
.executeUpdate("""
368 CREATE TABLE identity2 (
369 _id INTEGER PRIMARY KEY,
370 address TEXT UNIQUE NOT NULL,
371 identity_key BLOB NOT NULL,
372 added_timestamp INTEGER NOT NULL,
373 trust_level INTEGER NOT NULL
375 INSERT INTO identity2 (_id, address, identity_key, added_timestamp, trust_level)
376 SELECT i._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = i.uuid) address, i.identity_key, i.added_timestamp, i.trust_level
378 WHERE address IS NOT NULL;
380 ALTER TABLE identity2 RENAME TO identity;
382 CREATE TABLE message_send_log2 (
383 _id INTEGER PRIMARY KEY,
384 content_id INTEGER NOT NULL REFERENCES message_send_log_content (_id) ON DELETE CASCADE,
385 address TEXT NOT NULL,
386 device_id INTEGER NOT NULL
388 INSERT INTO message_send_log2 (_id, content_id, address, device_id)
389 SELECT m._id, m.content_id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = m.uuid) address, m.device_id
390 FROM message_send_log m
391 WHERE address IS NOT NULL;
392 DROP INDEX msl_recipient_index;
393 DROP INDEX msl_content_index;
394 DROP TABLE message_send_log;
395 ALTER TABLE message_send_log2 RENAME TO message_send_log;
396 CREATE INDEX msl_recipient_index ON message_send_log (address, device_id, content_id);
397 CREATE INDEX msl_content_index ON message_send_log (content_id);
399 CREATE TABLE sender_key2 (
400 _id INTEGER PRIMARY KEY,
401 address TEXT NOT NULL,
402 device_id INTEGER NOT NULL,
403 distribution_id BLOB NOT NULL,
404 record BLOB NOT NULL,
405 created_timestamp INTEGER NOT NULL,
406 UNIQUE(address, device_id, distribution_id)
408 INSERT INTO sender_key2 (_id, address, device_id, distribution_id, record, created_timestamp)
409 SELECT s._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = s.uuid) address, s.device_id, s.distribution_id, s.record, s.created_timestamp
411 WHERE address IS NOT NULL;
412 DROP TABLE sender_key;
413 ALTER TABLE sender_key2 RENAME TO sender_key;
415 CREATE TABLE sender_key_shared2 (
416 _id INTEGER PRIMARY KEY,
417 address TEXT NOT NULL,
418 device_id INTEGER NOT NULL,
419 distribution_id BLOB NOT NULL,
420 timestamp INTEGER NOT NULL,
421 UNIQUE(address, device_id, distribution_id)
423 INSERT INTO sender_key_shared2 (_id, address, device_id, distribution_id, timestamp)
424 SELECT s._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = s.uuid) address, s.device_id, s.distribution_id, s.timestamp
425 FROM sender_key_shared s
426 WHERE address IS NOT NULL;
427 DROP TABLE sender_key_shared;
428 ALTER TABLE sender_key_shared2 RENAME TO sender_key_shared;
430 CREATE TABLE session2 (
431 _id INTEGER PRIMARY KEY,
432 account_id_type INTEGER NOT NULL,
433 address TEXT NOT NULL,
434 device_id INTEGER NOT NULL,
435 record BLOB NOT NULL,
436 UNIQUE(account_id_type, address, device_id)
438 INSERT INTO session2 (_id, account_id_type, address, device_id, record)
439 SELECT s._id, s.account_id_type, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = s.uuid) address, s.device_id, s.record
441 WHERE address IS NOT NULL;
443 ALTER TABLE session2 RENAME TO session;
445 DROP TABLE tmp_mapping_table;
449 if (oldVersion
< 16) {
450 logger
.debug("Updating database: Adding stale_timestamp prekey field");
451 try (final var statement
= connection
.createStatement()) {
452 statement
.executeUpdate("""
453 ALTER TABLE pre_key ADD COLUMN stale_timestamp INTEGER;
454 ALTER TABLE kyber_pre_key ADD COLUMN stale_timestamp INTEGER;
455 ALTER TABLE kyber_pre_key ADD COLUMN timestamp INTEGER DEFAULT 0;
459 if (oldVersion
< 17) {
460 logger
.debug("Updating database: Adding key_value table");
461 try (final var statement
= connection
.createStatement()) {
462 statement
.executeUpdate("""
463 CREATE TABLE key_value (
464 _id INTEGER PRIMARY KEY,
465 key TEXT UNIQUE NOT NULL,
471 if (oldVersion
< 18) {
472 logger
.debug("Updating database: Adding cdsi table");
473 try (final var statement
= connection
.createStatement()) {
474 statement
.executeUpdate("""
476 _id INTEGER PRIMARY KEY,
477 number TEXT NOT NULL UNIQUE,
478 last_seen_at INTEGER NOT NULL
483 if (oldVersion
< 19) {
484 logger
.debug("Updating database: Adding contact hidden column");
485 try (final var statement
= connection
.createStatement()) {
486 statement
.executeUpdate("""
487 ALTER TABLE recipient ADD COLUMN hidden INTEGER NOT NULL DEFAULT FALSE;
491 if (oldVersion
< 20) {
492 logger
.debug("Updating database: Creating storage id tables and columns");
493 try (final var statement
= connection
.createStatement()) {
494 statement
.executeUpdate("""
495 CREATE TABLE storage_id (
496 _id INTEGER PRIMARY KEY,
497 type INTEGER NOT NULL,
498 storage_id BLOB UNIQUE NOT NULL
500 ALTER TABLE group_v1 ADD COLUMN storage_id BLOB;
501 ALTER TABLE group_v1 ADD COLUMN storage_record BLOB;
502 ALTER TABLE group_v2 ADD COLUMN storage_id BLOB;
503 ALTER TABLE group_v2 ADD COLUMN storage_record BLOB;
504 ALTER TABLE recipient ADD COLUMN storage_id BLOB;
505 ALTER TABLE recipient ADD COLUMN storage_record BLOB;
509 if (oldVersion
< 21) {
510 logger
.debug("Updating database: Create unregistered column");
511 try (final var statement
= connection
.createStatement()) {
512 statement
.executeUpdate("""
513 ALTER TABLE recipient ADD unregistered_timestamp INTEGER;
517 if (oldVersion
< 22) {
518 logger
.debug("Updating database: Store recipient aci/pni as TEXT");
519 try (final var statement
= connection
.createStatement()) {
520 createUuidMappingTable(connection
, statement
);
522 statement
.executeUpdate("""
523 CREATE TABLE recipient2 (
524 _id INTEGER PRIMARY KEY AUTOINCREMENT,
525 storage_id BLOB UNIQUE,
528 username TEXT UNIQUE,
531 unregistered_timestamp INTEGER,
533 profile_key_credential BLOB,
540 expiration_time INTEGER NOT NULL DEFAULT 0,
541 mute_until INTEGER NOT NULL DEFAULT 0,
542 blocked INTEGER NOT NULL DEFAULT FALSE,
543 archived INTEGER NOT NULL DEFAULT FALSE,
544 profile_sharing INTEGER NOT NULL DEFAULT FALSE,
545 hide_story INTEGER NOT NULL DEFAULT FALSE,
546 hidden INTEGER NOT NULL DEFAULT FALSE,
548 profile_last_update_timestamp INTEGER NOT NULL DEFAULT 0,
549 profile_given_name TEXT,
550 profile_family_name TEXT,
552 profile_about_emoji TEXT,
553 profile_avatar_url_path TEXT,
554 profile_mobile_coin_address BLOB,
555 profile_unidentified_access_mode TEXT,
556 profile_capabilities TEXT
558 INSERT INTO recipient2 (_id, aci, pni, storage_id, storage_record, number, username, unregistered_timestamp, profile_key, profile_key_credential, given_name, family_name, color, expiration_time, blocked, archived, profile_sharing, hidden, profile_last_update_timestamp, profile_given_name, profile_family_name, profile_about, profile_about_emoji, profile_avatar_url_path, profile_mobile_coin_address, profile_unidentified_access_mode, profile_capabilities)
559 SELECT r._id, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = r.uuid AND t.address not like 'PNI:%') aci, (SELECT t.address FROM tmp_mapping_table t WHERE t.uuid = r.pni AND t.address like 'PNI:%' AND (SELECT COUNT(pni) FROM recipient WHERE pni = r.pni) = 1) pni, storage_id, storage_record, number, username, unregistered_timestamp, profile_key, profile_key_credential, given_name, family_name, color, expiration_time, blocked, archived, profile_sharing, hidden, profile_last_update_timestamp, profile_given_name, profile_family_name, profile_about, profile_about_emoji, profile_avatar_url_path, profile_mobile_coin_address, profile_unidentified_access_mode, profile_capabilities
561 DROP TABLE recipient;
562 ALTER TABLE recipient2 RENAME TO recipient;
564 DROP TABLE tmp_mapping_table;
568 if (oldVersion
< 23) {
569 logger
.debug("Updating database: Create group profile sharing column");
570 try (final var statement
= connection
.createStatement()) {
571 statement
.executeUpdate("""
572 ALTER TABLE group_v2 ADD profile_sharing INTEGER NOT NULL DEFAULT TRUE;
576 if (oldVersion
< 24) {
577 logger
.debug("Updating database: Create needs_pni_signature column");
578 try (final var statement
= connection
.createStatement()) {
579 statement
.executeUpdate("""
580 ALTER TABLE recipient ADD needs_pni_signature INTEGER NOT NULL DEFAULT FALSE;
584 if (oldVersion
< 25) {
585 logger
.debug("Updating database: Create nick_name and note columns");
586 try (final var statement
= connection
.createStatement()) {
587 statement
.executeUpdate("""
588 ALTER TABLE recipient ADD nick_name_given_name TEXT;
589 ALTER TABLE recipient ADD nick_name_family_name TEXT;
590 ALTER TABLE recipient ADD note TEXT;
594 if (oldVersion
< 26) {
595 logger
.debug("Updating database: Create discoverabel and profile_phone_number_sharing columns");
596 try (final var statement
= connection
.createStatement()) {
597 statement
.executeUpdate("""
598 ALTER TABLE recipient ADD discoverable INTEGER;
599 ALTER TABLE recipient ADD profile_phone_number_sharing TEXT;
603 if (oldVersion
< 27) {
604 logger
.debug("Updating database: Create expiration_time_version column");
605 try (final var statement
= connection
.createStatement()) {
606 statement
.executeUpdate("""
607 ALTER TABLE recipient ADD expiration_time_version INTEGER DEFAULT 1 NOT NULL;
613 private static void createUuidMappingTable(
614 final Connection connection
, final Statement statement
615 ) throws SQLException
{
616 statement
.executeUpdate("""
617 CREATE TABLE tmp_mapping_table (
619 address TEXT NOT NULL
629 final var uuidAddressMapping
= new HashMap
<UUID
, ServiceId
>();
630 try (final var preparedStatement
= connection
.prepareStatement(sql
)) {
631 try (var result
= Utils
.executeQueryForStream(preparedStatement
, (resultSet
) -> {
632 final var pni
= Optional
.ofNullable(resultSet
.getBytes("pni"))
633 .map(UuidUtil
::parseOrNull
)
634 .map(ServiceId
.PNI
::from
);
635 final var serviceIdUuid
= Optional
.ofNullable(resultSet
.getBytes("uuid")).map(UuidUtil
::parseOrNull
);
636 final var serviceId
= serviceIdUuid
.isPresent() && pni
.isPresent() && serviceIdUuid
.get()
637 .equals(pni
.get().getRawUuid())
638 ? pni
.<ServiceId
>map(p
-> p
)
639 : serviceIdUuid
.<ServiceId
>map(ACI
::from
);
641 return new Pair
<>(serviceId
, pni
);
643 result
.forEach(p
-> {
644 final var serviceId
= p
.first();
645 final var pni
= p
.second();
646 if (serviceId
.isPresent()) {
647 final var rawUuid
= serviceId
.get().getRawUuid();
648 if (!uuidAddressMapping
.containsKey(rawUuid
)) {
649 uuidAddressMapping
.put(rawUuid
, serviceId
.get());
652 if (pni
.isPresent()) {
653 uuidAddressMapping
.put(pni
.get().getRawUuid(), pni
.get());
659 final var insertSql
= """
660 INSERT INTO tmp_mapping_table (uuid, address)
663 try (final var insertStatement
= connection
.prepareStatement(insertSql
)) {
664 for (final var entry
: uuidAddressMapping
.entrySet()) {
665 final var uuid
= entry
.getKey();
666 final var serviceId
= entry
.getValue();
667 insertStatement
.setBytes(1, UuidUtil
.toByteArray(uuid
));
668 insertStatement
.setString(2, serviceId
.toString());
669 insertStatement
.execute();