Commit 6775e4a1 by Alexander Belozerov Committed by Cuong Tran

Indexes: add WHERE and USING (#482)

parent 5803fbaa
...@@ -65,6 +65,21 @@ module AnnotateModels ...@@ -65,6 +65,21 @@ module AnnotateModels
# Don't show default value for these column types # Don't show default value for these column types
NO_DEFAULT_COL_TYPES = %w(json jsonb hstore).freeze NO_DEFAULT_COL_TYPES = %w(json jsonb hstore).freeze
INDEX_CLAUSES = {
unique: {
default: 'UNIQUE',
markdown: '_unique_'
},
where: {
default: 'WHERE',
markdown: '_where_'
},
using: {
default: 'USING',
markdown: '_using_'
}
}.freeze
class << self class << self
def annotate_pattern(options = {}) def annotate_pattern(options = {})
if options[:wrapper_open] if options[:wrapper_open]
...@@ -356,12 +371,54 @@ module AnnotateModels ...@@ -356,12 +371,54 @@ module AnnotateModels
end end
end end
def index_unique_info(index, format = :default)
index.unique ? " #{INDEX_CLAUSES[:unique][format]}" : ''
end
def index_where_info(index, format = :default)
value = index.try(:where).try(:to_s)
if value.blank?
''
else
" #{INDEX_CLAUSES[:where][format]} #{value}"
end
end
def index_using_info(index, format = :default)
value = index.try(:using) && index.using.try(:to_sym)
if !value.blank? && value != :btree
" #{INDEX_CLAUSES[:using][format]} #{value}"
else
''
end
end
def final_index_string_in_markdown(index) def final_index_string_in_markdown(index)
sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", index_columns_info(index).join("`**\n# * **`")) details = sprintf(
"%s%s%s",
index_unique_info(index, :markdown),
index_where_info(index, :markdown),
index_using_info(index, :markdown)
).strip
details = " (#{details})" unless details.blank?
sprintf(
"# * `%s`%s:\n# * **`%s`**\n",
index.name,
details,
index_columns_info(index).join("`**\n# * **`")
)
end end
def final_index_string(index, max_size) def final_index_string(index, max_size)
sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index_columns_info(index).join(',')})", index.unique ? "UNIQUE" : "").rstrip + "\n" sprintf(
"# %-#{max_size}.#{max_size}s %s%s%s%s",
index.name,
"(#{index_columns_info(index).join(',')})",
index_unique_info(index),
index_where_info(index),
index_using_info(index)
).rstrip + "\n"
end end
def hide_limit?(col_type, options) def hide_limit?(col_type, options)
......
...@@ -5,12 +5,14 @@ require 'annotate/active_record_patch' ...@@ -5,12 +5,14 @@ require 'annotate/active_record_patch'
require 'active_support/core_ext/string' require 'active_support/core_ext/string'
describe AnnotateModels do describe AnnotateModels do
def mock_index(name, columns = [], orders = {}, unique = false) def mock_index(name, params = {})
double('IndexKeyDefinition', double('IndexKeyDefinition',
name: name, name: name,
columns: columns, columns: params[:columns] || [],
unique: unique, unique: params[:unique] || false,
orders: orders) orders: params[:orders] || {},
where: params[:where],
using: params[:using])
end end
def mock_foreign_key(name, from_column, to_table, to_column = 'id', constraints = {}) def mock_foreign_key(name, from_column, to_table, to_column = 'id', constraints = {})
...@@ -303,8 +305,8 @@ EOS ...@@ -303,8 +305,8 @@ EOS
[ [
mock_column(:id, :integer), mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer) mock_column(:foreign_thing_id, :integer)
], [mock_index('index_rails_02e851e3b7', ['id']), ], [mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', ['foreign_thing_id'])]) mock_index('index_rails_02e851e3b8', columns: ['foreign_thing_id'])])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info # Schema Info
# #
...@@ -331,10 +333,10 @@ EOS ...@@ -331,10 +333,10 @@ EOS
mock_column("value", :string) mock_column("value", :string)
], ],
[ [
mock_index('index_rails_02e851e3b7', ['id']), mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', mock_index('index_rails_02e851e3b8',
%w(firstname surname value), columns: %w(firstname surname value),
'surname' => :asc, 'value' => :desc) orders: { 'surname' => :asc, 'value' => :desc })
]) ])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info # Schema Info
...@@ -354,6 +356,72 @@ EOS ...@@ -354,6 +356,72 @@ EOS
EOS EOS
end end
it 'should get indexes keys with where clause' do
klass = mock_class(:users,
:id,
[
mock_column("id", :integer),
mock_column("firstname", :string),
mock_column("surname", :string),
mock_column("value", :string)
],
[
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
columns: %w(firstname surname),
where: 'value IS NOT NULL')
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# firstname :string not null
# surname :string not null
# value :string not null
#
# Indexes
#
# index_rails_02e851e3b7 (id)
# index_rails_02e851e3b8 (firstname,surname) WHERE value IS NOT NULL
#
EOS
end
it 'should get indexes keys with using clause other than btree' do
klass = mock_class(:users,
:id,
[
mock_column("id", :integer),
mock_column("firstname", :string),
mock_column("surname", :string),
mock_column("value", :string)
],
[
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
columns: %w(firstname surname),
using: 'hash')
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info
#
# Table name: users
#
# id :integer not null, primary key
# firstname :string not null
# surname :string not null
# value :string not null
#
# Indexes
#
# index_rails_02e851e3b7 (id)
# index_rails_02e851e3b8 (firstname,surname) USING hash
#
EOS
end
it 'should get simple indexes keys' do it 'should get simple indexes keys' do
klass = mock_class(:users, klass = mock_class(:users,
:id, :id,
...@@ -362,10 +430,10 @@ EOS ...@@ -362,10 +430,10 @@ EOS
mock_column(:foreign_thing_id, :integer) mock_column(:foreign_thing_id, :integer)
], ],
[ [
mock_index('index_rails_02e851e3b7', ['id']), mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', mock_index('index_rails_02e851e3b8',
['foreign_thing_id'], columns: ['foreign_thing_id'],
'foreign_thing_id' => :desc) orders: { 'foreign_thing_id' => :desc })
]) ])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS)
# Schema Info # Schema Info
...@@ -384,8 +452,8 @@ EOS ...@@ -384,8 +452,8 @@ EOS
[ [
mock_column("id", :integer), mock_column("id", :integer),
mock_column("name", :string) mock_column("name", :string)
], [mock_index('index_rails_02e851e3b7', ['id']), ], [mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', 'LOWER(name)')]) mock_index('index_rails_02e851e3b8', columns: 'LOWER(name)')])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS)
# Schema Info # Schema Info
# #
...@@ -501,10 +569,79 @@ EOS ...@@ -501,10 +569,79 @@ EOS
mock_column(:name, :string, limit: 50) mock_column(:name, :string, limit: 50)
], ],
[ [
mock_index('index_rails_02e851e3b7', ['id']), mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
columns: ['foreign_thing_id'])
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`name`** | `string(50)` | `not null`
#
# ### Indexes
#
# * `index_rails_02e851e3b7`:
# * **`id`**
# * `index_rails_02e851e3b8`:
# * **`foreign_thing_id`**
#
EOS
end
it 'should get schema info as Markdown with unique indexes' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
],
[
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', mock_index('index_rails_02e851e3b8',
['foreign_thing_id'], columns: ['foreign_thing_id'],
'foreign_thing_id' => :desc) unique: true)
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`name`** | `string(50)` | `not null`
#
# ### Indexes
#
# * `index_rails_02e851e3b7`:
# * **`id`**
# * `index_rails_02e851e3b8` (_unique_):
# * **`foreign_thing_id`**
#
EOS
end
it 'should get schema info as Markdown with ordered indexes' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
],
[
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
columns: ['foreign_thing_id'],
orders: { 'foreign_thing_id' => :desc })
]) ])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS) expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX} # #{AnnotateModels::PREFIX}
...@@ -528,6 +665,77 @@ EOS ...@@ -528,6 +665,77 @@ EOS
EOS EOS
end end
it 'should get schema info as Markdown with indexes with WHERE clause' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
],
[
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
columns: ['foreign_thing_id'],
unique: true,
where: 'name IS NOT NULL')
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`name`** | `string(50)` | `not null`
#
# ### Indexes
#
# * `index_rails_02e851e3b7`:
# * **`id`**
# * `index_rails_02e851e3b8` (_unique_ _where_ name IS NOT NULL):
# * **`foreign_thing_id`**
#
EOS
end
it 'should get schema info as Markdown with indexes with using clause other than btree' do
klass = mock_class(:users,
:id,
[
mock_column(:id, :integer),
mock_column(:name, :string, limit: 50)
],
[
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
columns: ['foreign_thing_id'],
using: 'hash')
])
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
# #{AnnotateModels::PREFIX}
#
# Table name: `users`
#
# ### Columns
#
# Name | Type | Attributes
# ----------- | ------------------ | ---------------------------
# **`id`** | `integer` | `not null, primary key`
# **`name`** | `string(50)` | `not null`
#
# ### Indexes
#
# * `index_rails_02e851e3b7`:
# * **`id`**
# * `index_rails_02e851e3b8` (_using_ hash):
# * **`foreign_thing_id`**
#
EOS
end
describe '#set_defaults' do describe '#set_defaults' do
it 'should default show_complete_foreign_keys to false' do it 'should default show_complete_foreign_keys to false' do
expect(Annotate.true?(ENV['show_complete_foreign_keys'])).to be(false) expect(Annotate.true?(ENV['show_complete_foreign_keys'])).to be(false)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment