-- ---------------------------------------------------------------------------
-- Os logins e e-mails com maiuscula, antes de ligar o PostgreSQL (D349).
--
--   docker compose exec -T mysql mysql -uroot -proot <banco> \
--     < scripts/medir-logins-com-maiuscula.sql
--
-- POR QUE ISTO EXISTE. No MySQL o login e o e-mail sao `utf8mb3_general_ci`, e
-- a comparacao ignora a caixa: quem digita "User1" entra como "user1". No
-- PostgreSQL a comparacao e byte a byte, e a entrada procura o login como veio
-- (o adaptador de `config/autoload/doctrine.global.php`). O `setLogin` e o
-- `setEmail` gravam em minusculas, mas so o que passou por eles: o que ja
-- estava gravado com maiuscula -- de antes do setter, de importacao, de SQL a
-- mao -- so a producao diz quanto e (a copia anonimizada reescreveu os logins).
--
-- A CONSULTA DO D349 DA ZERO NO MYSQL, com qualquer numero de maiusculas:
--
--     SELECT COUNT(*) FROM usuarios WHERE login <> LOWER(login) OR email <> LOWER(email)
--
-- Na collation da coluna, 'User1' e 'user1' sao IGUAIS, e o `<>` e falso em
-- toda linha. Medido em 03/10/2026 num banco descartavel montado do dump de
-- producao, com nove usuarios inventados, cinco logins e dois e-mails com
-- maiuscula: a consulta do D349 deu 0, e a desta, byte a byte, deu 5 e 2. Ela
-- continua aqui, na coluna `pela_consulta_do_d349`, para ninguem confiar nela
-- de novo. (Os dois UPDATE do fim, no mesmo banco: 3 logins e 1 e-mail na
-- primeira vez, nada na segunda; o par, o de acento e o e-mail com par
-- ficaram como estavam.)
--
-- Os quatro UPDATE de 06/10/2026 (o renomear e a caixa) tem ensaio no
-- repositorio, `php scripts/ensaiar-sql-do-passo-3.php`: um banco descartavel
-- com as tabelas do `mysql.sql` e trinta e sete usuarios inventados -- o par
-- de um ativo, o de dois inativos, o de dois ativos com um inativo, o de
-- acento, os repetidos exatos (de login e de e-mail, com um inativo, com dois
-- ativos, com dois inativos, com um ativo e dois inativos), o grupo de caixa e
-- exato juntos, os pares de e-mail e o e-mail vazio, ativo e inativo. Eles
-- mudam 12, 4, 6 e 2 linhas na primeira vez e nada na segunda, e o ensaio
-- confere cada usuario depois, as listagens 3b, 4, 4b e 4c -- e que o que
-- ainda se repete sao so os ativos da 4c, com o indice unico do D295 nascendo
-- sem eles. Com o nome novo maior
-- que a coluna, o modo estrito recusa inteiro o
-- primeiro dos quatro, e a 4b o acusa antes, como o nome novo que ja e de
-- outro. Rodado no 8.4.11 e no 8.0.46, com a mesma saida. (Linha de comentario
-- nenhuma deste arquivo comeca pela palavra do comando: o ensaio tira os quatro
-- dos blocos que comecam assim, como o operador os descomenta.)
--
-- NAO IMPRIME LOGIN NEM E-MAIL: so contagens, e os ids dos pares.
--
-- ---------------------------------------------------------------------------
-- OS PARES QUE SO DIFEREM PELA CAIXA sao o problema que nao se resolve aqui.
--
-- No MySQL, "Joao.Silva" e "joao.silva" sao o mesmo login. Baixar a caixa dos
-- dois deixaria duas linhas IGUAIS -- e o adaptador de entrada procura UMA, sem
-- ordem: se achar a inativa primeiro, a ativa nao entra. Em 19/08/2026 a
-- producao tinha 159 logins repetidos em 389 linhas, quase todas de
-- desligados, e um so login com mais de uma pessoa ativa (D31) -- mas aquela
-- conta juntava repetidos exatos e pares de caixa, e nao separava. Esta separa.
--
-- O QUE FAZER COM OS PARES FOI DECIDIDO PELO DONO EM 06/10/2026 (lote de
-- perguntas, pergunta 8, a opcao (b); D31, D295, D349): no passo 3 do corte, o
-- INATIVO de cada par ganha `.inativo<id>` no fim do login -- e do e-mail, no
-- par de e-mails --, e entao a caixa de todos baixa. Ninguem ATIVO muda de
-- login, e o par some. O par com DOIS ativos fica como esta: a listagem 4c da
-- os ids, e cada um e do dono, um a um.
--
-- E O REPETIDO EXATO TAMBEM, DECIDIDO EM 07/10/2026 (pergunta 32 da pagina do
-- lote, a opcao (a), "A mesma regra para eles"; D31, D295): o login -- ou o
-- e-mail -- com a MESMA grafia em duas linhas ou mais tem o inativo renomeado
-- do mesmo jeito. Por isso o renomear e o 4b agrupam por "mais de uma LINHA"
-- (`COUNT(*) > 1`), e nao mais por "mais de uma grafia": o grupo pelo minusculo
-- byte a byte junta os dois tipos. O de dois ATIVOS continua do dono (a 4c), e
-- o indice unico do D295 deixa de esbarrar no inativo. O E-MAIL VAZIO fica de
-- fora do grupo: nao e e-mail repetido, e o renomear o faria virar
-- `.inativo<id>` sozinho (a 4c ja o tirava pelo mesmo motivo).
--
-- "PAR" AQUI E SO A CAIXA, e a chave e `CAST(LOWER(login) AS BINARY)`: o
-- minusculo, comparado byte a byte. O `GROUP BY LOWER(login)` sozinho agrupa
-- pela collation (`utf8mb3_general_ci`), que ignora tambem o acento e o espaco
-- do fim -- e ate a revisao de 06/10/2026 era ele: o inativo de um par
-- "paulo"/"Paulo" com acento ganhava `.inativo<id>`, e a decisao do dono e so
-- sobre a caixa. O acento e o espaco do fim sao a pergunta 9, que espera a
-- medida. No PostgreSQL esses dois nem sao par: o `LOWER` de um nao e o do
-- outro, e a entrada do D349 compara o `LOWER`. A listagem 4c fica pela
-- collation, de proposito: ela mostra quem a entrada do MySQL ve como o mesmo
-- login.
--
-- OS UPDATE ESTAO COMENTADOS NO FIM, no molde de `medir-caixas-abertos.sql`
-- (D28): os dois primeiros renomeiam os inativos dos pares; os dois de baixo
-- baixam a caixa so de quem ficou SEM par e so dos que sao todo ASCII. Os de
-- acento esperam a medida da coluna `login_com_acento_ou_simbolo` (pergunta 9,
-- a opcao (d): decidir depois dela; e a regra da medida e a da pergunta 35, de
-- 07/10/2026, a opcao (b): poucos, nada no codigo e aviso a cada um; muitos, a
-- comparacao sem acento); o `setLogin` usa `strtolower`, que nao
-- baixa "E" com acento, e o `LOWER` do MySQL baixa -- os dois lados divergiriam.
--
-- ---------------------------------------------------------------------------
-- O CONSERTO TEM DUAS METADES, e esta e a do DADO
--
-- A outra e o CODIGO (o D349, a F0 de Usuarios): a identidade em minusculas
-- antes de procurar, e `LOWER` nas buscas por login e e-mail. Uma sem a outra
-- nao resolve no PostgreSQL:
--
--   - so o dado: o "User1" gravado vira "user1", e quem digitava "User1" -- o
--     teclado do celular poe a maiuscula -- deixa de entrar;
--   - so o codigo: quem digita "user1" passa a ser procurado como "user1", e o
--     "User1" gravado nao e achado.
--
-- No MySQL o UPDATE e inofensivo -- a collation ja ignorava a caixa --, e por
-- isso ele roda la, no passo 3 do corte, antes do dump. Ele sobrevive a volta
-- atras, como o resto do passo 3, e la nao faz diferenca.
-- ---------------------------------------------------------------------------

SELECT '=== 1. QUANTOS TEM MAIUSCULA (byte a byte) ===' AS '';

SELECT
    COUNT(*)                                                                        AS usuarios,
    SUM(CAST(login AS BINARY) <> CAST(LOWER(login) AS BINARY))                      AS login_com_maiuscula,
    SUM(CAST(login AS BINARY) <> CAST(LOWER(login) AS BINARY) AND status = 1)       AS dos_ativos,
    SUM(CAST(email AS BINARY) <> CAST(LOWER(email) AS BINARY))                      AS email_com_maiuscula,
    SUM(CAST(email AS BINARY) <> CAST(LOWER(email) AS BINARY) AND status = 1)       AS email_dos_ativos,
    SUM(login <> LOWER(login) OR email <> LOWER(email))                             AS pela_consulta_do_d349
FROM usuarios;

SELECT '=== 2. FORA DO ASCII -- o UPDATE nao os toca; confira a mao ===' AS '';

SELECT
    SUM(LENGTH(login) <> CHAR_LENGTH(login)) AS login_com_acento_ou_simbolo,
    SUM(LENGTH(email) <> CHAR_LENGTH(email)) AS email_com_acento_ou_simbolo
FROM usuarios;

SELECT '=== 3. OS PARES QUE SO DIFEREM PELA CAIXA -- o passo 3 renomeia os inativos (decisao de 06/10/2026) ===' AS '';

-- O grupo e o minusculo byte a byte (o cabecalho diz por que), e a condicao e
-- mais de uma forma distinta byte a byte dentro dele. Um grupo com uma grafia so
-- e repetido EXATO (o D31), e e a listagem 3b: baixar a caixa nao o piora. Com
-- mais de uma, baixar a caixa cria a duplicata.
SELECT 'login' AS coluna,
       COUNT(*)            AS pares,
       SUM(linhas)         AS linhas,
       SUM(ativos > 1)     AS com_mais_de_um_ativo,
       SUM(ativos = 1)     AS com_um_ativo
  FROM (SELECT COUNT(*) AS linhas, SUM(status = 1) AS ativos
          FROM usuarios
         GROUP BY CAST(LOWER(login) AS BINARY)
        HAVING COUNT(DISTINCT CAST(login AS BINARY)) > 1) p
UNION ALL
SELECT 'email',
       COUNT(*), SUM(linhas), SUM(ativos > 1), SUM(ativos = 1)
  FROM (SELECT COUNT(*) AS linhas, SUM(status = 1) AS ativos
          FROM usuarios
         GROUP BY CAST(LOWER(email) AS BINARY)
        HAVING COUNT(DISTINCT CAST(email AS BINARY)) > 1) p;

SELECT '=== 3b. OS REPETIDOS EXATOS -- a mesma grafia em mais de uma linha; o passo 3 renomeia os inativos (decisao de 07/10/2026) ===' AS '';

-- A mesma grafia, byte a byte, em duas linhas ou mais (o D31 os contava junto
-- com os pares). Por estado: `inativos` sao os que o passo 3 renomeia, e
-- `com_mais_de_um_ativo` os grupos que ele nao resolve -- sao do dono, e os ids
-- estao na 4c. O e-mail vazio nao conta.
SELECT 'login' AS coluna,
       COUNT(*)                          AS grupos,
       COALESCE(SUM(linhas), 0)          AS linhas,
       COALESCE(SUM(linhas - ativos), 0) AS inativos,
       COALESCE(SUM(ativos > 1), 0)      AS com_mais_de_um_ativo
  FROM (SELECT COUNT(*) AS linhas, SUM(status = 1) AS ativos
          FROM usuarios
         GROUP BY CAST(login AS BINARY)
        HAVING COUNT(*) > 1) r
UNION ALL
SELECT 'email',
       COUNT(*), COALESCE(SUM(linhas), 0), COALESCE(SUM(linhas - ativos), 0), COALESCE(SUM(ativos > 1), 0)
  FROM (SELECT COUNT(*) AS linhas, SUM(status = 1) AS ativos
          FROM usuarios
         WHERE email <> ''
         GROUP BY CAST(email AS BINARY)
        HAVING COUNT(*) > 1) r;

SELECT '=== 4. OS IDS DE CADA PAR E DE CADA REPETIDO EXATO, para abrir no cadastro (sem login nem e-mail) ===' AS '';

-- `grafias` = 1 e repetido exato; mais que 1, par de caixa (ou os dois juntos).
SELECT 'login' AS coluna,
       GROUP_CONCAT(id ORDER BY id)     AS ids,
       GROUP_CONCAT(status ORDER BY id) AS status_de_cada,
       COUNT(DISTINCT CAST(login AS BINARY)) AS grafias
  FROM usuarios
 GROUP BY CAST(LOWER(login) AS BINARY)
HAVING COUNT(*) > 1
UNION ALL
SELECT 'email',
       GROUP_CONCAT(id ORDER BY id),
       GROUP_CONCAT(status ORDER BY id),
       COUNT(DISTINCT CAST(email AS BINARY))
  FROM usuarios
 WHERE email <> ''
 GROUP BY CAST(LOWER(email) AS BINARY)
HAVING COUNT(*) > 1;

SELECT '=== 4b. O RENOMEAR DOS INATIVOS DE CADA PAR E DE CADA REPETIDO EXATO: quantos, e se cabe -- passariam_do_tamanho e ja_existe em zero sao condicao ===' AS '';

-- O `.inativo<id>` soma 8 caracteres e os digitos do id. `passariam_do_tamanho`
-- conta quem nao caberia na coluna (o tamanho e lido do `information_schema`, e
-- nao escrito aqui): no modo estrito o UPDATE inteiro e recusado -- nada muda --,
-- e fora dele o MySQL corta o fim, e o id se perde. `ja_existe` conta o nome novo
-- que ja e de outra linha: com ele o par nao some, e o renomear deixa de ser
-- idempotente. Os dois tem de dar 0 antes da janela; se nao, o caso e do dono.
-- O grupo e o mesmo dos dois UPDATE do renomear: o minusculo byte a byte com
-- mais de uma LINHA -- o par de caixa e o repetido exato (07/10/2026) --, e o
-- e-mail vazio fora.
SELECT 'login' AS coluna,
       COUNT(*) AS inativos_a_renomear,
       COALESCE(SUM(CHAR_LENGTH(u.login) + 8 + CHAR_LENGTH(u.id) >
           (SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
             WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'usuarios' AND COLUMN_NAME = 'login')), 0)
           AS passariam_do_tamanho,
       COALESCE(SUM(EXISTS (SELECT 1 FROM usuarios o
                             WHERE o.login = CONCAT(u.login, '.inativo', u.id))), 0) AS ja_existe
  FROM usuarios u
  JOIN (SELECT CAST(LOWER(login) AS BINARY) AS chave
          FROM usuarios
         GROUP BY CAST(LOWER(login) AS BINARY)
        HAVING COUNT(*) > 1) par
    ON par.chave = CAST(LOWER(u.login) AS BINARY)
 WHERE u.status <> 1
UNION ALL
SELECT 'email',
       COUNT(*),
       COALESCE(SUM(CHAR_LENGTH(u.email) + 8 + CHAR_LENGTH(u.id) >
           (SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
             WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'usuarios' AND COLUMN_NAME = 'email')), 0),
       COALESCE(SUM(EXISTS (SELECT 1 FROM usuarios o
                             WHERE o.email = CONCAT(u.email, '.inativo', u.id))), 0)
  FROM usuarios u
  JOIN (SELECT CAST(LOWER(email) AS BINARY) AS chave
          FROM usuarios
         WHERE email <> ''
         GROUP BY CAST(LOWER(email) AS BINARY)
        HAVING COUNT(*) > 1) par
    ON par.chave = CAST(LOWER(u.email) AS BINARY)
 WHERE u.status <> 1;

SELECT '=== 4c. MAIS DE UM ATIVO NO MESMO LOGIN OU E-MAIL -- o passo 3 nao os toca; sao do dono, um a um ===' AS '';

-- Pela collation, como a entrada do MySQL os ve: o par de caixa com dois ativos
-- e tambem o repetido EXATO entre ativos (o D31 contou 0 logins assim em
-- 19/08/2026; os e-mails ele nao separou). So os ids. O e-mail vazio nao e par.
SELECT 'login' AS coluna,
       GROUP_CONCAT(id ORDER BY id) AS ids_dos_ativos
  FROM usuarios
 WHERE status = 1
 GROUP BY LOWER(login)
HAVING COUNT(*) > 1
UNION ALL
SELECT 'email',
       GROUP_CONCAT(id ORDER BY id)
  FROM usuarios
 WHERE status = 1 AND email <> ''
 GROUP BY LOWER(email)
HAVING COUNT(*) > 1;

SELECT '=== 5. OS COMANDOS, PARA CONFERIR ANTES DE RODAR ===' AS '';

-- QUATRO UPDATE, NESTA ORDEM, no passo 3 do corte e so junto com o codigo do
-- D349 no ar (ver o cabecalho). DESCOMENTE PARA EXECUTAR, e so depois de olhar
-- as listagens 1 a 4c -- com os dois zeros da 4b.
--
-- OS DOIS PRIMEIROS RENOMEIAM O INATIVO DE CADA PAR (decisao de 06/10/2026,
-- pergunta 8) E DE CADA REPETIDO EXATO (decisao de 07/10/2026, pergunta 32 da
-- pagina do lote): o login, no grupo de logins, e o e-mail, no grupo de
-- e-mails, ganham `.inativo<id>` no fim. O grupo e o minusculo byte a byte com
-- mais de uma linha, e o e-mail vazio fica fora dele. O ativo nao muda, e o
-- grupo com dois ativos so perde os inativos dele -- os ativos ficam para o
-- dono (a 4c). Rodar de novo nao muda nada: o renomeado sai do grupo, e o que
-- sobra nele e ativo.
--
-- UPDATE usuarios u
--   JOIN (SELECT CAST(LOWER(login) AS BINARY) AS chave
--           FROM usuarios
--          GROUP BY CAST(LOWER(login) AS BINARY)
--         HAVING COUNT(*) > 1) par
--     ON par.chave = CAST(LOWER(u.login) AS BINARY)
--    SET u.login = CONCAT(u.login, '.inativo', u.id)
--  WHERE u.status <> 1;
--
-- UPDATE usuarios u
--   JOIN (SELECT CAST(LOWER(email) AS BINARY) AS chave
--           FROM usuarios
--          WHERE email <> ''
--          GROUP BY CAST(LOWER(email) AS BINARY)
--         HAVING COUNT(*) > 1) par
--     ON par.chave = CAST(LOWER(u.email) AS BINARY)
--    SET u.email = CONCAT(u.email, '.inativo', u.id)
--  WHERE u.status <> 1;
--
-- OS DOIS DE BAIXO BAIXAM A CAIXA de quem tem maiuscula, NAO tem par e e todo
-- ASCII -- depois do renomear, o ativo do par desfeito e os renomeados tambem.
-- Rodar de novo nao muda nada: depois da primeira, ninguem que eles alcancam tem
-- maiuscula.
--
-- UPDATE usuarios u
--   JOIN (SELECT CAST(LOWER(login) AS BINARY) AS chave
--           FROM usuarios
--          GROUP BY CAST(LOWER(login) AS BINARY)
--         HAVING COUNT(DISTINCT CAST(login AS BINARY)) = 1) sem_par
--     ON sem_par.chave = CAST(LOWER(u.login) AS BINARY)
--    SET u.login = LOWER(u.login)
--  WHERE CAST(u.login AS BINARY) <> CAST(LOWER(u.login) AS BINARY)
--    AND LENGTH(u.login) = CHAR_LENGTH(u.login);
--
-- UPDATE usuarios u
--   JOIN (SELECT CAST(LOWER(email) AS BINARY) AS chave
--           FROM usuarios
--          GROUP BY CAST(LOWER(email) AS BINARY)
--         HAVING COUNT(DISTINCT CAST(email AS BINARY)) = 1) sem_par
--     ON sem_par.chave = CAST(LOWER(u.email) AS BINARY)
--    SET u.email = LOWER(u.email)
--  WHERE CAST(u.email AS BINARY) <> CAST(LOWER(u.email) AS BINARY)
--    AND LENGTH(u.email) = CHAR_LENGTH(u.email);

SELECT 'Os UPDATE estao comentados no fim deste arquivo. Confira as listagens antes.' AS '';
