sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur kan jag komma åt ett standardvärde för Postgres-kolumnen med ActiveRecord?

När ActiveRecord behöver veta om en tabell gör den en fråga som liknar ditt informationsschema fråga men AR kommer att gå igenom PostgreSQL-specifika systemtabeller istället:

  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

Sök i PostgreSQL-adapterkällan för "regclass" och du kommer att se några andra frågor som AR kommer att använda för att ta reda på tabellens struktur.

pg_get_expr anropet i ovanstående fråga är varifrån kolumnens standardvärde kommer.

Resultaten av den frågan går, mer eller mindre, rakt in i 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

PostgreSQLColumn konstruktör kommer att använda extract_value_from_default att Ruby-ify standard; slutet av switchen i extract_value_from_default är intressant här:

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

Så om standardvärdet är bundet till en sekvens (som ett id kolumnen i PostgreSQL kommer att vara), då kommer standarden att komma ut från databasen som ett funktionsanrop liknande detta:

nextval('models_id_seq'::regclass)

Det kommer att hamna i ovanstående else filial och column.default.nil? kommer att vara sant.

För ett id kolumn detta är inte ett problem, AR förväntar sig att databasen tillhandahåller värdena för id kolumner så att det inte bryr sig vad standardvärdet är.

Detta är ett stort problem om kolumnens standard är något som AR inte förstår, säg ett funktionsanrop som t.ex. som md5(random()::text) . Problemet är att AR kommer att initiera alla attribut till sina standardvärden – som Model.columns ser dem, inte som databasen ser dem – när du säger Model.new . Till exempel, i konsolen ser du saker som detta:

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

Så om def_is_function faktiskt använder ett funktionsanrop som standardvärde, kommer AR att ignorera det och försöka infoga en NULL som kolumns värde. Den NULL kommer att förhindra att standardvärdet används och du kommer att sluta med en förvirrande röra. Standardinställningar som AR kan förstå (som strängar och siffror) fungerar dock bra.

Resultatet är att du inte riktigt kan använda icke-triviala standardkolumnvärden med ActiveRecord, om du vill ha ett icke-trivialt värde måste du göra i Ruby genom en av ActiveRecord-återkallningarna (som before_create ).

IMO skulle det vara mycket bättre om AR lämnade standardvärdena till databasen om den inte förstod dem:att lämna dem utanför INSERT eller använda DEFAULT i VALUES skulle ge mycket bättre resultat; AR skulle naturligtvis behöva ladda om nyskapade objekt från databasen för att få alla korrekta standardinställningar, men du skulle bara behöva ladda om det om det fanns standardvärden som AR inte förstod. Om annat i extract_value_from_default använde en speciell "Jag vet inte vad detta betyder"-flagga istället för nil då skulle villkoret "Jag måste ladda om det här objektet efter första sparandet" vara trivialt att upptäcka och du skulle bara ladda om det när det behövs.

Ovanstående är PostgreSQL-specifik men processen bör vara liknande för andra databaser; jag ger dock inga garantier.




  1. Hur man uppdaterar rader med ett slumpmässigt datum

  2. Enklaste sättet att aktivera PHP och MySQL på Mac OS 10.6 (Snow Leopard), 10.7 (Lion), 10.8 (Mountain Lion)?

  3. SQL-server anslut tabeller och pivot

  4. Totalt antal poster per vecka