sql >> Database >  >> RDS >> PostgreSQL

Hoe krijg ik toegang tot een standaardwaarde van een Postgres-kolom met ActiveRecord?

Wanneer ActiveRecord iets moet weten over een tabel, voert het een query uit die lijkt op uw information_schema query, maar AR gaat door de PostgreSQL-specifieke systeemtabellen in plaats daarvan:

  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
         pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
    FROM pg_attribute a LEFT JOIN pg_attrdef d
      ON a.attrelid = d.adrelid AND a.attnum = d.adnum
   WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
     AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum

Zoek in de PostgreSQL-adapterbron voor "regclass" en je zult enkele andere zoekopdrachten zien die AR zal gebruiken om de structuur van de tabel te achterhalen.

De pg_get_expr oproep in de bovenstaande query is waar de standaardwaarde van de kolom vandaan komt.

De resultaten van die zoekopdracht zijn min of meer rechtstreeks naar PostgreSQLColumn.new :

def columns(table_name, name = nil)
  # Limit, precision, and scale are all handled by the superclass.
  column_definitions(table_name).collect do |column_name, type, default, notnull|
    PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
  end
end

De PostgreSQLColumn constructeur gebruikt extract_value_from_default om de standaard te Ruby-ify; het einde van de switch in extract_value_from_default is hier interessant:

else
  # Anything else is blank, some user type, or some function
  # and we can't know the value of that, so return nil.
  nil

Dus als de standaardwaarde is gebonden aan een reeks (die een id kolom in PostgreSQL zal zijn), dan komt de standaard uit de database als een functieaanroep die er ongeveer zo uitziet:

nextval('models_id_seq'::regclass)

Dat zal eindigen in de bovenstaande else branch en column.default.nil? zal waar zijn.

Voor een id kolom is dit geen probleem, AR verwacht dat de database de waarden levert voor id kolommen, dus het maakt niet uit wat de standaardwaarde is.

Dit is een groot probleem als de standaardinstelling van de kolom iets is dat AR niet begrijpt, zeg een functieaanroep zoals als md5(random()::text) . Het probleem is dat AR alle attributen initialiseert naar hun standaardwaarden - als Model.columns ziet ze, niet zoals de database ze ziet – als je zegt Model.new . In de console ziet u bijvoorbeeld dit soort dingen:

 > Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>

Dus als def_is_function daadwerkelijk een functie-aanroep als standaardwaarde gebruikt, zal AR dat negeren en proberen een NULL in te voegen als de waarde van die kolom. Die NULL voorkomt dat de standaardwaarde wordt gebruikt en je krijgt een verwarrende puinhoop. Standaardinstellingen die AR kan begrijpen (zoals tekenreeksen en cijfers) werken echter prima.

Het resultaat is dat je niet echt niet-triviale standaard kolomwaarden kunt gebruiken met ActiveRecord, als je een niet-triviale waarde wilt, dan moet je dat in Ruby doen via een van de ActiveRecord-callbacks (zoals before_create ).

IMO zou het veel beter zijn als AR de standaardwaarden aan de database zou overlaten als het ze niet begreep:ze uit de INSERT weglaten of DEFAULT gebruiken in de VALUES zou veel betere resultaten opleveren; AR zou natuurlijk nieuw gemaakte objecten uit de database opnieuw moeten laden om alle juiste standaardwaarden te krijgen, maar je zou het opnieuw moeten laden als er standaardwaarden waren die AR niet begreep. Als de else in extract_value_from_default gebruikte een speciale "ik weet niet wat dit betekent" vlag in plaats van nil dan zou de voorwaarde "Ik moet dit object opnieuw laden na de eerste keer opslaan" triviaal zijn om te detecteren en zou je alleen opnieuw moeten laden als dat nodig is.

Het bovenstaande is PostgreSQL-specifiek, maar het proces zou vergelijkbaar moeten zijn voor andere databases; ik geef echter geen garanties.




  1. PostgreSQL en vergrendeling

  2. Mysql Regex om 0 te vervangen van ipv4

  3. Alleen invoeging toestaan ​​vanuit een trigger

  4. Waarom het afstemmen van SQL-prestaties de belangrijkste vaardigheid voor databasebeheer is