add export pipeline: BigQuery → GCS → Hetzner S3 (roda.sh)

This commit is contained in:
2026-03-25 10:13:34 +01:00
parent 335abbfa2f
commit dd221cff88
3 changed files with 1068 additions and 0 deletions

535
all_tables.txt Normal file
View File

@@ -0,0 +1,535 @@
br_anatel_banda_larga_fixa.densidade_brasil
br_anatel_banda_larga_fixa.densidade_municipio
br_anatel_banda_larga_fixa.densidade_uf
br_anatel_banda_larga_fixa.microdados
br_anatel_indice_brasileiro_conectividade.municipio
br_anp_precos_combustiveis.microdados
br_ans_beneficiario.informacao_consolidada
br_bcb_estban.agencia
br_bcb_estban.dicionario
br_bcb_estban.municipio
br_bcb_sicor.dicionario
br_bcb_sicor.empreendimento
br_bcb_sicor.liberacao
br_bcb_sicor.operacao
br_bcb_sicor.operacoes_desclassificadas
br_bcb_sicor.recurso_publico_complemento_operacao
br_bcb_sicor.recurso_publico_cooperado
br_bcb_sicor.recurso_publico_gleba
br_bcb_sicor.recurso_publico_mutuario
br_bcb_sicor.recurso_publico_propriedade
br_bcb_sicor.saldo
br_bcb_taxa_cambio.taxa_cambio
br_bcb_taxa_selic.taxa_selic
br_bd_diretorios_brasil.area_conhecimento
br_bd_diretorios_brasil.cbo_1994
br_bd_diretorios_brasil.cbo_2002
br_bd_diretorios_brasil.cep
br_bd_diretorios_brasil.cid_10
br_bd_diretorios_brasil.cid_9
br_bd_diretorios_brasil.cnae_1
br_bd_diretorios_brasil.cnae_2
br_bd_diretorios_brasil.curso_superior
br_bd_diretorios_brasil.distrito_1991
br_bd_diretorios_brasil.distrito_2000
br_bd_diretorios_brasil.distrito_2010
br_bd_diretorios_brasil.empresa
br_bd_diretorios_brasil.escola
br_bd_diretorios_brasil.etnia_indigena
br_bd_diretorios_brasil.instituicao_ensino_superior
br_bd_diretorios_brasil.municipio
br_bd_diretorios_brasil.natureza_juridica
br_bd_diretorios_brasil.regiao
br_bd_diretorios_brasil.setor_censitario_2010
br_bd_diretorios_brasil.setor_censitario_2022
br_bd_diretorios_brasil.subatividade_ibge
br_bd_diretorios_brasil.uf
br_bd_diretorios_mundo.continente
br_bd_diretorios_mundo.nomenclatura_comum_mercosul
br_bd_diretorios_mundo.pais
br_bd_diretorios_mundo.sistema_harmonizado
br_bd_metadados.bigquery_tables
br_bd_metadados.prefect_flow_runs
br_camara_dados_abertos.deputado
br_camara_dados_abertos.deputado_ocupacao
br_camara_dados_abertos.deputado_profissao
br_camara_dados_abertos.despesa
br_camara_dados_abertos.evento
br_camara_dados_abertos.evento_orgao
br_camara_dados_abertos.evento_presenca_deputado
br_camara_dados_abertos.evento_requerimento
br_camara_dados_abertos.frente
br_camara_dados_abertos.frente_deputado
br_camara_dados_abertos.funcionario
br_camara_dados_abertos.legislatura
br_camara_dados_abertos.legislatura_mesa
br_camara_dados_abertos.licitacao
br_camara_dados_abertos.licitacao_contrato
br_camara_dados_abertos.licitacao_item
br_camara_dados_abertos.licitacao_pedido
br_camara_dados_abertos.licitacao_proposta
br_camara_dados_abertos.orgao
br_camara_dados_abertos.orgao_deputado
br_camara_dados_abertos.proposicao_autor
br_camara_dados_abertos.proposicao_microdados
br_camara_dados_abertos.proposicao_tema
br_camara_dados_abertos.votacao
br_camara_dados_abertos.votacao_objeto
br_camara_dados_abertos.votacao_orientacao_bancada
br_camara_dados_abertos.votacao_parlamentar
br_camara_dados_abertos.votacao_proposicao
br_ce_fortaleza_sefin_iptu.face_quadra
br_cgu_beneficios_cidadao.auxilio_brasil
br_cgu_beneficios_cidadao.auxilio_emergencial
br_cgu_beneficios_cidadao.bolsa_familia_pagamento
br_cgu_beneficios_cidadao.bpc
br_cgu_beneficios_cidadao.garantia_safra
br_cgu_beneficios_cidadao.novo_bolsa_familia
br_cgu_cartao_pagamento.dicionario
br_cgu_cartao_pagamento.microdados_compras_centralizadas
br_cgu_cartao_pagamento.microdados_defesa_civil
br_cgu_cartao_pagamento.microdados_governo_federal
br_cgu_dados_abertos.conjunto
br_cgu_dados_abertos.organizacao
br_cgu_dados_abertos.recurso
br_cgu_emendas_parlamentares.microdados
br_cgu_licitacao_contrato.contrato_apostilamento
br_cgu_licitacao_contrato.contrato_compra
br_cgu_licitacao_contrato.contrato_item
br_cgu_licitacao_contrato.contrato_termo_aditivo
br_cgu_licitacao_contrato.licitacao
br_cgu_licitacao_contrato.licitacao_empenho
br_cgu_licitacao_contrato.licitacao_item
br_cgu_licitacao_contrato.licitacao_participante
br_cgu_orcamento_publico.orcamento
br_cgu_receitas_publicas.receitas
br_cgu_servidores_executivo_federal.afastamentos
br_cgu_servidores_executivo_federal.cadastro_aposentados
br_cgu_servidores_executivo_federal.cadastro_pensionistas
br_cgu_servidores_executivo_federal.cadastro_reserva_reforma_militares
br_cgu_servidores_executivo_federal.cadastro_servidores
br_cgu_servidores_executivo_federal.observacoes
br_cgu_servidores_executivo_federal.remuneracao
br_cnj_improbidade_administrativa.condenacao
br_cnpq_bolsas.dicionario
br_cnpq_bolsas.microdados
br_cvm_administradores_carteira.pessoa_fisica
br_cvm_administradores_carteira.pessoa_juridica
br_cvm_administradores_carteira.responsavel
br_cvm_oferta_publica_distribuicao.dia
br_datahackers_state_data.microdados
br_fbsp_absp.uf
br_fbsp_absp.violencia_escola
br_fgv_igp.igp_10_mes
br_fgv_igp.igp_di_ano
br_fgv_igp.igp_di_mes
br_fgv_igp.igp_m_ano
br_fgv_igp.igp_m_mes
br_fgv_igp.igp_og_ano
br_fgv_igp.igp_og_mes
br_geobr_mapas.amazonia_legal
br_geobr_mapas.area_minima_comparavel_2010
br_geobr_mapas.area_risco_desastre
br_geobr_mapas.arranjo_populacional
br_geobr_mapas.bioma
br_geobr_mapas.concentracao_urbana
br_geobr_mapas.escola
br_geobr_mapas.estabelecimentos_saude
br_geobr_mapas.limite_vizinhanca
br_geobr_mapas.mesorregiao
br_geobr_mapas.microrregiao
br_geobr_mapas.municipio
br_geobr_mapas.pais
br_geobr_mapas.pegada_urbana
br_geobr_mapas.regiao
br_geobr_mapas.regiao_imediata
br_geobr_mapas.regiao_intermediaria
br_geobr_mapas.regiao_metropolitana_2017
br_geobr_mapas.saude
br_geobr_mapas.sede_municipal
br_geobr_mapas.semiarido
br_geobr_mapas.setor_censitario_2010
br_geobr_mapas.terra_indigena
br_geobr_mapas.uf
br_geobr_mapas.unidade_conservacao
br_ibge_censo_2022.alfabetizacao_grupo_idade_sexo_raca
br_ibge_censo_2022.cadastro_enderecos
br_ibge_censo_2022.caracteristica_domicilio_grupo_idade_raca_destino_lixo
br_ibge_censo_2022.caracteristica_domicilio_grupo_idade_raca_esgotamento_sanitario
br_ibge_censo_2022.caracteristica_domicilio_grupo_idade_raca_ligacao_abastecimento_agua
br_ibge_censo_2022.caracteristica_domicilio_grupo_idade_raca_tipo_domicilio
br_ibge_censo_2022.dicionario
br_ibge_censo_2022.domicilio_recenseado
br_ibge_censo_2022.indice_envelhecimento_raca
br_ibge_censo_2022.municipio
br_ibge_censo_2022.populacao_grupo_idade_sexo_raca
br_ibge_censo_2022.populacao_grupo_idade_uf
br_ibge_censo_2022.populacao_idade_sexo
br_ibge_censo_2022.setor_censitario
br_ibge_censo_2022.terra_indigena
br_ibge_censo_2022.territorio_quilombola
br_ibge_censo_demografico.dicionario
br_ibge_censo_demografico.microdados_domicilio_1970
br_ibge_censo_demografico.microdados_domicilio_1980
br_ibge_censo_demografico.microdados_domicilio_1991
br_ibge_censo_demografico.microdados_domicilio_2000
br_ibge_censo_demografico.microdados_domicilio_2010
br_ibge_censo_demografico.microdados_pessoa_1970
br_ibge_censo_demografico.microdados_pessoa_1980
br_ibge_censo_demografico.microdados_pessoa_1991
br_ibge_censo_demografico.microdados_pessoa_2000
br_ibge_censo_demografico.microdados_pessoa_2010
br_ibge_censo_demografico.setor_censitario_alfabetizacao_homens_mulheres_2010
br_ibge_censo_demografico.setor_censitario_alfabetizacao_total_2010
br_ibge_censo_demografico.setor_censitario_basico_2010
br_ibge_censo_demografico.setor_censitario_domicilio_caracteristicas_gerais_2010
br_ibge_censo_demografico.setor_censitario_domicilio_moradores_2010
br_ibge_censo_demografico.setor_censitario_domicilio_renda_2010
br_ibge_censo_demografico.setor_censitario_entorno_2010
br_ibge_censo_demografico.setor_censitario_idade_homens_2010
br_ibge_censo_demografico.setor_censitario_idade_mulheres_2010
br_ibge_censo_demografico.setor_censitario_idade_total_2010
br_ibge_censo_demografico.setor_censitario_pessoa_renda_2010
br_ibge_censo_demografico.setor_censitario_raca_alfabetizacao_idade_genero_2010
br_ibge_censo_demografico.setor_censitario_raca_idade_0_4_genero_2010
br_ibge_censo_demografico.setor_censitario_raca_idade_genero_2010
br_ibge_censo_demografico.setor_censitario_registro_civil_2010
br_ibge_censo_demografico.setor_censitario_relacao_parentesco_conjuges_2010
br_ibge_censo_demografico.setor_censitario_relacao_parentesco_filhos_2010
br_ibge_censo_demografico.setor_censitario_relacao_parentesco_filhos_enteados_2010
br_ibge_censo_demografico.setor_censitario_relacao_parentesco_outros_2010
br_ibge_censo_demografico.setor_censitario_responsavel_domicilios_homens_total_2010
br_ibge_censo_demografico.setor_censitario_responsavel_domicilios_mulheres_2010
br_ibge_censo_demografico.setor_censitario_responsavel_renda_2010
br_ibge_estadic.dicionario
br_ibge_inpc.mes_brasil
br_ibge_inpc.mes_categoria_brasil
br_ibge_inpc.mes_categoria_municipio
br_ibge_inpc.mes_categoria_rm
br_ibge_ipca.mes_brasil
br_ibge_ipca.mes_categoria_brasil
br_ibge_ipca.mes_categoria_municipio
br_ibge_ipca.mes_categoria_rm
br_ibge_ipca15.mes_brasil
br_ibge_ipca15.mes_categoria_brasil
br_ibge_ipca15.mes_categoria_municipio
br_ibge_ipca15.mes_categoria_rm
br_ibge_pam.lavoura_permanente
br_ibge_pam.lavoura_temporaria
br_ibge_pevs.producao_extracao_vegetal
br_ibge_pevs.producao_silvicultura
br_ibge_pib.gini
br_ibge_pib.municipio
br_ibge_pnad.dicionario
br_ibge_pnad.microdados_compatibilizados_domicilio
br_ibge_pnad.microdados_compatibilizados_pessoa
br_ibge_pnad_covid.dicionario
br_ibge_pnadc.dicionario
br_ibge_pnadc.educacao
br_ibge_pnadc.microdados
br_ibge_pnadc.rendimentos_outras_fontes
br_ibge_pof.dicionario
br_ibge_populacao.brasil
br_ibge_populacao.municipio
br_ibge_populacao.uf
br_ibge_ppm.efetivo_rebanhos
br_ibge_ppm.producao_aquicultura
br_ibge_ppm.producao_origem_animal
br_ibge_ppm.producao_pecuaria
br_inep_ana.dicionario
br_inep_avaliacao_alfabetizacao.alunos
br_inep_avaliacao_alfabetizacao.dicionario
br_inep_avaliacao_alfabetizacao.meta_alfabetizacao_brasil
br_inep_avaliacao_alfabetizacao.meta_alfabetizacao_municipio
br_inep_avaliacao_alfabetizacao.meta_alfabetizacao_uf
br_inep_avaliacao_alfabetizacao.municipio
br_inep_avaliacao_alfabetizacao.uf
br_inep_censo_educacao_superior.curso
br_inep_censo_educacao_superior.dicionario
br_inep_censo_educacao_superior.ies
br_inep_censo_escolar.dicionario
br_inep_censo_escolar.escola
br_inep_censo_escolar.turma
br_inep_educacao_especial.brasil_distorcao_idade_serie
br_inep_educacao_especial.brasil_taxa_rendimento
br_inep_educacao_especial.distorcao_idade_serie
br_inep_educacao_especial.docente_aee
br_inep_educacao_especial.docente_formacao
br_inep_educacao_especial.etapa_ensino
br_inep_educacao_especial.faixa_etaria
br_inep_educacao_especial.localizacao
br_inep_educacao_especial.matricula_aee
br_inep_educacao_especial.sexo_raca_cor
br_inep_educacao_especial.taxa_rendimento
br_inep_educacao_especial.tempo_ensino
br_inep_educacao_especial.tipo_deficiencia
br_inep_educacao_especial.uf_distorcao_idade_serie
br_inep_educacao_especial.uf_taxa_rendimento
br_inep_enem.dicionario
br_inep_enem.microdados
br_inep_enem.questionario_socioeconomico_1998
br_inep_enem.questionario_socioeconomico_1999
br_inep_enem.questionario_socioeconomico_2000
br_inep_enem.questionario_socioeconomico_2001
br_inep_enem.questionario_socioeconomico_2002
br_inep_enem.questionario_socioeconomico_2003
br_inep_enem.questionario_socioeconomico_2004
br_inep_enem.questionario_socioeconomico_2005
br_inep_enem.questionario_socioeconomico_2006
br_inep_enem.questionario_socioeconomico_2007
br_inep_enem.questionario_socioeconomico_2008
br_inep_enem.questionario_socioeconomico_2009
br_inep_enem.questionario_socioeconomico_2010
br_inep_enem.questionario_socioeconomico_2011
br_inep_enem.questionario_socioeconomico_2012
br_inep_enem.questionario_socioeconomico_2013
br_inep_enem.questionario_socioeconomico_2014
br_inep_enem.questionario_socioeconomico_2015
br_inep_enem.questionario_socioeconomico_2016
br_inep_enem.questionario_socioeconomico_2017
br_inep_enem.questionario_socioeconomico_2018
br_inep_enem.questionario_socioeconomico_2019
br_inep_enem.questionario_socioeconomico_2020
br_inep_enem.questionario_socioeconomico_2021
br_inep_enem.questionario_socioeconomico_2022
br_inep_enem.questionario_socioeconomico_2023
br_inep_formacao_docente.dicionario
br_inep_ideb.brasil
br_inep_ideb.escola
br_inep_ideb.municipio
br_inep_ideb.regiao
br_inep_ideb.uf
br_inep_indicador_nivel_socioeconomico.dicionario
br_inep_indicador_nivel_socioeconomico.escola
br_inep_indicadores_educacionais.brasil
br_inep_indicadores_educacionais.brasil_remuneracao_docentes
br_inep_indicadores_educacionais.brasil_taxa_transicao
br_inep_indicadores_educacionais.escola
br_inep_indicadores_educacionais.municipio
br_inep_indicadores_educacionais.municipio_taxa_transicao
br_inep_indicadores_educacionais.regiao
br_inep_indicadores_educacionais.regiao_taxa_transicao
br_inep_indicadores_educacionais.uf
br_inep_indicadores_educacionais.uf_remuneracao_docentes
br_inep_indicadores_educacionais.uf_taxa_transicao
br_inep_saeb.aluno_ef_2ano
br_inep_saeb.aluno_ef_5ano
br_inep_saeb.aluno_ef_9ano
br_inep_saeb.aluno_em_34ano
br_inep_saeb.brasil
br_inep_saeb.brasil_taxa_alfabetizacao
br_inep_saeb.dicionario
br_inep_saeb.municipio
br_inep_saeb.proficiencia
br_inep_saeb.uf
br_inep_saeb.uf_taxa_alfabetizacao
br_inep_sinopse_estatistica_educacao_basica.dicionario
br_inep_sinopse_estatistica_educacao_basica.docente_deficiencia
br_inep_sinopse_estatistica_educacao_basica.docente_escolaridade
br_inep_sinopse_estatistica_educacao_basica.docente_etapa_ensino
br_inep_sinopse_estatistica_educacao_basica.docente_faixa_etaria_sexo
br_inep_sinopse_estatistica_educacao_basica.docente_localizacao
br_inep_sinopse_estatistica_educacao_basica.docente_regime_contrato
br_inep_sinopse_estatistica_educacao_basica.educacao_especial_etapa_ensino
br_inep_sinopse_estatistica_educacao_basica.educacao_especial_faixa_etaria
br_inep_sinopse_estatistica_educacao_basica.educacao_especial_localizacao
br_inep_sinopse_estatistica_educacao_basica.educacao_especial_sexo_raca_cor
br_inep_sinopse_estatistica_educacao_basica.educacao_especial_tempo_ensino
br_inep_sinopse_estatistica_educacao_basica.educacao_especial_tipo_deficiencia
br_inep_sinopse_estatistica_educacao_basica.etapa_ensino_serie
br_inep_sinopse_estatistica_educacao_basica.faixa_etaria
br_inep_sinopse_estatistica_educacao_basica.localizacao
br_inep_sinopse_estatistica_educacao_basica.sexo_raca_cor
br_inep_sinopse_estatistica_educacao_basica.tempo_ensino
br_inmet_bdmep.microdados
br_inpe_prodes.municipio_bioma
br_inpe_queimadas.microdados
br_inpe_sisam.microdados
br_ipea_avs.municipio
br_mdr_snis.municipio_agua_esgoto
br_mdr_snis.prestador_agua_esgoto
br_me_caged.dicionario
br_me_caged.microdados_movimentacao
br_me_caged.microdados_movimentacao_excluida
br_me_caged.microdados_movimentacao_fora_prazo
br_me_cno.dicionario
br_me_cnpj.dicionario
br_me_cnpj.empresas
br_me_cnpj.estabelecimentos
br_me_cnpj.simples
br_me_cnpj.socios
br_me_comex_stat.dicionario
br_me_comex_stat.municipio_exportacao
br_me_comex_stat.municipio_importacao
br_me_comex_stat.ncm_exportacao
br_me_comex_stat.ncm_importacao
br_me_rais.dicionario
br_me_rais.microdados_estabelecimentos
br_me_rais.microdados_vinculos
br_me_sic.dicionario
br_me_sic.transferencia
br_me_siconfi.municipio_balanco_patrimonial
br_me_siconfi.municipio_despesas_funcao
br_me_siconfi.municipio_despesas_orcamentarias
br_me_siconfi.municipio_receitas_orcamentarias
br_me_siconfi.uf_despesas_funcao
br_me_siconfi.uf_despesas_orcamentarias
br_me_siconfi.uf_receitas_orcamentarias
br_mec_prouni.dicionario
br_mec_sisu.microdados
br_mg_belohorizonte_smfa_iptu.dicionario
br_mg_belohorizonte_smfa_iptu.iptu
br_mme_consumo_energia_eletrica.uf
br_mp_pep.cargos_funcoes
br_ms_cnes.dados_complementares
br_ms_cnes.dicionario
br_ms_cnes.equipamento
br_ms_cnes.equipe
br_ms_cnes.estabelecimento
br_ms_cnes.estabelecimento_ensino
br_ms_cnes.estabelecimento_filantropico
br_ms_cnes.gestao_metas
br_ms_cnes.habilitacao
br_ms_cnes.incentivos
br_ms_cnes.leito
br_ms_cnes.profissional
br_ms_cnes.regra_contratual
br_ms_cnes.servico_especializado
br_ms_pns.dicionario
br_ms_pns.microdados_2013
br_ms_pns.microdados_2019
br_ms_populacao.municipio
br_ms_sia.dicionario
br_ms_sia.producao_ambulatorial
br_ms_sia.psicossocial
br_ms_sih.aihs_reduzidas
br_ms_sih.dicionario
br_ms_sih.servicos_profissionais
br_ms_sim.dicionario
br_ms_sim.microdados
br_ms_sinan.dicionario
br_ms_sinan.microdados_dengue
br_ms_sinan.microdados_influenza_srag
br_ms_sinasc.dicionario
br_ms_sinasc.microdados
br_ms_sisvan.dicionario
br_ms_sisvan.microdados
br_ms_vacinacao_covid19.dicionario
br_poder360_pesquisas.microdados
br_rf_arrecadacao.cnae
br_rf_arrecadacao.ir_ipi
br_rf_arrecadacao.itr
br_rf_arrecadacao.natureza_juridica
br_rf_arrecadacao.uf
br_rf_cafir.dicionario
br_rf_cafir.imoveis_rurais
br_rf_cno.areas
br_rf_cno.cnaes
br_rf_cno.dicionario
br_rf_cno.microdados
br_rf_cno.vinculos
br_rj_isp_estatisticas_seguranca.armas_apreendidas_mensal
br_rj_isp_estatisticas_seguranca.armas_fogo_apreendidas_mensal
br_rj_isp_estatisticas_seguranca.evolucao_mensal_cisp
br_rj_isp_estatisticas_seguranca.evolucao_mensal_municipio
br_rj_isp_estatisticas_seguranca.evolucao_mensal_uf
br_rj_isp_estatisticas_seguranca.evolucao_mensal_upp
br_rj_isp_estatisticas_seguranca.evolucao_policial_morto_servico_mensal
br_rj_isp_estatisticas_seguranca.feminicidio_mensal_cisp
br_rj_isp_estatisticas_seguranca.relacao_cisp_aisp_risp
br_rj_isp_estatisticas_seguranca.taxa_evolucao_anual_municipio
br_rj_isp_estatisticas_seguranca.taxa_evolucao_anual_uf
br_rj_isp_estatisticas_seguranca.taxa_evolucao_mensal_municipio
br_rj_isp_estatisticas_seguranca.taxa_evolucao_mensal_uf
br_rj_isp_estatisticas_seguranca.taxa_letalidade
br_seeg_emissoes.dicionario
br_seeg_emissoes.municipio
br_seeg_emissoes.uf
br_sfb_sicar.area_imovel
br_sfb_sicar.dicionario
br_simet_educacao_conectada.escola
br_sp_saopaulo_geosampa_iptu.iptu
br_stf_corte_aberta.decisoes
br_stf_corte_aberta.dicionario
br_trase_supply_chain.beef
br_trase_supply_chain.beef_slaughterhouses
br_trase_supply_chain.soy_beans
br_trase_supply_chain.soy_beans_crushing_facilities
br_trase_supply_chain.soy_beans_refining_facilities
br_trase_supply_chain.soy_beans_storage_facilities
br_tse_eleicoes.bens_candidato
br_tse_eleicoes.candidatos
br_tse_eleicoes.despesas_candidato
br_tse_eleicoes.detalhes_votacao_municipio
br_tse_eleicoes.detalhes_votacao_municipio_zona
br_tse_eleicoes.detalhes_votacao_secao
br_tse_eleicoes.dicionario
br_tse_eleicoes.partidos
br_tse_eleicoes.perfil_eleitorado_local_votacao
br_tse_eleicoes.perfil_eleitorado_municipio_zona
br_tse_eleicoes.perfil_eleitorado_secao
br_tse_eleicoes.receitas_candidato
br_tse_eleicoes.receitas_comite
br_tse_eleicoes.receitas_orgao_partidario
br_tse_eleicoes.resultados_candidato
br_tse_eleicoes.resultados_candidato_municipio
br_tse_eleicoes.resultados_candidato_municipio_zona
br_tse_eleicoes.resultados_candidato_secao
br_tse_eleicoes.resultados_partido_municipio
br_tse_eleicoes.resultados_partido_municipio_zona
br_tse_eleicoes.resultados_partido_secao
br_tse_eleicoes.vagas
br_tse_filiacao_partidaria.microdados
br_tse_filiacao_partidaria.microdados_antigos
dataset_new_arch.tabela_new_arch
logs.cloudaudit_googleapis_com_activity
logs.cloudaudit_googleapis_com_data_access
mundo_transfermarkt_competicoes.brasileirao_serie_a
mundo_transfermarkt_competicoes.copa_brasil
mundo_transfermarkt_competicoes_internacionais.champions_league
test_dataset.test_table
us_harvard_ned.parliamentary_elections
us_harvard_ned.presidential_elections
world_ampas_oscar.winner_demographics
world_iea_pirls.dictionary
world_iea_pirls.home_context
world_iea_pirls.school_context
world_iea_pirls.student_achievement
world_iea_pirls.student_context
world_iea_pirls.student_teacher_link
world_iea_pirls.teacher_context
world_iea_pirls.within_country_scoring_reliability
world_iea_timss.dictionary
world_iea_timss.home_context_grade_4
world_iea_timss.school_context_grade_4
world_iea_timss.school_context_grade_8
world_iea_timss.student_achievement_grade_4
world_iea_timss.student_achievement_grade_8
world_iea_timss.student_context_grade_4
world_iea_timss.student_context_grade_8
world_iea_timss.teacher_context_grade_4
world_iea_timss.teacher_mathematics_grade_8
world_iea_timss.teacher_science_grade_8
world_imdb_movies.top_movies_per_year
world_oecd_pisa.student
world_oecd_public_finance.country
world_olympedia_olympics.athlete_bio
world_olympedia_olympics.athlete_event_result
world_olympedia_olympics.country
world_olympedia_olympics.game
world_olympedia_olympics.game_medal_tally
world_olympedia_olympics.result
world_sofascore_competicoes_futebol.brasileirao_serie_a
world_sofascore_competicoes_futebol.uefa_champions_league
world_wb_mides.dicionario
world_wb_mides.empenho
world_wb_mides.licitacao
world_wb_mides.licitacao_item
world_wb_mides.licitacao_participante
world_wb_mides.liquidacao
world_wb_mides.orgao_unidade_gestora
world_wb_mides.pagamento
world_wb_mides.relacionamentos
world_wwf_hydrosheds.basins_atlas
world_wwf_hydrosheds.lakes_atlas
world_wwf_hydrosheds.rivers_atlas

6
failed_tables.txt Normal file
View File

@@ -0,0 +1,6 @@
[ACCESS_DENIED] br_bcb_taxa_cambio.taxa_cambio
[ACCESS_DENIED] br_bcb_taxa_selic.taxa_selic
[ACCESS_DENIED] br_bcb_taxa_cambio.taxa_cambio
[ACCESS_DENIED] br_bcb_taxa_selic.taxa_selic
[ACCESS_DENIED] br_bcb_taxa_cambio.taxa_cambio
[ACCESS_DENIED] br_bcb_taxa_selic.taxa_selic

527
roda.sh Executable file
View File

@@ -0,0 +1,527 @@
#!/usr/bin/env bash
# =============================================================================
# export_basedosdados.sh
# Exports all basedosdados BigQuery tables → GCS (Parquet+zstd) → Hetzner Object Storage
#
# Prerequisites (run once before this script):
# gcloud auth login
# gcloud auth application-default login
# gcloud config set project YOUR_PROJECT_ID
# cp .env.example .env # then fill in your values
#
# Usage:
# chmod +x export_basedosdados.sh
# ./export_basedosdados.sh # full run (locally)
# ./export_basedosdados.sh --dry-run # list tables + estimated sizes, no export
# ./export_basedosdados.sh --gcloud-run # create GCP VM → run there → delete VM
# =============================================================================
set -euo pipefail
# Add util-linux to PATH on macOS (provides flock)
[[ -d "/opt/homebrew/opt/util-linux/bin" ]] && export PATH="/opt/homebrew/opt/util-linux/bin:$PATH"
# Load .env if present
if [[ -f "$(dirname "$0")/.env" ]]; then
set -a
# shellcheck source=.env
source "$(dirname "$0")/.env"
set +a
fi
DRY_RUN=false
GCLOUD_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
elif [[ "${1:-}" == "--gcloud-run" ]]; then
GCLOUD_RUN=true
fi
# -----------------------------------------------------------------------------
# LOGGING
# -----------------------------------------------------------------------------
LOG_FILE="export_$(date +%Y%m%d_%H%M%S).log"
FAILED_FILE="failed_tables.txt"
DONE_FILE="done_tables.txt"
DONE_TRANSFERS_FILE="done_transfers.txt"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
log_err() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" | tee -a "$LOG_FILE" >&2; }
# -----------------------------------------------------------------------------
# STEP 0 — Verify dependencies
# -----------------------------------------------------------------------------
log "Checking dependencies..."
if $GCLOUD_RUN; then
for cmd in gcloud; do
if ! command -v "$cmd" &>/dev/null; then
log_err "'$cmd' not found. Install google-cloud-sdk."
exit 1
fi
done
else
for cmd in bq gcloud gsutil parallel rclone flock; do
if ! command -v "$cmd" &>/dev/null; then
log_err "'$cmd' not found. Install google-cloud-sdk, GNU parallel, and rclone."
exit 1
fi
done
fi
# Validate S3 credentials
if [[ -z "${AWS_ACCESS_KEY_ID:-}" || -z "${AWS_SECRET_ACCESS_KEY:-}" ]]; then
log_err "Credenciais S3 não encontradas. Preencha o .env com AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY."
exit 1
fi
# Configure rclone remotes via env vars — no rclone.conf or inline credentials needed.
# GCS remote (bd:) uses Application Default Credentials from gcloud auth application-default login.
# Hetzner S3 remote (hz:) uses the credentials from .env, kept out of the process command line.
export RCLONE_CONFIG_BD_TYPE="google cloud storage"
export RCLONE_CONFIG_BD_BUCKET_POLICY_ONLY="true"
export RCLONE_CONFIG_HZ_TYPE="s3"
export RCLONE_CONFIG_HZ_PROVIDER="Other"
export RCLONE_CONFIG_HZ_ENDPOINT="$HETZNER_S3_ENDPOINT"
export RCLONE_CONFIG_HZ_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
export RCLONE_CONFIG_HZ_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"
# =============================================================================
# GCLOUD RUN — create a Compute Engine VM, run the export there, then clean up
# =============================================================================
if $GCLOUD_RUN; then
VM_NAME="${GCP_VM_NAME:-bd-export-vm}"
VM_ZONE="${GCP_VM_ZONE:-us-central1-a}"
SCRIPT_PATH="$(realpath "$0")"
ENV_PATH="$(dirname "$SCRIPT_PATH")/.env"
log "=============================="
log " GCLOUD RUN MODE"
log "=============================="
# ── Step 1/4: Create instance ───────────────────────────────────────────
log "[1/4] Creating VM: $VM_NAME ($VM_ZONE) ..."
if gcloud compute instances describe "$VM_NAME" \
--zone="$VM_ZONE" --project="$YOUR_PROJECT" &>/dev/null; then
log " VM already exists, reusing it."
else
gcloud compute instances create "$VM_NAME" \
--project="$YOUR_PROJECT" \
--zone="$VM_ZONE" \
--machine-type=e2-standard-4 \
--image-family=debian-12 \
--image-project=debian-cloud \
--boot-disk-size=20GB \
--scopes=cloud-platform
log " VM created."
fi
# ── Step 2/4: Wait for SSH + copy files ────────────────────────────────
log "[2/4] Waiting for SSH and copying files..."
for i in {1..18}; do
if gcloud compute ssh "$VM_NAME" \
--zone="$VM_ZONE" --project="$YOUR_PROJECT" \
--command="echo ready" 2>/dev/null; then
break
fi
log " SSH not ready yet ($i/18), retrying in 10s..."
sleep 10
done
gcloud compute scp "$SCRIPT_PATH" "$ENV_PATH" \
"$VM_NAME:~/" \
--zone="$VM_ZONE" \
--project="$YOUR_PROJECT"
log " Files copied."
# ── Step 3/4: Install dependencies ─────────────────────────────────────
log "[3/4] Installing dependencies on VM (~2 min)..."
gcloud compute ssh "$VM_NAME" \
--zone="$VM_ZONE" \
--project="$YOUR_PROJECT" \
--command="bash -s" <<'REMOTE_SETUP'
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update -qq
sudo apt-get install -y apt-transport-https ca-certificates gnupg curl parallel rclone
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
| sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list >/dev/null
sudo apt-get update -qq
sudo apt-get install -y google-cloud-cli
chmod +x ~/roda.sh
echo "Dependencies installed."
REMOTE_SETUP
log " Dependencies ready."
# ── Step 4/4: Run the export script interactively ──────────────────────
log "[4/4] Launching roda.sh on VM — answer prompts as they appear."
gcloud compute ssh "$VM_NAME" \
--zone="$VM_ZONE" \
--project="$YOUR_PROJECT" \
-- bash ~/roda.sh
# ── Cleanup: Delete VM ──────────────────────────────────────────────────
echo ""
echo "============================================================"
echo " CLEANUP"
echo "============================================================"
read -rp "Delete VM instance $VM_NAME? [y/N] " del_vm
if [[ "$del_vm" =~ ^[Yy]$ ]]; then
log "Deleting VM $VM_NAME ..."
gcloud compute instances delete "$VM_NAME" \
--zone="$VM_ZONE" \
--project="$YOUR_PROJECT" \
--quiet
log "VM deleted."
else
log "VM kept. To delete manually:"
log " gcloud compute instances delete $VM_NAME --zone=$VM_ZONE --project=$YOUR_PROJECT"
fi
exit 0
fi
# -----------------------------------------------------------------------------
# STEP 1 — Create GCS bucket in US region (same as basedosdados)
# -----------------------------------------------------------------------------
if $DRY_RUN; then
log "[DRY RUN] Would create GCS bucket: gs://$BUCKET_NAME in region $BUCKET_REGION"
else
log "Creating GCS bucket: gs://$BUCKET_NAME in region $BUCKET_REGION"
if gsutil ls "gs://$BUCKET_NAME" &>/dev/null; then
log "Bucket already exists, skipping creation."
else
gsutil mb \
-p "$YOUR_PROJECT" \
-l "$BUCKET_REGION" \
-b on \
"gs://$BUCKET_NAME"
log "Bucket created: gs://$BUCKET_NAME"
fi
fi
# Resume support: load already-done tables/transfers
touch "$DONE_FILE" "$FAILED_FILE" "$DONE_TRANSFERS_FILE"
# -----------------------------------------------------------------------------
# STEP 2 — Build the full table list from the basedosdados project
#
# We auto-discover all datasets and tables via the BQ API so we don't rely
# on a hardcoded list. This also detects any new tables added since the
# tables-summary.md was written.
#
# Atomicity: we write to a .tmp file and mv it into place only on success,
# so an interrupted run never leaves a partial list behind.
# -----------------------------------------------------------------------------
log "Discovering all datasets in project: $SOURCE_PROJECT ..."
TABLE_LIST_FILE="all_tables.txt"
TABLE_LIST_TMP="${TABLE_LIST_FILE}.tmp"
if [[ ! -f "$TABLE_LIST_FILE" ]]; then
bq ls --project_id="$SOURCE_PROJECT" --max_results=10000 --format=json 2>/dev/null \
| python3 -c "
import json, sys
datasets = json.load(sys.stdin)
for ds in datasets:
print(ds['datasetReference']['datasetId'])
" > /tmp/datasets.txt
log "Found $(wc -l < /tmp/datasets.txt) datasets. Listing tables in parallel..."
TMP_TABLE_DIR=$(mktemp -d)
list_dataset_tables() {
local dataset="$1"
local source="$2"
local tmp_dir="$3"
bq ls \
--project_id="$source" \
--dataset_id="$source:$dataset" \
--max_results=10000 \
--format=json 2>/dev/null \
| python3 -c "
import json, sys
data = sys.stdin.read()
if not data.strip():
sys.exit(0)
for t in json.loads(data):
ref = t.get('tableReference', {})
if t.get('type') in ('TABLE', 'EXTERNAL'):
print(ref['datasetId'] + '.' + ref['tableId'])
" > "$tmp_dir/$dataset.txt"
}
export -f list_dataset_tables
parallel --jobs 16 list_dataset_tables {} "$SOURCE_PROJECT" "$TMP_TABLE_DIR" < /tmp/datasets.txt
cat "$TMP_TABLE_DIR"/*.txt | sort > "$TABLE_LIST_TMP"
rm -rf "$TMP_TABLE_DIR"
mv "$TABLE_LIST_TMP" "$TABLE_LIST_FILE"
log "Total tables discovered: $(wc -l < "$TABLE_LIST_FILE")"
else
log "Reusing existing table list: $TABLE_LIST_FILE ($(wc -l < "$TABLE_LIST_FILE") tables)"
fi
# -----------------------------------------------------------------------------
# DRY RUN — show table count and exit
# -----------------------------------------------------------------------------
if $DRY_RUN; then
TOTAL=$(wc -l < "$TABLE_LIST_FILE")
log "[DRY RUN] $TOTAL tables found. No exports will run."
log "[DRY RUN] Estimating total size via bq show in parallel (this may take a while)..."
get_table_bytes() {
local table="$1"
local source="$2"
local dataset table_id
dataset=$(echo "$table" | cut -d. -f1)
table_id=$(echo "$table" | cut -d. -f2)
bq show --format=json "${source}:${dataset}.${table_id}" 2>/dev/null \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('numBytes','0'))" 2>/dev/null \
|| echo 0
}
export -f get_table_bytes
TOTAL_BYTES=$(parallel --jobs 16 get_table_bytes {} "$SOURCE_PROJECT" < "$TABLE_LIST_FILE" \
| awk '{s+=$1} END{print s+0}')
TOTAL_GB=$(echo "scale=2; $TOTAL_BYTES / 1073741824" | bc)
# Parquet+zstd typically compresses structured data 510x vs BigQuery's raw numBytes
COMPRESSED_LOW=$(echo "scale=2; $TOTAL_GB / 10" | bc)
COMPRESSED_HIGH=$(echo "scale=2; $TOTAL_GB / 5" | bc)
EGRESS_LOW=$(echo "scale=2; $COMPRESSED_LOW * 0.08" | bc)
EGRESS_HIGH=$(echo "scale=2; $COMPRESSED_HIGH * 0.12" | bc)
log "[DRY RUN] BigQuery raw size (uncompressed): ~${TOTAL_GB} GB"
log "[DRY RUN] Estimated Parquet+zstd size: ~${COMPRESSED_LOW}${COMPRESSED_HIGH} GB"
log "[DRY RUN] Estimated GCS→Hetzner egress cost: USD ${EGRESS_LOW}${EGRESS_HIGH}"
log "[DRY RUN] Done. Remove --dry-run to start the actual export."
exit 0
fi
# -----------------------------------------------------------------------------
# COST WARNING — confirm before starting export
# -----------------------------------------------------------------------------
echo ""
echo "============================================================"
echo " COST WARNING"
echo " Transferring data from GCS to Hetzner costs ~\$0.08-0.12/GB"
echo " in internet egress fees charged to: $YOUR_PROJECT"
echo " Run with --dry-run first to estimate the total size."
echo "============================================================"
echo ""
read -rp "Press ENTER to start the export, or Ctrl+C to abort: "
# -----------------------------------------------------------------------------
# STEP 3 — Export function (called in parallel)
# -----------------------------------------------------------------------------
export_table() {
local table="$1"
local bucket="$2"
local project="$3"
local source="$4"
local done_file="$5"
local failed_file="$6"
local log_file="$7"
# Skip if already done
if grep -qxF "$table" "$done_file" 2>/dev/null; then
echo "[SKIP] $table (already exported)" >> "$log_file"
return 0
fi
local dataset table_id gcs_prefix
dataset=$(echo "$table" | cut -d. -f1)
table_id=$(echo "$table" | cut -d. -f2)
gcs_prefix="gs://$bucket/$dataset/$table_id"
echo "[START] Exporting $source:$table$gcs_prefix/*.parquet" >> "$log_file"
# Run bq extract with retry (up to 3 attempts)
# Skip retries immediately if the error is a known incompatible type
local attempt=0
local success=false
local output
while [[ $attempt -lt 3 ]]; do
attempt=$((attempt + 1))
output=$(bq extract \
--project_id="$project" \
--destination_format=PARQUET \
--compression=ZSTD \
--location=US \
"${source}:${dataset}.${table_id}" \
"${gcs_prefix}/*.parquet" \
2>&1)
local exit_code=$?
echo "$output" >> "$log_file"
if [[ $exit_code -eq 0 ]]; then
success=true
break
fi
# Detect permanently incompatible types — no point retrying
if echo "$output" | grep -qi "not supported\|unsupported type\|GEOGRAPHY\|JSON type"; then
echo "[SKIP_INCOMPATIBLE] $table — unsupported column type, skipping retries" >> "$log_file"
flock "$failed_file" bash -c "echo '[INCOMPATIBLE] $table' >> '$failed_file'"
return 0
fi
# Detect access/permission errors — no point retrying
if echo "$output" | grep -qi "access denied\|permission denied\|not authorized\|403\|does not exist\|Not found"; then
echo "[SKIP_ACCESS] $table — access denied or not found, skipping retries" >> "$log_file"
flock "$failed_file" bash -c "echo '[ACCESS_DENIED] $table' >> '$failed_file'"
return 0
fi
echo "[RETRY $attempt/3] $table" >> "$log_file"
sleep $((attempt * 10))
done
if $success; then
# flock prevents race condition when multiple workers write concurrently
flock "$done_file" bash -c "echo '$table' >> '$done_file'"
echo "[DONE] $table" >> "$log_file"
else
flock "$failed_file" bash -c "echo '$table' >> '$failed_file'"
echo "[FAIL] $table after 3 attempts" >> "$log_file"
fi
}
export -f export_table
# -----------------------------------------------------------------------------
# STEP 4 — Run exports in parallel
# -----------------------------------------------------------------------------
log "Starting parallel exports ($PARALLEL_EXPORTS workers)..."
log "Progress is logged to: $LOG_FILE"
log "Failed tables will be written to: $FAILED_FILE"
# Filter out already-done tables
comm -23 \
<(sort "$TABLE_LIST_FILE") \
<(sort "$DONE_FILE") \
| parallel \
--jobs "$PARALLEL_EXPORTS" \
--progress \
--bar \
export_table {} "$BUCKET_NAME" "$YOUR_PROJECT" "$SOURCE_PROJECT" \
"$DONE_FILE" "$FAILED_FILE" "$LOG_FILE" \
|| true # failures are tracked in $FAILED_FILE; don't let parallel's exit code abort the script
TOTAL=$(wc -l < "$TABLE_LIST_FILE")
DONE=$(wc -l < "$DONE_FILE")
FAILED=$(wc -l < "$FAILED_FILE")
log "Export phase complete: $DONE/$TOTAL done, $FAILED failed"
if [[ $FAILED -gt 0 ]]; then
log "Failed tables:"
cat "$FAILED_FILE" | tee -a "$LOG_FILE"
log "To retry failed tables only, run: bash $0 --retry-failed"
fi
# -----------------------------------------------------------------------------
# STEP 5 — Transfer GCS → Hetzner Object Storage via rclone (no local staging)
#
# rclone streams data directly between GCS and S3 through RAM only —
# no local disk required.
# -----------------------------------------------------------------------------
log "Starting transfer to Hetzner Object Storage ($HETZNER_S3_ENDPOINT)..."
TRANSFER_LOG_DIR=$(mktemp -d)
# Compute total datasets in GCS bucket once (used for progress display)
TRANSFER_TOTAL=$(gsutil ls "gs://$BUCKET_NAME/" | wc -l)
export TRANSFER_TOTAL
download_dataset() {
local dataset="$1"
local bucket="$2"
local s3_bucket="$3"
local s3_concurrency="$4"
local done_transfers_file="$5"
local log_dir="$6"
local total="$7"
local dataset_log="$log_dir/${dataset}.log"
# Resume: skip datasets already transferred
if grep -qxF "$dataset" "$done_transfers_file" 2>/dev/null; then
echo "[SKIP_TRANSFER] $dataset (already transferred)" > "$dataset_log"
return 0
fi
echo "[TRANSFER] gs://$bucket/$dataset/ → hz:$s3_bucket/$dataset/" > "$dataset_log"
# Named remotes bd: (GCS) and hz: (Hetzner S3) are configured via RCLONE_CONFIG_* env vars
if rclone copy \
"bd:$bucket/$dataset/" \
"hz:$s3_bucket/$dataset/" \
--transfers "$s3_concurrency" \
--s3-upload-concurrency "$s3_concurrency" \
--progress \
>> "$dataset_log" 2>&1; then
flock "$done_transfers_file" bash -c "echo '$dataset' >> '$done_transfers_file'"
echo "[TRANSFERRED] $dataset" >> "$dataset_log"
local done_count
done_count=$(wc -l < "$done_transfers_file")
local pct=$(( done_count * 100 / total ))
echo "[${done_count}/${total}] ${pct}% datasets transferidos"
else
echo "[TRANSFER FAIL] rclone failed for $dataset" >> "$dataset_log"
return 1
fi
}
export -f download_dataset
# Get list of exported datasets, skipping already-transferred ones
comm -23 \
<(gsutil ls "gs://$BUCKET_NAME/" | sed 's|gs://[^/]*/||;s|/||' | sort -u) \
<(sort "$DONE_TRANSFERS_FILE") \
| parallel \
--jobs "$PARALLEL_UPLOADS" \
download_dataset {} "$BUCKET_NAME" "$HETZNER_S3_BUCKET" "$S3_CONCURRENCY" "$DONE_TRANSFERS_FILE" "$TRANSFER_LOG_DIR" "$TRANSFER_TOTAL" \
|| true # failures are tracked per-dataset; don't abort
# Merge per-dataset logs into main log in order
for f in $(ls "$TRANSFER_LOG_DIR"/*.log 2>/dev/null | sort); do
cat "$f" >> "$LOG_FILE"
done
rm -rf "$TRANSFER_LOG_DIR"
log "Transfer complete."
# -----------------------------------------------------------------------------
# STEP 6 — Verify file counts on Hetzner Object Storage vs GCS
# -----------------------------------------------------------------------------
log "Verifying file counts..."
GCS_COUNT=$(gsutil ls -r "gs://$BUCKET_NAME/**" | grep '\.parquet$' | wc -l)
S3_COUNT=$(rclone ls "hz:$HETZNER_S3_BUCKET" 2>/dev/null | grep '\.parquet$' | wc -l)
log "GCS parquet files: $GCS_COUNT"
log "S3 parquet files: $S3_COUNT"
if [[ "$GCS_COUNT" -eq "$S3_COUNT" ]]; then
log "File counts match. Transfer verified."
else
log_err "Count mismatch! GCS=$GCS_COUNT S3=$S3_COUNT"
log_err "Re-run the script to resume failed datasets or check $LOG_FILE for errors."
fi
# -----------------------------------------------------------------------------
# STEP 7 — Clean up GCS bucket to stop storage charges
# -----------------------------------------------------------------------------
read -rp "Delete GCS bucket gs://$BUCKET_NAME to stop storage charges? [y/N] " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
log "Deleting bucket gs://$BUCKET_NAME ..."
gsutil -m rm -r "gs://$BUCKET_NAME"
gsutil rb "gs://$BUCKET_NAME"
log "Bucket deleted. Storage charges stopped."
else
log "Bucket kept. Remember to delete it later: gsutil -m rm -r gs://$BUCKET_NAME && gsutil rb gs://$BUCKET_NAME"
fi
log "All done! Data is at s3://$HETZNER_S3_BUCKET/ ($HETZNER_S3_ENDPOINT)"
log "Total exported: $DONE tables | Failed: $FAILED tables"
log "See $LOG_FILE for full details."