Module: ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
- Defined in:
- lib/updateable_views_inheritance/postgresql_adapter.rb
Defined Under Namespace
Modules: Tutuf
Instance Method Summary collapse
-
#create_child(child_view, options) ⇒ Object
Use this in migration to create child table and view.
-
#create_child_view(parent_table, child_view, child_table = nil, child_table_pk = nil) ⇒ Object
Creates aggregate updateable view of parent and child relations.
-
#create_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view = nil) ⇒ Object
Creates Single Table Inheritanche-like aggregate view called
sti_aggregate_view
forparent_relation
and all its descendants. -
#drop_child(child_view) ⇒ Object
Drop child view and table.
-
#drop_view(name) ⇒ Object
Drops a view from the database.
-
#is_view?(name) ⇒ Boolean
Checks whether relation
name
is a view. -
#pk_and_sequence_for(relation) ⇒ Object
Returns a relation’s primary key and belonging sequence.
- #primary_key(relation) ⇒ Object
-
#rebuild_all_parent_and_children_views ⇒ Object
Recreates all views in all hierarchy chains.
-
#rebuild_parent_and_children_views(parent_relation) ⇒ Object
Recreates views in the part of the hierarchy chain starting from the
parent_relation
. -
#rebuild_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view = nil) ⇒ Object
Recreates the Single_Table_Inheritanche-like aggregate view
sti_aggregate_view
forparent_relation
and all its descendants. -
#remove_parent_and_children_views(parent_relation) ⇒ Object
Recursively delete
parent_relation
(if it is a view) and the children views the depend on it. -
#reset_pk_sequence!(table, pk = nil, sequence = nil) ⇒ Object
Resets sequence to the max value of the table’s pk if present respecting inheritance (i.e. one sequence can be shared by many tables).
-
#set_defaults(view_name, table_name) ⇒ Object
Set default values from the table columns for a view.
-
#supports_disable_referential_integrity? ⇒ Boolean
Overriden - it solargraph-must return false, otherwise deleting fixtures won’t work.
- #table_exists_with_updateable_views_inheritance_support?(name) ⇒ Boolean
-
#views(name = nil) ⇒ Object
Return the list of all views in the schema search path.
Instance Method Details
#create_child(child_view, options) ⇒ Object
Use this in migration to create child table and view. Options:
- :parent
-
Parent relation
- :table
-
Deprecated. Use :child_table instead
- :child_table
-
Default is
"#{child_view}_data"
- :child_table_pk
-
Handy when :child_table is a view and PK cannot be inferred from the database.
- :skip_creating_child_table
-
When given, :child_table option also must be specified
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 20 def create_child(child_view, ) raise 'Please call me with a parent, for example: create_child(:steam_locomotives, :parent => :locomotives)' unless [:parent] parent_relation = [:parent].to_s parent_table = if is_view?(parent_relation) # interpreted as inheritance chain deeper than two levels query(<<~SQL)[0][0] SELECT child_relation FROM updateable_views_inheritance WHERE child_aggregate_view = #{quote(parent_relation)} SQL else parent_relation end child_table = [:child_table] || [:table] || "#{child_view}_data" child_table_pk = [:child_table_pk].to_s if [:child_table_pk] unless .key?(:skip_creating_child_table) unqualified_child_view_name = Utils.extract_schema_qualified_name(child_view).identifier child_table_pk ||= "#{unqualified_child_view_name.singularize}_id" create_table(child_table, id: false) do |t| t.integer child_table_pk, null: false yield t end execute "ALTER TABLE #{child_table} ADD PRIMARY KEY (#{child_table_pk})" execute "ALTER TABLE #{child_table} ADD FOREIGN KEY (#{child_table_pk}) REFERENCES #{parent_table} ON DELETE CASCADE ON UPDATE CASCADE" end create_child_view(parent_relation, child_view, child_table, child_table_pk) end |
#create_child_view(parent_table, child_view, child_table = nil, child_table_pk = nil) ⇒ Object
Creates aggregate updateable view of parent and child relations. The convention for naming child tables is "#{child_view}_data"
. If you don’t follow it, supply child_table_name
as third argument.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 63 def create_child_view(parent_table, child_view, child_table = nil, child_table_pk = nil) child_table ||= "#{child_view}_data" parent_columns = columns(parent_table) child_columns = columns(child_table) child_column_names = child_columns.map(&:name) parent_column_names = parent_columns.map(&:name) child_pk = child_table_pk || pk_and_sequence_for(child_table)[0] child_column_names.delete(child_pk) parent_pk, parent_pk_seq = pk_and_sequence_for(parent_table) parent_column_names.delete(parent_pk) do_create_child_view(parent_table, parent_column_names, parent_pk, child_view, child_column_names, child_pk, child_table) make_child_view_updateable(parent_table, parent_column_names, parent_pk, parent_pk_seq, child_view, child_column_names, child_pk, child_table) # assign default values for table columns on the view - it is not automatic in Postgresql 8.1 set_defaults(child_view, parent_table) set_defaults(child_view, child_table) create_system_table_records(parent_table, child_view, child_table) end |
#create_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view = nil) ⇒ Object
Creates Single Table Inheritanche-like aggregate view called sti_aggregate_view
for parent_relation
and all its descendants. The view isn’t updateable. The order of all or just the first few columns in the aggregate view can be explicitly set by passing array of column names as third argument. If there are columns with the same name but different types in two or more relations they will appear as a single column of type text
in the view.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 224 def create_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view = nil) columns_for_view ||= [] relations_heirarchy = get_view_hierarchy_for(parent_relation) relations = relations_heirarchy.flatten leaves_relations = get_leaves_relations(relations_heirarchy) all_columns = leaves_relations.map{|rel| columns(rel)}.flatten columns_hash = {} conflict_column_names = [] all_columns.each do |col| c = columns_hash[col.name] if(c && col.sql_type != c.sql_type) conflict_column_names << col.name else columns_hash[col.name] = col end end conflict_column_names = conflict_column_names.uniq.sort if !conflict_column_names.empty? sorted_column_names = (columns_for_view + columns_hash.keys.sort).uniq parent_klass_name = Tutuf::ClassTableReflection.get_klass_for_table(parent_relation) quoted_inheritance_column = quote_column_name(parent_klass_name.inheritance_column) queries = relations.map{|rel| generate_single_table_inheritanche_union_clause(rel, sorted_column_names, conflict_column_names, columns_hash, quoted_inheritance_column)} unioin_clauses = queries.join("\n UNION ") execute <<-SQL CREATE VIEW #{sti_aggregate_view} AS ( #{unioin_clauses} ) SQL end |
#drop_child(child_view) ⇒ Object
Drop child view and table
54 55 56 57 58 59 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 54 def drop_child(child_view) drop_view(child_view) child_table = query("SELECT child_relation FROM updateable_views_inheritance WHERE child_aggregate_view = #{quote(child_view)}")[0][0] drop_table(child_table) execute "DELETE FROM updateable_views_inheritance WHERE child_aggregate_view = #{quote(child_view)}" end |
#drop_view(name) ⇒ Object
Drops a view from the database.
149 150 151 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 149 def drop_view(name) execute "DROP VIEW #{quote_table_name(name)}" end |
#is_view?(name) ⇒ Boolean
Checks whether relation name
is a view.
164 165 166 167 168 169 170 171 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 164 def is_view?(name) result = query(<<~SQL, name).map { |row| row[0] } SELECT viewname FROM pg_views WHERE viewname = '#{name}' SQL !result.empty? end |
#pk_and_sequence_for(relation) ⇒ Object
Returns a relation’s primary key and belonging sequence. If relation
is a table the result is its PK and sequence. When it is a view, PK and sequence of the table at the root of the inheritance chain are returned.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 119 def pk_and_sequence_for(relation) result = query(<<-SQL.squish, 'PK')[0] SELECT attr.attname FROM pg_attribute attr, pg_constraint cons WHERE cons.conrelid = attr.attrelid AND cons.conrelid = '#{relation}'::regclass AND cons.contype = 'p' AND attr.attnum = ANY(cons.conkey) SQL if result.nil? or result.empty? parent = parent_table(relation) pk_and_sequence_for(parent) if parent else pk = result[0] sequence = query("SELECT pg_get_serial_sequence('#{relation}', '#{result[0]}') ")[0][0] if sequence # ActiveRecord expects PostgreSQL::Name object as sequence, not a string sequence_with_schema = Utils.extract_schema_qualified_name(sequence) [pk, sequence_with_schema] else [pk, nil] end end rescue nil end |
#primary_key(relation) ⇒ Object
110 111 112 113 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 110 def primary_key(relation) res = pk_and_sequence_for(relation) res && res.first end |
#rebuild_all_parent_and_children_views ⇒ Object
Recreates all views in all hierarchy chains
189 190 191 192 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 189 def rebuild_all_parent_and_children_views parent_relations = select_values('SELECT DISTINCT parent_relation FROM updateable_views_inheritance') parent_relations.each { |parent_relation| rebuild_parent_and_children_views(parent_relation) } end |
#rebuild_parent_and_children_views(parent_relation) ⇒ Object
Recreates views in the part of the hierarchy chain starting from the parent_relation
.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 195 def rebuild_parent_and_children_views(parent_relation) # Current implementation is not very efficient - it can drop and recreate one and the same view in the bottom of the hierarchy many times. remove_parent_and_children_views(parent_relation) children = query(<<-SQL) SELECT parent_relation, child_aggregate_view, child_relation FROM updateable_views_inheritance WHERE parent_relation = '#{parent_relation}' SQL #if the parent is in the middle of the inheritance chain, it's a view that should be rebuilt as well parent = query(<<-SQL)[0] SELECT parent_relation, child_aggregate_view, child_relation FROM updateable_views_inheritance WHERE child_aggregate_view = '#{parent_relation}' SQL create_child_view(parent[0], parent[1], parent[2]) if (parent && !parent.empty?) children.each do |child| create_child_view(child[0], child[1], child[2]) rebuild_parent_and_children_views(child[1]) end end |
#rebuild_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view = nil) ⇒ Object
Recreates the Single_Table_Inheritanche-like aggregate view sti_aggregate_view
for parent_relation
and all its descendants.
255 256 257 258 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 255 def rebuild_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view = nil) drop_view(sti_aggregate_view) create_single_table_inheritance_view(sti_aggregate_view, parent_relation, columns_for_view) end |
#remove_parent_and_children_views(parent_relation) ⇒ Object
Recursively delete parent_relation
(if it is a view) and the children views the depend on it.
174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 174 def remove_parent_and_children_views(parent_relation) children_views = query(<<-SQL).map{|row| row[0]} SELECT child_aggregate_view FROM updateable_views_inheritance WHERE parent_relation = '#{parent_relation}' SQL children_views.each do |cv| remove_parent_and_children_views(cv) # drop the view only if it wasn't dropped beforehand in recursive call from other method. drop_view(cv) if is_view?(cv) end drop_view(parent_relation) if is_view?(parent_relation) end |
#reset_pk_sequence!(table, pk = nil, sequence = nil) ⇒ Object
Resets sequence to the max value of the table’s pk if present respecting inheritance (i.e. one sequence can be shared by many tables).
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 88 def reset_pk_sequence!(table, pk = nil, sequence = nil) parent = parent_table(table) if parent reset_pk_sequence!(parent, pk, sequence) else unless pk and sequence default_pk, default_sequence = pk_and_sequence_for(table) pk ||= default_pk sequence ||= default_sequence end if pk if sequence select_value <<-SQL, 'Reset sequence' SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false) SQL else @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger end end end end |
#set_defaults(view_name, table_name) ⇒ Object
Set default values from the table columns for a view
311 312 313 314 315 316 317 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 311 def set_defaults(view_name, table_name) column_definitions(table_name).each do |column_name, type, default, notnull| if !default.nil? execute("ALTER TABLE #{quote_table_name(view_name)} ALTER #{quote_column_name(column_name)} SET DEFAULT #{default}") end end end |
#supports_disable_referential_integrity? ⇒ Boolean
Overriden - it solargraph-must return false, otherwise deleting fixtures won’t work
261 262 263 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 261 def supports_disable_referential_integrity? false end |
#table_exists_with_updateable_views_inheritance_support?(name) ⇒ Boolean
265 266 267 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 265 def table_exists_with_updateable_views_inheritance_support?(name) is_view?(name) ? true : table_exists_without_updateable_views_inheritance_support?(name) end |
#views(name = nil) ⇒ Object
Return the list of all views in the schema search path.
154 155 156 157 158 159 160 161 |
# File 'lib/updateable_views_inheritance/postgresql_adapter.rb', line 154 def views(name=nil) schemas = schema_search_path.split(/,\s*/).map { |p| quote(p) }.join(',') query(<<~SQL, name).map { |row| row[0] } SELECT viewname FROM pg_views WHERE schemaname IN (#{schemas}) SQL end |