sql >> Database teknologi >  >> RDS >> PostgreSQL

Hvordan får jeg adgang til en Postgres-kolonnestandardværdi ved hjælp af ActiveRecord?

Når ActiveRecord har brug for at kende til en tabel, udfører den en forespørgsel svarende til dit informationsskema forespørgsel, men AR vil gå gennem PostgreSQL-specifikke systemtabeller i stedet:

  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øg i PostgreSQL-adapterkilden for "regclass", og du vil se nogle andre forespørgsler, som AR vil bruge til at finde ud af tabellens struktur.

pg_get_expr kaldet i ovenstående forespørgsel er det sted, hvor kolonnens standardværdi kommer fra.

Resultaterne af den forespørgsel går, mere eller mindre, lige ind 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 vil bruge kode>udtræk_værdi_fra_standard at Ruby-ify standarden; slutningen af kontakten i extract_value_from_default er interessant her:

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å hvis standardværdien er bundet til en sekvens (som er et id kolonne i PostgreSQL vil være), så vil standarden komme ud af databasen som et funktionskald svarende til dette:

nextval('models_id_seq'::regclass)

Det ender i ovenstående else branch og column.default.nil? vil være sandt.

For et id kolonne dette er ikke et problem, AR forventer, at databasen leverer værdierne for id kolonner, så det er ligeglad med, hvad standardværdien er.

Dette er et stort problem, hvis kolonnens standard er noget, som AR ikke forstår, sig et funktionskald f.eks. som md5(random()::tekst) . Problemet er, at AR vil initialisere alle attributterne til deres standardværdier – som Model.columns ser dem, ikke som databasen ser dem – når du siger Model.new . For eksempel, i konsollen vil du se ting som dette:

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

Så hvis def_is_function faktisk bruger et funktionskald som standardværdi, vil AR ignorere det og forsøge at indsætte en NULL som kolonnens værdi. Den NULL forhindrer standardværdien i at blive brugt, og du ender med et forvirrende rod. Standarder, som AR kan forstå (såsom strenge og tal) fungerer dog fint.

Resultatet er, at du ikke rigtig kan bruge ikke-trivielle standardkolonneværdier med ActiveRecord, hvis du vil have en ikke-triviel værdi, skal du gøre det i Ruby gennem et af ActiveRecord-tilbagekaldene (såsom before_create ).

IMO ville det være meget bedre, hvis AR overlod standardværdierne til databasen, hvis den ikke forstod dem:at udelade dem fra INSERT eller bruge DEFAULT i VALUES ville give meget bedre resultater; AR ville selvfølgelig skulle genindlæse nyoprettede objekter fra databasen for at få alle de korrekte standardindstillinger, men du ville kun have brug for genindlæsningen, hvis der var standarder, som AR ikke forstod. Hvis else i extract_value_from_default brugt et specielt "Jeg ved ikke, hvad det betyder"-flag i stedet for nul så ville betingelsen "Jeg skal genindlæse dette objekt efter den første lagring" være triviel at opdage, og du vil kun genindlæse, når det er nødvendigt.

Ovenstående er PostgreSQL-specifik, men processen bør være ens for andre databaser; jeg giver dog ingen garantier.




  1. Find antallet af rækker i den første tabel, der matcher id'erne for rækken i den anden tabel med maks. dato

  2. Konverter 'datetime' til 'date' i SQL Server (T-SQL-eksempler)

  3. SQL Server Konverter heltal til binær streng

  4. Hvordan opretter og forespørger jeg linkede databaseservere i SQL Server?