1

J'ai une table héritée avec une expression par défaut dans un projet Rails. Cette expression sélectionne un nombre aléatoire à quatre chiffres et le convertit en chaîne. Au cours de mes tests, je suis en train de taper cette déclaration exacte dans psql pour définir l'expression par défaut:Pourquoi Postgres double-t-il le premier identificateur de fonction lorsque je définis une fonction par défaut pour une colonne?

alter table owners alter column signing_code set default 
substring(to_char(((random() * ((10000 - 1))::double precision) + 
(1)::double precision), '0000'::text), '....$'::text); 

Puis, quand je fais \d owners c'est ce que Postgres a décidé est mon expression par défaut réelle:

default "substring"(to_char(((random() * ((10000 - 1))::double 
precision) + (1)::double precision), '0000'::text), '....$'::text) 

Avis les guillemets autour du premier identificateur de fonction substring. Cela provoque deux problèmes avec les décharges schéma Rails/charges:

  1. Lorsque le schéma de dumping db/schema.rb, Ruby invalide est produit parce que les guillemets doubles ne sont pas échappés
  2. Même si vous échappez correctement les citations par main, lors du chargement du schéma de nouveau dans la base de données, Rails définit incorrectement l'expression entière comme une valeur de chaîne par défaut, pas une expression (il entoure l'expression avec des guillemets simples)

y at-il un moyen d'obtenir Postgres pas double citation la première fonction dans l'appel de fonction imbriqué dans mon cas? Ce serait une bonne solution de contournement, sinon je vais soumettre un bug avec le projet Rails.

Répondre

0

Pourquoi la fonction substring est entre guillemets n'a pas vraiment d'importance, les deux expressions sont fonctionnellement équivalentes. PostgreSQL analysera l'expression par défaut, puis sauvegardera la version analysée et pendant ce processus, l'identificateur substring sera doublé. Vous pouvez voir des choses semblables se produisent si vous ajoutez une contrainte CHECK comme ceci:

check (c in (1, 2, 3)) 

Faire un \d sur une table avec cette contrainte vous donnera:

CHECK (c = ANY (ARRAY[1, 2, 3])) 

PostgreSQL se traduit par l'expression in à un équivalent = ANY expression parce que c'est ce qu'il veut travailler avec en interne. Les deux expressions sont fonctionnellement équivalentes. Le vrai problème est qu'ActiveRecord ne sait pas quoi faire avec une expression par défaut qui est plus complexe qu'une seule valeur littérale. AR fait une hypothèse invalide sur ce à quoi ressemblent les défauts, puis se met aveuglément à faire des dégâts. Lorsque vous avez affaire à quelque chose au-delà du langage DDL SQL le plus simple, il est préférable d'utiliser db/structure.sql au lieu de db/schema.rb. structure.sql utilise les outils de vidage/restauration natifs de la base de données afin de comprendre et de préserver tout ce que la base de données connaît.

Le passage à structure.sql est assez simple:

  1. Mise à jour config/application.rb à utiliser le format:

    config.active_record.schema_format = :sql 
    
  2. Dump votre structure.sql en utilisant la tâche de râteau db:structure:dump.

  3. Supprimez db/schema.rb de votre arborescence source et le contrôle de révision.

  4. Ajouter db/structure.sql au contrôle de révision.

  5. Reformer vos doigts pour utiliser différentes tâches de rake pour le dumping et la restauration du schéma:

    • db:structure:dump au lieu de db:schema:dump.
    • db:structure:load au lieu de db:schema:load.

Je commence toujours avec structure.sql comme schema.rb est trop limité pour ce que je veux travailler avec mes bases de données.