116_auth_identity_legacy_external_safety_reports.sql 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. CREATE OR REPLACE FUNCTION public.__migration_116_safe_legacy_metadata_jsonb(input_text TEXT)
  2. RETURNS JSONB
  3. LANGUAGE plpgsql
  4. AS $$
  5. DECLARE
  6. parsed JSONB;
  7. BEGIN
  8. IF input_text IS NULL OR BTRIM(input_text) = '' THEN
  9. RETURN '{}'::jsonb;
  10. END IF;
  11. BEGIN
  12. parsed := input_text::jsonb;
  13. EXCEPTION
  14. WHEN OTHERS THEN
  15. RETURN '{}'::jsonb;
  16. END;
  17. IF jsonb_typeof(parsed) = 'object' THEN
  18. RETURN parsed;
  19. END IF;
  20. RETURN jsonb_build_object('_legacy_metadata_raw_json', parsed);
  21. END;
  22. $$;
  23. CREATE OR REPLACE FUNCTION public.__migration_116_is_valid_legacy_metadata_jsonb(input_text TEXT)
  24. RETURNS BOOLEAN
  25. LANGUAGE plpgsql
  26. AS $$
  27. DECLARE
  28. parsed JSONB;
  29. BEGIN
  30. IF input_text IS NULL OR BTRIM(input_text) = '' THEN
  31. RETURN TRUE;
  32. END IF;
  33. parsed := input_text::jsonb;
  34. RETURN TRUE;
  35. EXCEPTION
  36. WHEN OTHERS THEN
  37. RETURN FALSE;
  38. END;
  39. $$;
  40. DO $$
  41. BEGIN
  42. IF to_regclass('public.user_external_identities') IS NULL THEN
  43. RETURN;
  44. END IF;
  45. EXECUTE $sql$
  46. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  47. SELECT
  48. 'legacy_external_identity_invalid_metadata_json',
  49. 'legacy_external_identity:' || uei.id::text,
  50. jsonb_build_object(
  51. 'legacy_identity_id', uei.id,
  52. 'user_id', uei.user_id,
  53. 'provider', LOWER(BTRIM(COALESCE(uei.provider, ''))),
  54. 'provider_user_id', BTRIM(COALESCE(uei.provider_user_id, '')),
  55. 'provider_union_id', BTRIM(COALESCE(uei.provider_union_id, '')),
  56. 'reason', 'legacy metadata is not valid JSON; migration downgraded metadata to empty object',
  57. 'raw_metadata', LEFT(BTRIM(COALESCE(uei.metadata, '')), 1000),
  58. 'migration', '116_auth_identity_legacy_external_safety_reports'
  59. )
  60. FROM user_external_identities AS uei
  61. JOIN users AS u ON u.id = uei.user_id
  62. WHERE u.deleted_at IS NULL
  63. AND BTRIM(COALESCE(uei.metadata, '')) <> ''
  64. AND NOT public.__migration_116_is_valid_legacy_metadata_jsonb(uei.metadata)
  65. ON CONFLICT (report_type, report_key) DO NOTHING;
  66. $sql$;
  67. EXECUTE $sql$
  68. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  69. SELECT
  70. 'legacy_external_identity_conflict',
  71. 'legacy_external_identity:' || legacy.id::text,
  72. legacy.metadata_json || jsonb_build_object(
  73. 'legacy_identity_id', legacy.id,
  74. 'legacy_user_id', legacy.user_id,
  75. 'provider_type', legacy.provider_type,
  76. 'provider_key', legacy.provider_key,
  77. 'provider_subject', legacy.provider_subject,
  78. 'conflicting_legacy_user_ids', ambiguous.conflicting_legacy_user_ids,
  79. 'reason', 'legacy canonical identity subject belongs to multiple legacy users and cannot be auto-resolved',
  80. 'migration', '116_auth_identity_legacy_external_safety_reports'
  81. )
  82. FROM (
  83. SELECT
  84. uei.id,
  85. uei.user_id,
  86. LOWER(BTRIM(COALESCE(uei.provider, ''))) AS provider_type,
  87. CASE
  88. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN 'wechat-main'
  89. ELSE 'linuxdo'
  90. END AS provider_key,
  91. CASE
  92. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN BTRIM(COALESCE(uei.provider_union_id, ''))
  93. ELSE BTRIM(COALESCE(uei.provider_user_id, ''))
  94. END AS provider_subject,
  95. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json
  96. FROM user_external_identities AS uei
  97. JOIN users AS u ON u.id = uei.user_id
  98. WHERE u.deleted_at IS NULL
  99. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) IN ('linuxdo', 'wechat')
  100. AND (
  101. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'linuxdo' AND BTRIM(COALESCE(uei.provider_user_id, '')) <> '')
  102. OR
  103. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' AND BTRIM(COALESCE(uei.provider_union_id, '')) <> '')
  104. )
  105. ) AS legacy
  106. JOIN (
  107. SELECT
  108. provider_type,
  109. provider_key,
  110. provider_subject,
  111. to_jsonb(array_agg(DISTINCT user_id ORDER BY user_id)) AS conflicting_legacy_user_ids
  112. FROM (
  113. SELECT
  114. uei.user_id,
  115. LOWER(BTRIM(COALESCE(uei.provider, ''))) AS provider_type,
  116. CASE
  117. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN 'wechat-main'
  118. ELSE 'linuxdo'
  119. END AS provider_key,
  120. CASE
  121. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN BTRIM(COALESCE(uei.provider_union_id, ''))
  122. ELSE BTRIM(COALESCE(uei.provider_user_id, ''))
  123. END AS provider_subject
  124. FROM user_external_identities AS uei
  125. JOIN users AS u ON u.id = uei.user_id
  126. WHERE u.deleted_at IS NULL
  127. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) IN ('linuxdo', 'wechat')
  128. AND (
  129. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'linuxdo' AND BTRIM(COALESCE(uei.provider_user_id, '')) <> '')
  130. OR
  131. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' AND BTRIM(COALESCE(uei.provider_union_id, '')) <> '')
  132. )
  133. ) AS legacy_subjects
  134. GROUP BY provider_type, provider_key, provider_subject
  135. HAVING COUNT(DISTINCT user_id) > 1
  136. ) AS ambiguous
  137. ON ambiguous.provider_type = legacy.provider_type
  138. AND ambiguous.provider_key = legacy.provider_key
  139. AND ambiguous.provider_subject = legacy.provider_subject
  140. ON CONFLICT (report_type, report_key) DO NOTHING;
  141. $sql$;
  142. EXECUTE $sql$
  143. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  144. SELECT
  145. 'legacy_external_identity_conflict',
  146. 'legacy_external_identity:' || legacy.id::text,
  147. legacy.metadata_json || jsonb_build_object(
  148. 'legacy_identity_id', legacy.id,
  149. 'legacy_user_id', legacy.user_id,
  150. 'existing_identity_id', ai.id,
  151. 'existing_user_id', ai.user_id,
  152. 'provider_type', legacy.provider_type,
  153. 'provider_key', legacy.provider_key,
  154. 'provider_subject', legacy.provider_subject,
  155. 'reason', 'legacy canonical identity subject already belongs to another user',
  156. 'migration', '116_auth_identity_legacy_external_safety_reports'
  157. )
  158. FROM (
  159. SELECT
  160. uei.id,
  161. uei.user_id,
  162. LOWER(BTRIM(COALESCE(uei.provider, ''))) AS provider_type,
  163. CASE
  164. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN 'wechat-main'
  165. ELSE 'linuxdo'
  166. END AS provider_key,
  167. CASE
  168. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN BTRIM(COALESCE(uei.provider_union_id, ''))
  169. ELSE BTRIM(COALESCE(uei.provider_user_id, ''))
  170. END AS provider_subject,
  171. BTRIM(COALESCE(uei.provider_user_id, '')) AS provider_user_id,
  172. BTRIM(COALESCE(uei.provider_union_id, '')) AS provider_union_id,
  173. BTRIM(COALESCE(uei.provider_username, '')) AS provider_username,
  174. BTRIM(COALESCE(uei.display_name, '')) AS display_name,
  175. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json
  176. FROM user_external_identities AS uei
  177. JOIN users AS u ON u.id = uei.user_id
  178. WHERE u.deleted_at IS NULL
  179. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) IN ('linuxdo', 'wechat')
  180. AND (
  181. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'linuxdo' AND BTRIM(COALESCE(uei.provider_user_id, '')) <> '')
  182. OR
  183. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' AND BTRIM(COALESCE(uei.provider_union_id, '')) <> '')
  184. )
  185. ) AS legacy
  186. JOIN (
  187. SELECT
  188. provider_type,
  189. provider_key,
  190. provider_subject
  191. FROM (
  192. SELECT
  193. uei.user_id,
  194. LOWER(BTRIM(COALESCE(uei.provider, ''))) AS provider_type,
  195. CASE
  196. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN 'wechat-main'
  197. ELSE 'linuxdo'
  198. END AS provider_key,
  199. CASE
  200. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN BTRIM(COALESCE(uei.provider_union_id, ''))
  201. ELSE BTRIM(COALESCE(uei.provider_user_id, ''))
  202. END AS provider_subject
  203. FROM user_external_identities AS uei
  204. JOIN users AS u ON u.id = uei.user_id
  205. WHERE u.deleted_at IS NULL
  206. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) IN ('linuxdo', 'wechat')
  207. AND (
  208. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'linuxdo' AND BTRIM(COALESCE(uei.provider_user_id, '')) <> '')
  209. OR
  210. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' AND BTRIM(COALESCE(uei.provider_union_id, '')) <> '')
  211. )
  212. ) AS legacy_subjects
  213. GROUP BY provider_type, provider_key, provider_subject
  214. HAVING COUNT(DISTINCT user_id) = 1
  215. ) AS clear_subjects
  216. ON clear_subjects.provider_type = legacy.provider_type
  217. AND clear_subjects.provider_key = legacy.provider_key
  218. AND clear_subjects.provider_subject = legacy.provider_subject
  219. JOIN auth_identities AS ai
  220. ON ai.provider_type = legacy.provider_type
  221. AND ai.provider_key = legacy.provider_key
  222. AND ai.provider_subject = legacy.provider_subject
  223. WHERE ai.user_id <> legacy.user_id
  224. ON CONFLICT (report_type, report_key) DO NOTHING;
  225. $sql$;
  226. EXECUTE $sql$
  227. WITH legacy AS (
  228. SELECT
  229. uei.id,
  230. uei.user_id,
  231. LOWER(BTRIM(COALESCE(uei.provider, ''))) AS provider_type,
  232. CASE
  233. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN 'wechat-main'
  234. ELSE 'linuxdo'
  235. END AS provider_key,
  236. CASE
  237. WHEN LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' THEN BTRIM(COALESCE(uei.provider_union_id, ''))
  238. ELSE BTRIM(COALESCE(uei.provider_user_id, ''))
  239. END AS provider_subject,
  240. BTRIM(COALESCE(uei.provider_user_id, '')) AS provider_user_id,
  241. BTRIM(COALESCE(uei.provider_union_id, '')) AS provider_union_id,
  242. BTRIM(COALESCE(uei.provider_username, '')) AS provider_username,
  243. BTRIM(COALESCE(uei.display_name, '')) AS display_name,
  244. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json,
  245. COALESCE(uei.updated_at, uei.created_at, NOW()) AS verified_at
  246. FROM user_external_identities AS uei
  247. JOIN users AS u ON u.id = uei.user_id
  248. WHERE u.deleted_at IS NULL
  249. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) IN ('linuxdo', 'wechat')
  250. AND (
  251. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'linuxdo' AND BTRIM(COALESCE(uei.provider_user_id, '')) <> '')
  252. OR
  253. (LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat' AND BTRIM(COALESCE(uei.provider_union_id, '')) <> '')
  254. )
  255. ),
  256. clear_subjects AS (
  257. SELECT
  258. provider_type,
  259. provider_key,
  260. provider_subject
  261. FROM legacy
  262. GROUP BY provider_type, provider_key, provider_subject
  263. HAVING COUNT(DISTINCT user_id) = 1
  264. ),
  265. canonical_legacy AS (
  266. SELECT
  267. legacy.*,
  268. ROW_NUMBER() OVER (
  269. PARTITION BY legacy.provider_type, legacy.provider_key, legacy.provider_subject
  270. ORDER BY legacy.verified_at DESC, legacy.id DESC
  271. ) AS canonical_row_num
  272. FROM legacy
  273. JOIN clear_subjects
  274. ON clear_subjects.provider_type = legacy.provider_type
  275. AND clear_subjects.provider_key = legacy.provider_key
  276. AND clear_subjects.provider_subject = legacy.provider_subject
  277. )
  278. INSERT INTO auth_identities (
  279. user_id,
  280. provider_type,
  281. provider_key,
  282. provider_subject,
  283. verified_at,
  284. metadata
  285. )
  286. SELECT
  287. legacy.user_id,
  288. legacy.provider_type,
  289. legacy.provider_key,
  290. legacy.provider_subject,
  291. legacy.verified_at,
  292. legacy.metadata_json || jsonb_build_object(
  293. 'legacy_identity_id', legacy.id,
  294. 'provider_user_id', legacy.provider_user_id,
  295. 'provider_union_id', NULLIF(legacy.provider_union_id, ''),
  296. 'provider_username', legacy.provider_username,
  297. 'display_name', legacy.display_name,
  298. 'migration', '116_auth_identity_legacy_external_safety_reports'
  299. )
  300. FROM canonical_legacy AS legacy
  301. LEFT JOIN auth_identities AS ai
  302. ON ai.provider_type = legacy.provider_type
  303. AND ai.provider_key = legacy.provider_key
  304. AND ai.provider_subject = legacy.provider_subject
  305. WHERE legacy.canonical_row_num = 1
  306. AND ai.id IS NULL
  307. ON CONFLICT (provider_type, provider_key, provider_subject) DO NOTHING;
  308. $sql$;
  309. EXECUTE $sql$
  310. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  311. SELECT
  312. 'legacy_external_channel_conflict',
  313. 'legacy_external_identity:' || legacy.id::text,
  314. legacy.metadata_json || jsonb_build_object(
  315. 'legacy_identity_id', legacy.id,
  316. 'legacy_user_id', legacy.user_id,
  317. 'existing_channel_id', channel.id,
  318. 'existing_identity_id', existing_ai.id,
  319. 'existing_user_id', existing_ai.user_id,
  320. 'provider_type', 'wechat',
  321. 'provider_key', 'wechat-main',
  322. 'provider_subject', legacy.provider_union_id,
  323. 'channel', legacy.channel,
  324. 'channel_app_id', legacy.channel_app_id,
  325. 'channel_subject', legacy.provider_user_id,
  326. 'reason', 'legacy channel subject already belongs to another user',
  327. 'migration', '116_auth_identity_legacy_external_safety_reports'
  328. )
  329. FROM (
  330. SELECT
  331. uei.id,
  332. uei.user_id,
  333. BTRIM(COALESCE(uei.provider_user_id, '')) AS provider_user_id,
  334. BTRIM(COALESCE(uei.provider_union_id, '')) AS provider_union_id,
  335. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json,
  336. BTRIM(COALESCE(public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'channel', '')) AS channel,
  337. BTRIM(COALESCE(
  338. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'channel_app_id',
  339. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'appid',
  340. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'app_id',
  341. ''
  342. )) AS channel_app_id
  343. FROM user_external_identities AS uei
  344. JOIN users AS u ON u.id = uei.user_id
  345. WHERE u.deleted_at IS NULL
  346. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  347. AND BTRIM(COALESCE(uei.provider_union_id, '')) <> ''
  348. AND BTRIM(COALESCE(uei.provider_user_id, '')) <> ''
  349. ) AS legacy
  350. JOIN (
  351. SELECT
  352. BTRIM(COALESCE(uei.provider_union_id, '')) AS provider_subject
  353. FROM user_external_identities AS uei
  354. JOIN users AS u ON u.id = uei.user_id
  355. WHERE u.deleted_at IS NULL
  356. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  357. AND BTRIM(COALESCE(uei.provider_union_id, '')) <> ''
  358. AND BTRIM(COALESCE(uei.provider_user_id, '')) <> ''
  359. GROUP BY BTRIM(COALESCE(uei.provider_union_id, ''))
  360. HAVING COUNT(DISTINCT uei.user_id) = 1
  361. ) AS clear_subjects
  362. ON clear_subjects.provider_subject = legacy.provider_union_id
  363. JOIN auth_identities AS legacy_ai
  364. ON legacy_ai.user_id = legacy.user_id
  365. AND legacy_ai.provider_type = 'wechat'
  366. AND legacy_ai.provider_key = 'wechat-main'
  367. AND legacy_ai.provider_subject = legacy.provider_union_id
  368. JOIN auth_identity_channels AS channel
  369. ON channel.provider_type = 'wechat'
  370. AND channel.provider_key = 'wechat-main'
  371. AND channel.channel = legacy.channel
  372. AND channel.channel_app_id = legacy.channel_app_id
  373. AND channel.channel_subject = legacy.provider_user_id
  374. JOIN auth_identities AS existing_ai
  375. ON existing_ai.id = channel.identity_id
  376. WHERE legacy.channel <> ''
  377. AND legacy.channel_app_id <> ''
  378. AND existing_ai.user_id <> legacy.user_id
  379. ON CONFLICT (report_type, report_key) DO NOTHING;
  380. $sql$;
  381. EXECUTE $sql$
  382. WITH legacy AS (
  383. SELECT
  384. uei.user_id,
  385. BTRIM(COALESCE(uei.provider_user_id, '')) AS provider_user_id,
  386. BTRIM(COALESCE(uei.provider_union_id, '')) AS provider_union_id,
  387. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json,
  388. BTRIM(COALESCE(public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'channel', '')) AS channel,
  389. BTRIM(COALESCE(
  390. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'channel_app_id',
  391. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'appid',
  392. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) ->> 'app_id',
  393. ''
  394. )) AS channel_app_id
  395. FROM user_external_identities AS uei
  396. JOIN users AS u ON u.id = uei.user_id
  397. WHERE u.deleted_at IS NULL
  398. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  399. AND BTRIM(COALESCE(uei.provider_union_id, '')) <> ''
  400. AND BTRIM(COALESCE(uei.provider_user_id, '')) <> ''
  401. ),
  402. clear_subjects AS (
  403. SELECT
  404. provider_union_id AS provider_subject
  405. FROM legacy
  406. GROUP BY provider_union_id
  407. HAVING COUNT(DISTINCT user_id) = 1
  408. )
  409. INSERT INTO auth_identity_channels (
  410. identity_id,
  411. provider_type,
  412. provider_key,
  413. channel,
  414. channel_app_id,
  415. channel_subject,
  416. metadata
  417. )
  418. SELECT
  419. legacy_ai.id,
  420. 'wechat',
  421. 'wechat-main',
  422. legacy.channel,
  423. legacy.channel_app_id,
  424. legacy.provider_user_id,
  425. legacy.metadata_json || jsonb_build_object(
  426. 'openid', legacy.provider_user_id,
  427. 'unionid', legacy.provider_union_id,
  428. 'migration', '116_auth_identity_legacy_external_safety_reports'
  429. )
  430. FROM legacy
  431. JOIN clear_subjects
  432. ON clear_subjects.provider_subject = legacy.provider_union_id
  433. JOIN auth_identities AS legacy_ai
  434. ON legacy_ai.user_id = legacy.user_id
  435. AND legacy_ai.provider_type = 'wechat'
  436. AND legacy_ai.provider_key = 'wechat-main'
  437. AND legacy_ai.provider_subject = legacy.provider_union_id
  438. LEFT JOIN auth_identity_channels AS channel
  439. ON channel.provider_type = 'wechat'
  440. AND channel.provider_key = 'wechat-main'
  441. AND channel.channel = legacy.channel
  442. AND channel.channel_app_id = legacy.channel_app_id
  443. AND channel.channel_subject = legacy.provider_user_id
  444. WHERE legacy.channel <> ''
  445. AND legacy.channel_app_id <> ''
  446. AND channel.id IS NULL
  447. ON CONFLICT DO NOTHING;
  448. $sql$;
  449. EXECUTE $sql$
  450. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  451. SELECT
  452. 'wechat_openid_only_requires_remediation',
  453. 'legacy_external_identity:' || legacy.id::text,
  454. legacy.metadata_json || jsonb_build_object(
  455. 'legacy_identity_id', legacy.id,
  456. 'user_id', legacy.user_id,
  457. 'openid', legacy.provider_user_id,
  458. 'reason', 'legacy user_external_identities row only has openid and cannot be canonicalized offline',
  459. 'migration', '116_auth_identity_legacy_external_safety_reports'
  460. )
  461. FROM (
  462. SELECT
  463. uei.id,
  464. uei.user_id,
  465. BTRIM(COALESCE(uei.provider_user_id, '')) AS provider_user_id,
  466. public.__migration_116_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json
  467. FROM user_external_identities AS uei
  468. JOIN users AS u ON u.id = uei.user_id
  469. WHERE u.deleted_at IS NULL
  470. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  471. AND BTRIM(COALESCE(uei.provider_user_id, '')) <> ''
  472. AND BTRIM(COALESCE(uei.provider_union_id, '')) = ''
  473. ) AS legacy
  474. ON CONFLICT (report_type, report_key) DO NOTHING;
  475. $sql$;
  476. END $$;
  477. DO $$
  478. BEGIN
  479. IF NOT EXISTS (
  480. SELECT 1
  481. FROM pg_constraint
  482. WHERE conname = 'auth_identities_metadata_is_object_check'
  483. ) THEN
  484. ALTER TABLE auth_identities
  485. ADD CONSTRAINT auth_identities_metadata_is_object_check
  486. CHECK (jsonb_typeof(metadata) = 'object');
  487. END IF;
  488. IF NOT EXISTS (
  489. SELECT 1
  490. FROM pg_constraint
  491. WHERE conname = 'auth_identity_channels_metadata_is_object_check'
  492. ) THEN
  493. ALTER TABLE auth_identity_channels
  494. ADD CONSTRAINT auth_identity_channels_metadata_is_object_check
  495. CHECK (jsonb_typeof(metadata) = 'object');
  496. END IF;
  497. IF NOT EXISTS (
  498. SELECT 1
  499. FROM pg_constraint
  500. WHERE conname = 'auth_identity_migration_reports_details_is_object_check'
  501. ) THEN
  502. ALTER TABLE auth_identity_migration_reports
  503. ADD CONSTRAINT auth_identity_migration_reports_details_is_object_check
  504. CHECK (jsonb_typeof(details) = 'object');
  505. END IF;
  506. END $$;
  507. DROP FUNCTION IF EXISTS public.__migration_116_is_valid_legacy_metadata_jsonb(TEXT);
  508. DROP FUNCTION IF EXISTS public.__migration_116_safe_legacy_metadata_jsonb(TEXT);