Skip to content

Indexes: add WHERE and USING #482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions lib/annotate/annotate_models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ module AnnotateModels
# Don't show default value for these column types
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
def annotate_pattern(options = {})
if options[:wrapper_open]
Expand Down Expand Up @@ -356,12 +371,54 @@ def index_columns_info(index)
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)
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

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

def hide_limit?(col_type, options)
Expand Down
242 changes: 225 additions & 17 deletions spec/annotate/annotate_models_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
require 'active_support/core_ext/string'

describe AnnotateModels do
def mock_index(name, columns = [], orders = {}, unique = false)
def mock_index(name, params = {})
double('IndexKeyDefinition',
name: name,
columns: columns,
unique: unique,
orders: orders)
columns: params[:columns] || [],
unique: params[:unique] || false,
orders: params[:orders] || {},
where: params[:where],
using: params[:using])
end

def mock_foreign_key(name, from_column, to_table, to_column = 'id', constraints = {})
Expand Down Expand Up @@ -303,8 +305,8 @@ def mock_column(name, type, options = {})
[
mock_column(:id, :integer),
mock_column(:foreign_thing_id, :integer)
], [mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b8', ['foreign_thing_id'])])
], [mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', columns: ['foreign_thing_id'])])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info
#
Expand All @@ -331,10 +333,10 @@ def mock_column(name, type, options = {})
mock_column("value", :string)
],
[
mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
%w(firstname surname value),
'surname' => :asc, 'value' => :desc)
columns: %w(firstname surname value),
orders: { 'surname' => :asc, 'value' => :desc })
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
# Schema Info
Expand All @@ -354,6 +356,72 @@ def mock_column(name, type, options = {})
EOS
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
klass = mock_class(:users,
:id,
Expand All @@ -362,10 +430,10 @@ def mock_column(name, type, options = {})
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'],
'foreign_thing_id' => :desc)
columns: ['foreign_thing_id'],
orders: { 'foreign_thing_id' => :desc })
])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS)
# Schema Info
Expand All @@ -384,8 +452,8 @@ def mock_column(name, type, options = {})
[
mock_column("id", :integer),
mock_column("name", :string)
], [mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b8', 'LOWER(name)')])
], [mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8', columns: 'LOWER(name)')])
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', simple_indexes: true)).to eql(<<-EOS)
# Schema Info
#
Expand Down Expand Up @@ -501,10 +569,79 @@ def mock_column(name, type, options = {})
mock_column(:name, :string, limit: 50)
],
[
mock_index('index_rails_02e851e3b7', ['id']),
mock_index('index_rails_02e851e3b7', columns: ['id']),
mock_index('index_rails_02e851e3b8',
['foreign_thing_id'],
'foreign_thing_id' => :desc)
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',
columns: ['foreign_thing_id'],
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)
# #{AnnotateModels::PREFIX}
Expand All @@ -528,6 +665,77 @@ def mock_column(name, type, options = {})
EOS
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
it 'should default show_complete_foreign_keys to false' do
expect(Annotate.true?(ENV['show_complete_foreign_keys'])).to be(false)
Expand Down