AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / user-34652

exhuma's questions

Martin Hope
exhuma
Asked: 2021-05-20 05:14:26 +0800 CST

Force NOW a retornar novos timestamps sem confirmar a transação

  • 3

Estou ciente de que, no PostgreSQL, o valor de retorno de NOW()é o registro de data e hora do início da transação . Portanto, se você usar NOW()várias vezes na mesma transação, sempre retornará o mesmo valor.

E isso é bom no meu livro.

Mas eu tenho um pequeno problema com testes de unidade em um aplicativo cliente com isso e adoraria poder dizer ao PostgreSQL para desabilitar (temporariamente) isso, se possível.

A razão pela qual eu não quero (não posso) usar outra função timestamp como clock_timestamp()é porque a chamada de função NOW()fica dentro de um gatilho e, no código de produção, quero o comportamento "início da transação".

Mas em meus testes de unidade estou corrigindo a função "commit" no nível da API para não comprometer dados reais acidentalmente no banco de dados durante o teste (não se preocupe, não uso o banco de dados de produção durante o teste). Portanto, durante os testes de unidade, commitnunca atinge o banco de dados, então não recebo novos registros de data e hora de transação.

O banco de dados usa tabelas temporais e novas entradas são anexadas apenas às tabelas de histórico se o carimbo de data/hora for alterado para garantir que consolidamos apenas uma entrada na tabela de histórico por transação.

Mas ao testar o comportamento da tabela temporal, isso agora faz com que nenhuma entrada apareça nas tabelas de histórico. O fragmento de chave do gatilho da tabela temporal é este:

new_validity_period = tstzrange(
    lower(OLD.validity_period),
    NOW(),
    '[)'
);
IF isempty(new_validity_period) THEN
    RAISE DEBUG 'New entry % will not introduce a new history item', OLD;
    RETURN OLD;
END IF;

Portanto, quando eu fizer uma operação de "inserção" no meu teste de unidade e o tempo de transação for '2020-01-01 01:02:03', o período de validade dessa entrada será [2020-01-01 01:02:02,). Se, ainda no mesmo teste unitário, eu excluir a entrada (e para testar se ela aparece na tabela de histórico), a operação acontece no mesmo TX, e o código acima ficará assim:

new_validity_perion = tstzrange(
    '2020-01-01 01:02:03', -- the lower-bound of the 'OLD' row
    '2020-01-01 01:02:03', -- the result of 'NOW()'
    '[)'
)
-- resulting in an empty range because the two timestamps are identical
IF isempty(new_validity_periond) THEN  -- <- Resulting to TRUE
    ...
    RETURN OLD;  -- returning here, not continuing to store the history entry
END IF;
-- code below here is skipped

Existe uma maneira de configurar o PG para sempre retornar o tempo de parede ao ligar NOW()? Os testes são executados em um contêiner docker para que eu tenha controle total sobre o server-config. Mas seria ainda melhor se eu pudesse alterar o comportamento definindo a configuração por meio de um comando SQL, para que eu pudesse alterar apenas o comportamento dos testes da tabela temporal.

Se isso não for possível, eu precisaria usar uma configuração de sessão/transação de banco de dados diferente para esses testes. Isso também é bom, mas eu queria saber se eu poderia controlar esse comportamento do PG.

Apêndice: Isolamento de transação de teste de unidade

Este é o código que uso para isolar as chamadas de confirmação no código do usuário:

@fixture
def rb_session():
    """
    Returns a session which will always be rolled back.
    """

    engine = create_engine(Configurations.getenv("IPBASE_DATABASE_DSN"))

    # Get a *specific* connection from the pool
    connection = engine.connect()
    # Explicitly start a new connection on the connection we got
    # This makes the normal "session.begin()" and "session.commit()"
    # calls in SQLAlchemy no-ops as there is already a transaction
    # in progress.
    transaction = connection.begin()
    # Ensure we use the "primed" connection for all our SQLAlchemy
    # session needs in our unit-tests.
    session = Session(bind=connection)
    try:
        yield session
    finally:
        transaction.rollback()
        session.close()
        connection.close()
postgresql timestamp
  • 1 respostas
  • 312 Views
Martin Hope
exhuma
Asked: 2018-09-18 06:50:24 +0800 CST

Como uso "OLD" ou "NEW" em uma instrução SQL dinâmica em um gatilho de atualização?

  • 1

Supondo que eu tenha a seguinte função sendo executada em uma atualização de linha:

CREATE OR REPLACE FUNCTION versioned_update()
RETURNS TRIGGER AS $$
DECLARE
     sql TEXT;
BEGIN
    sql := 'INSERT INTO backup_table VALUES (OLD)';
    EXECUTE sql;
END;
$$ language 'plpgsql';

O exemplo acima não funciona porque OLDé desconhecido nesse contexto de execução. Então eu tentei algo como:

sql := format('INSERT INTO backup_table VALUES (%L)', OLD);

e

sql := format('INSERT INTO backup_table VALUES (%L)', (OLD));

e

sql := format('INSERT INTO backup_table VALUES (%L)', (OLD.*));

Tudo sem sorte.

Esse problema faz parte de um gatilho maior no qual estou trabalhando. Olhando isoladamente não faz muito sentido, mas ilustra a questão.

postgresql trigger
  • 1 respostas
  • 466 Views
Martin Hope
exhuma
Asked: 2018-09-18 04:21:36 +0800 CST

Como posso escrever um gatilho "on-update" que recebe um nome de tabela como argumento?

  • 4

Observação : o exemplo a seguir é um código fortemente simplificado, mas ilustra a tarefa em um exemplo reproduzível. Olhar para este exemplo pode fazer você pensar "por que fazer assim"? Na realidade, a tarefa completa é armazenar uma trilha de auditoria para várias tabelas, mas somente se determinadas condições forem atendidas. A condição é a mesma para cada tabela (cada uma delas compartilha algumas colunas como inserted, updatede assim por diante). Portanto, o código para armazenar uma trilha de auditoria é o mesmo para cada tabela. Mas as colunas reais a serem copiadas são diferentes a cada vez. Eu quis criar um gatilho que lidasse com isso dinamicamente para que eu não precisasse tocá-lo toda vez que o esquema fosse alterado.


Considere o seguinte exemplo de trabalho (pergunta abaixo). Isso demonstra um esquema simples em que cada atualização na tabela datafaz com que os valores antigos sejam movidos para data2:

DROP TABLE IF EXISTS data CASCADE;
DROP TABLE IF EXISTS data2 CASCADE;
CREATE TABLE data (
    id SERIAL,
    name TEXT,
    updated TIMESTAMP WITH TIME ZONE
);
CREATE TABLE data2 (
    id SERIAL,
    name TEXT,
    updated TIMESTAMP WITH TIME ZONE
);

CREATE OR REPLACE FUNCTION update_trigger_func()
    RETURNS TRIGGER AS $$
    BEGIN
        NEW.updated = NOW();
        INSERT INTO data2 VALUES (OLD.*);
        RETURN NEW;
    END;
    $$ language 'plpgsql';

CREATE TRIGGER update_trigger
    BEFORE UPDATE ON data
    FOR EACH ROW
    EXECUTE PROCEDURE update_trigger_func();


SET client_min_messages TO 'debug';
INSERT INTO data (name) VALUES ('foo');
COMMIT;  -- Make sure we get new timestamps from NOW()
SELECT * FROM ONLY data;
SELECT * FROM ONLY data2;

SELECT pg_sleep(1);
UPDATE data SET name = 'bar';
COMMIT;  -- Make sure we get new timestamps from NOW()
SELECT * FROM ONLY data;
SELECT * FROM ONLY data2;

SELECT pg_sleep(1);
UPDATE data SET name = 'baz';
COMMIT;  -- Make sure we get new timestamps from NOW()
SELECT * FROM ONLY data;
SELECT * FROM ONLY data2;

Observe que a função update_trigger_functem o nome da tabela "history" codificado como data2na linha que diz:

INSERT INTO data2 VALUES (OLD.*);

Se data2fosse um argumento, esta função poderia ser reutilizável para outras tabelas também. Mas até agora não consegui encontrar o encantamento certo. Eu tentei as duas versões a seguir até agora:

INSERT INTO TG_ARGV[0] VALUES (OLD.*);

Mas isso causa um erro de sintaxe:

psql:temptable.sql:28: ERROR:  syntax error at or near "VALUES"
LINE 11:         INSERT INTO TG_ARGV[0] VALUES (OLD.*);

Então, alternativamente, tentei com SQL dinâmico:

sql := 'INSERT INTO' || TG_ARGV[0] || 'VALUES (OLD.*)';
EXECUTE sql;

Mas isso falha porque a OLDvariável não está disponível no contexto de execução:

psql:temptable.sql:58: ERROR:  missing FROM-clause entry for table "old"
LINE 1: INSERT INTO data2 VALUES (OLD.*)
                                    ^
QUERY:  INSERT INTO data2 VALUES (OLD.*)
CONTEXT:  PL/pgSQL function versioned_update() line 11 at EXECUTE

Dado que eu gostaria de usar essa função de gatilho em outras tabelas, não posso codificar os nomes das colunas. Como eu poderia conseguir isso?

postgresql trigger
  • 1 respostas
  • 471 Views
Martin Hope
exhuma
Asked: 2018-09-14 07:41:56 +0800 CST

É possível executar um gatilho de "atualização" se todas as colunas, exceto uma, tiverem sido alteradas?

  • 3

Suponha que eu tenha a seguinte tabela:

CREATE TABLE data (
    key TEXT primary key,
    some_interesting_value TEXT,
    inserted TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    last_seen TIMESTAMP WITH TIME ZONE
);

Então eu posso criar um gatilho para definir a coluna "atualizada" assim:

CREATE TRIGGER set_updated_col
    AFTER UPDATE ON data
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE PROCEDURE set_updated_timestamp();

A last_seencoluna neste caso contém um timestamp que é atualizado toda vez que um processo é executado (com algumas condições). No entanto, esse valor não é interessante para acompanhar as atualizações da tabela. No meu caso, tenho muitas dessas tabelas. Todos eles contêm informações sobre dispositivos físicos de rede, e o valor "last_seen" é importante para cada entidade.

O acionador acima definirá o carimbo de data/hora "atualizado", mesmo que apenas o valor "last_seen" seja modificado.

No meu caso eu quero evitar isso.

Eu poderia nomear manualmente todas as colunas da WHENcláusula, mas então, se eu adicionar uma coluna à tabela, existe o risco de ela ser esquecida na trigger.

Existe uma maneira de "remover" a last_seencoluna de OLD.*?

Como exemplo de código:

-- This should NOT trigger a change to `updated`
UPDATE data SET last_seen=NOW() WHERE key='foobar';

-- this SHOULD trigger a change to `updated`
UPDATE data SET some_interesting_value='foo', last_seen=NOW() WHERE key='foobar'
postgresql trigger
  • 1 respostas
  • 1214 Views
Martin Hope
exhuma
Asked: 2018-01-26 01:48:41 +0800 CST

Como posso usar uma função de 1 argumento em uma consulta "ANY"/"ALL"?

  • 2

Supondo que eu tenha a seguinte função:

 CREATE OR REPLACE FUNCTION myfun(IN TEXT)
     RETURNS boolean
     LANGUAGE SQL
     IMMUTABLE
     LEAKPROOF
     AS $CODE$
         SELECT COALESCE(
             $1 ILIKE ANY (ARRAY[
                 'test',
                 'bar'
             ]),
             false
         );
     $CODE$;

Que retorna truese $1corresponder 'test'ou 'bar'.

Como posso usar essa função em outra ANYconsulta? Por exemplo:

 SELECT myfun ANY(ARRAY['hello', 'world']);  -- should return false
 SELECT myfun ANY(ARRAY['hello', 'foo']);  -- should return true ('foo' matches)
 SELECT myfun ANY(ARRAY['test', 'world']);  -- should return true ('test matches')

Mas esta sintaxe está incorreta. E eu não sei muito bem como escrever isso corretamente.


editar: mais detalhes

Em essência, o que eu quero fazer é verificar se qualquer um de um conjunto de colunas contém qualquer um dos termos proibidos. Em outras palavras, tenho um conjunto de termos proibidos e preciso verificar várias colunas para conter essa palavra. Isso será usado posteriormente em uma política RLS. No exemplo abaixo, a função contains_forbidden_termé a função que eu gostaria de poder escrever.

Uma pequena, mas importante observação: As mesmas funções devem ser aplicadas a outras tabelas, mas com número variável de argumentos (daí o array como tipo de argumento).

CREATE TABLE foo (
    name TEXT,
    last_name TEXT
);
CREATE USER bob;
ALTER TABLE foo ENABLE ROW LEVEL SECURITY;

CREATE POLICY bob_foo ON foo TO bob USING (contains_forbidden_term(ARRAY[name, last_name]));

GRANT SELECT ON foo TO bob;
postgresql
  • 2 respostas
  • 211 Views
Martin Hope
exhuma
Asked: 2016-10-21 04:11:32 +0800 CST

Quais são as desvantagens de usar DOMAINS para armazenar metadados sobre a coluna?

  • 2

Me deparei com uma pergunta sobre um tipo de dados para valores MD5 . A recomendação nessa pergunta afirma usar uuidcomo tipo para esse campo.

A argumentação é sólida. Mas acho que isso pode ser confuso para alguém que não conhece os pontos revelados na pergunta acima. A decisão de usar uuidcomo tipo MD5 é algo que eu gostaria de ver em alguma forma de documentação em qualquer projeto que faça isso.

Como um "ajudante" para quem olha para o DB Schema, pode-se argumentar para criar um md5domínio herdado de uuid. Dessa forma, os tipos de coluna ficariam muito mais explícitos e a intenção ficaria muito mais clara.

Mas usaria um domínio apenas para "renomear" um tipo existente.

Ainda é algo que faria sentido documentar adequadamente. Mas essa documentação poderia ser centralizada em uma seção explicando os domínios no banco de dados. Então você não ganharia nada em termos de documentação. A vantagem que vejo é, como mencionei, que a intenção fica clara ao olhar a tabela DDL.

Há alguma desvantagem nisso?

postgresql domain
  • 1 respostas
  • 157 Views
Martin Hope
exhuma
Asked: 2015-11-23 08:02:48 +0800 CST

Quais partes compõem o pg_database_size?

  • 4

Estou tentando escrever um plug-in munin para representar graficamente os tamanhos do banco de dados. Juntamente com o uso pg_database_size, também quero representar graficamente seus componentes.

Até agora, cheguei ao seguinte:

SELECT
    SUM(pg_relation_size(oid, 'main')) AS main_size,
    SUM(pg_relation_size(oid, 'vm')) AS vm_size,
    SUM(pg_relation_size(oid, 'fsm')) AS fsm_size,
    SUM(
        CASE reltoastrelid
        WHEN 0 THEN 0
        ELSE pg_total_relation_size(reltoastrelid)
        END
    ) AS toast_size,
    SUM(pg_indexes_size(oid)) AS indexes_size
    FROM pg_class
    WHERE reltype != 0 -- 0=indices, covered by pg_indexes_size

No entanto, somar esses 5 valores me retorna algo que não é o mesmo que o resultado de pg_database_size. A diferença parece ser menos significativa para bancos de dados maiores.

Exemplo em um banco de dados maior:

┌──────────┬────────┬─────────┬─────────┬──────────┬───────────────┬──────────────────┬─────────┐
│   main   │   vm   │   fsm   │  toast  │ indexes  │ sum_of_values │ pg_database_size │  diff   │
├──────────┼────────┼─────────┼─────────┼──────────┼───────────────┼──────────────────┼─────────┤
│ 72441856 │ 753664 │ 2392064 │ 4677632 │ 41377792 │ 116 MB        │ 111 MB           │ 5222 kB │
└──────────┴────────┴─────────┴─────────┴──────────┴───────────────┴──────────────────┴─────────┘
(1 row)

Exemplo em um banco de dados menor:

┌─────────┬────────┬─────────┬────────┬─────────┬───────────────┬──────────────────┬─────────┐
│  main   │   vm   │   fsm   │ toast  │ indexes │ sum_of_values │ pg_database_size │  diff   │
├─────────┼────────┼─────────┼────────┼─────────┼───────────────┼──────────────────┼─────────┤
│ 2809856 │ 385024 │ 1351680 │ 557056 │ 2924544 │ 7840 kB       │ 6642 kB          │ 1198 kB │
└─────────┴────────┴─────────┴────────┴─────────┴───────────────┴──────────────────┴─────────┘
(1 row)

o que estou perdendo?

Talvez relacionado, talvez não: estou chocado ao ver o tamanho do índice. Eles são ENORMES. Algo na minha consulta está errado?


Aqui está um script que usei para inspecionar os diferentes valores:

SELECT
    SUM(pg_relation_size(oid, 'main')) AS main,
    SUM(pg_relation_size(oid, 'vm')) AS vm,
    SUM(pg_relation_size(oid, 'fsm')) AS fsm,
    SUM(
        CASE reltoastrelid
        WHEN 0 THEN 0
        ELSE pg_total_relation_size(reltoastrelid)
        END
    ) AS toast,
    SUM(pg_indexes_size(oid)) AS indexes,
    pg_size_pretty(
        SUM(pg_relation_size(oid, 'main'))::bigint +
        SUM(pg_relation_size(oid, 'vm'))::bigint +
        SUM(pg_relation_size(oid, 'fsm'))::bigint +
        SUM(pg_indexes_size(oid))::bigint +
        SUM(
            CASE reltoastrelid
            WHEN 0 THEN 0
            ELSE pg_total_relation_size(reltoastrelid)
            END
        )::bigint
    ) AS sum_of_values,
    pg_size_pretty(pg_database_size(current_database())) AS pg_database_size,

    pg_size_pretty(
        SUM(pg_relation_size(oid, 'main'))::bigint +
        SUM(pg_relation_size(oid, 'vm'))::bigint +
        SUM(pg_relation_size(oid, 'fsm'))::bigint +
        SUM(pg_indexes_size(oid))::bigint +
        SUM(
            CASE reltoastrelid
            WHEN 0 THEN 0
            ELSE pg_total_relation_size(reltoastrelid)
            END
        )::bigint - pg_database_size(current_database())::bigint
    ) AS diff

FROM pg_class
WHERE reltype != 0;
postgresql monitoring
  • 2 respostas
  • 2296 Views

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve