Ao executar um arquivo \copy
(pgadmin ou aws_s3.table_import_from_s3
) de 1,6 GB em um banco de dados compatível com o AWS Aurora Postgres, recebo o seguinte erro:
ERROR: invalid byte sequence for encoding "UTF8": 0xdc 0x36
CONTEXT: COPY staging, line 99779: "L24000403170365 ACTIVEZONE LLC ..."
EDIT: Aqui está o que eu consegui extrair para a definição da tabela (mas me avise se quiser mais):
nome_da_coluna | tipo_de_dados | comprimento_máximo_de_caractere | é_anulável | coluna_padrão |
---|---|---|---|---|
cru | texto | [nulo] | SIM | [nulo] |
EDIT: Também tentei alterar a coluna, mas bytea
não obtive efeito.
A fonte deveria ser ASCII, mas recebo o mesmo erro com codificações explícitas como utf8
, latin1
, win1251
, e win1252
. EDIT: Conforme solicitado em uma resposta, aqui estão mais informações sobre os comandos de importação. No pgadmin4, estou importando com o botão direito do mouse na tabela que mostra o seguinte sob as capas:
--command " "\\copy public.staging (\"raw\") FROM 'C:/data.txt' DELIMITER '|' ENCODING 'UTF8';""
Eu também uso o pgadmin4 para acionar a importação da tabela s3 chamando a consulta:
SELECT aws_s3.table_import_from_s3(
'staging',
'',
'(DELIMITER ''|'')',
aws_commons.create_s3_uri('data', 'data.txt', 'us-east-1')
);
Debaixo das cobertas, table_import_from_s3
chama o comando:
copy staging from '/rdsdbdata/extensions/aws_s3/{{internal filename}}' with (DELIMITER '|')
A resposta para perguntas semelhantes é limpar os dados de origem, então eu abri o python e tentei encontrar o caractere ofensivo. Não consegui encontrar nenhuma evidência de um caractere incomum na linha referenciada ou ao redor dela. Para fins de argumentação, acredito que o seguinte escaneará o arquivo inteiro (e você pode ver os resultados em linha):
>>> def charinfile(filename, bytechar):
... with open(filename, 'rb') as file:
... byteline = file.readline()
... while byteline: # readline returns empty string at EOF
... if byteline.find(bytechar) != -1:
... print("found!")
... return byteline
... byteline = file.readline()
... else:
... print("not found")
...
>>> charinfile(filename, b'\xdc')
not found
>>> charinfile(filename, b'\xdc36')
not found
>>> charinfile(filename, b'6') # make sure the code is working
found!
Também tentei versões em que uso strings em vez de bytes com os mesmos resultados. Posso confirmar que não há linhas em branco antes do EOF (usei contadores de linha para verificar se cheguei a ~1m de linhas).
O que estou perdendo?
O código sugerido por @laurenz-albe funcionou, mas também funcionou com
UFT8
então oENCODING
não era realmente o problema. Após alguns testes, mudar deFORMAT 'text'
paraFORMAT 'csv'
foi a mudança crítica. Continuei a usar os pontos de código recomendados (eles parecem uma prática recomendada para evitar colisão de caracteres), resultando em uma consulta como:... ou em
psql
,Ao usar o
TEXT
formato de cópia, o caractere de barra invertida tem um significado especial . A linha no seu arquivo que produz a falha contém uma sequência como esta:\334
é interpretado como um número octal devido à seguinte regra:334
em octal =0xdc
em hexadecimal, e6
em ASCII =0x36
em hexadecimal, e isso faz com0xdc 0x36
que o decodificador UTF8 reclame.A solução de princípio para esse problema é citar todas as barras invertidas no arquivo de entrada, duplicando-as . Como alternativa, você pode usar CSV com aspas e delimitadores que não existem no arquivo, como você fez, mas isso é perigoso se os dados não estiverem limpos.
Seus dados devem ter alguma codificação diferente. Conecte-se ao banco de dados com
psql
e tenteIsso pressupõe que os caracteres ASCII com os pontos de código 4 e 7 não ocorrem em nenhum lugar do seu arquivo.