Skip to content

Commit 2d4010c

Browse files
Indexes: add WHERE and USING
1 parent 5803fba commit 2d4010c

File tree

2 files changed

+280
-5
lines changed

2 files changed

+280
-5
lines changed

lib/annotate/annotate_models.rb

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ module AnnotateModels
6565
# Don't show default value for these column types
6666
NO_DEFAULT_COL_TYPES = %w(json jsonb hstore).freeze
6767

68+
INDEX_CLAUSES = {
69+
unique: {
70+
default: 'UNIQUE',
71+
markdown: '_unique_'
72+
},
73+
where: {
74+
default: 'WHERE',
75+
markdown: '_where_'
76+
},
77+
using: {
78+
default: 'USING',
79+
markdown: '_using_'
80+
}
81+
}.freeze
82+
6883
class << self
6984
def annotate_pattern(options = {})
7085
if options[:wrapper_open]
@@ -356,12 +371,54 @@ def index_columns_info(index)
356371
end
357372
end
358373

374+
def index_unique_info(index, format = :default)
375+
index.unique ? " #{INDEX_CLAUSES[:unique][format]}" : ''
376+
end
377+
378+
def index_where_info(index, format = :default)
379+
value = index.try(:where).try(:to_s)
380+
if value.blank?
381+
''
382+
else
383+
" #{INDEX_CLAUSES[:where][format]} #{value}"
384+
end
385+
end
386+
387+
def index_using_info(index, format = :default)
388+
value = index.try(:using) && index.using.try(:to_sym)
389+
if !value.blank? && value != :btree
390+
" #{INDEX_CLAUSES[:using][format]} #{value}"
391+
else
392+
''
393+
end
394+
end
395+
359396
def final_index_string_in_markdown(index)
360-
sprintf("# * `%s`%s:\n# * **`%s`**\n", index.name, index.unique ? " (_unique_)" : "", index_columns_info(index).join("`**\n# * **`"))
397+
details = sprintf(
398+
"%s%s%s",
399+
index_unique_info(index, :markdown),
400+
index_where_info(index, :markdown),
401+
index_using_info(index, :markdown)
402+
).strip
403+
details = " (#{details})" unless details.blank?
404+
405+
sprintf(
406+
"# * `%s`%s:\n# * **`%s`**\n",
407+
index.name,
408+
details,
409+
index_columns_info(index).join("`**\n# * **`")
410+
)
361411
end
362412

363413
def final_index_string(index, max_size)
364-
sprintf("# %-#{max_size}.#{max_size}s %s %s", index.name, "(#{index_columns_info(index).join(',')})", index.unique ? "UNIQUE" : "").rstrip + "\n"
414+
sprintf(
415+
"# %-#{max_size}.#{max_size}s %s%s%s%s",
416+
index.name,
417+
"(#{index_columns_info(index).join(',')})",
418+
index_unique_info(index),
419+
index_where_info(index),
420+
index_using_info(index)
421+
).rstrip + "\n"
365422
end
366423

367424
def hide_limit?(col_type, options)

spec/annotate/annotate_models_spec.rb

Lines changed: 221 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
require 'active_support/core_ext/string'
66

77
describe AnnotateModels do
8-
def mock_index(name, columns = [], orders = {}, unique = false)
8+
def mock_index(name, columns = [], orders = {}, unique = false, where = nil, using = nil)
99
double('IndexKeyDefinition',
1010
name: name,
1111
columns: columns,
1212
unique: unique,
13-
orders: orders)
13+
orders: orders,
14+
where: where,
15+
using: using)
1416
end
1517

1618
def mock_foreign_key(name, from_column, to_table, to_column = 'id', constraints = {})
@@ -354,6 +356,77 @@ def mock_column(name, type, options = {})
354356
EOS
355357
end
356358

359+
it 'should get indexes keys with where clause' do
360+
klass = mock_class(:users,
361+
:id,
362+
[
363+
mock_column("id", :integer),
364+
mock_column("firstname", :string),
365+
mock_column("surname", :string),
366+
mock_column("value", :string)
367+
],
368+
[
369+
mock_index('index_rails_02e851e3b7', ['id']),
370+
mock_index('index_rails_02e851e3b8',
371+
%w(firstname surname),
372+
{},
373+
false,
374+
'value IS NOT NULL')
375+
])
376+
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
377+
# Schema Info
378+
#
379+
# Table name: users
380+
#
381+
# id :integer not null, primary key
382+
# firstname :string not null
383+
# surname :string not null
384+
# value :string not null
385+
#
386+
# Indexes
387+
#
388+
# index_rails_02e851e3b7 (id)
389+
# index_rails_02e851e3b8 (firstname,surname) WHERE value IS NOT NULL
390+
#
391+
EOS
392+
end
393+
394+
it 'should get indexes keys with using clause other than btree' do
395+
klass = mock_class(:users,
396+
:id,
397+
[
398+
mock_column("id", :integer),
399+
mock_column("firstname", :string),
400+
mock_column("surname", :string),
401+
mock_column("value", :string)
402+
],
403+
[
404+
mock_index('index_rails_02e851e3b7', ['id']),
405+
mock_index('index_rails_02e851e3b8',
406+
%w(firstname surname),
407+
{},
408+
false,
409+
nil,
410+
'hash')
411+
])
412+
expect(AnnotateModels.get_schema_info(klass, 'Schema Info', show_indexes: true)).to eql(<<-EOS)
413+
# Schema Info
414+
#
415+
# Table name: users
416+
#
417+
# id :integer not null, primary key
418+
# firstname :string not null
419+
# surname :string not null
420+
# value :string not null
421+
#
422+
# Indexes
423+
#
424+
# index_rails_02e851e3b7 (id)
425+
# index_rails_02e851e3b8 (firstname,surname) USING hash
426+
#
427+
EOS
428+
end
429+
357430
it 'should get simple indexes keys' do
358431
klass = mock_class(:users,
359432
:id,
@@ -494,6 +567,40 @@ def mock_column(name, type, options = {})
494567
end
495568

496569
it 'should get schema info as Markdown with indexes' do
570+
klass = mock_class(:users,
571+
:id,
572+
[
573+
mock_column(:id, :integer),
574+
mock_column(:name, :string, limit: 50)
575+
],
576+
[
577+
mock_index('index_rails_02e851e3b7', ['id']),
578+
mock_index('index_rails_02e851e3b8',
579+
['foreign_thing_id'])
580+
])
581+
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
582+
# #{AnnotateModels::PREFIX}
583+
#
584+
# Table name: `users`
585+
#
586+
# ### Columns
587+
#
588+
# Name | Type | Attributes
589+
# ----------- | ------------------ | ---------------------------
590+
# **`id`** | `integer` | `not null, primary key`
591+
# **`name`** | `string(50)` | `not null`
592+
#
593+
# ### Indexes
594+
#
595+
# * `index_rails_02e851e3b7`:
596+
# * **`id`**
597+
# * `index_rails_02e851e3b8`:
598+
# * **`foreign_thing_id`**
599+
#
600+
EOS
601+
end
602+
603+
it 'should get schema info as Markdown with unique indexes' do
497604
klass = mock_class(:users,
498605
:id,
499606
[
@@ -504,7 +611,43 @@ def mock_column(name, type, options = {})
504611
mock_index('index_rails_02e851e3b7', ['id']),
505612
mock_index('index_rails_02e851e3b8',
506613
['foreign_thing_id'],
507-
'foreign_thing_id' => :desc)
614+
{},
615+
true)
616+
])
617+
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
618+
# #{AnnotateModels::PREFIX}
619+
#
620+
# Table name: `users`
621+
#
622+
# ### Columns
623+
#
624+
# Name | Type | Attributes
625+
# ----------- | ------------------ | ---------------------------
626+
# **`id`** | `integer` | `not null, primary key`
627+
# **`name`** | `string(50)` | `not null`
628+
#
629+
# ### Indexes
630+
#
631+
# * `index_rails_02e851e3b7`:
632+
# * **`id`**
633+
# * `index_rails_02e851e3b8` (_unique_):
634+
# * **`foreign_thing_id`**
635+
#
636+
EOS
637+
end
638+
639+
it 'should get schema info as Markdown with ordered indexes' do
640+
klass = mock_class(:users,
641+
:id,
642+
[
643+
mock_column(:id, :integer),
644+
mock_column(:name, :string, limit: 50)
645+
],
646+
[
647+
mock_index('index_rails_02e851e3b7', ['id']),
648+
mock_index('index_rails_02e851e3b8',
649+
['foreign_thing_id'],
650+
{'foreign_thing_id' => :desc})
508651
])
509652
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
510653
# #{AnnotateModels::PREFIX}
@@ -528,6 +671,81 @@ def mock_column(name, type, options = {})
528671
EOS
529672
end
530673

674+
it 'should get schema info as Markdown with indexes with WHERE clause' do
675+
klass = mock_class(:users,
676+
:id,
677+
[
678+
mock_column(:id, :integer),
679+
mock_column(:name, :string, limit: 50)
680+
],
681+
[
682+
mock_index('index_rails_02e851e3b7', ['id']),
683+
mock_index('index_rails_02e851e3b8',
684+
['foreign_thing_id'],
685+
{},
686+
true,
687+
'name IS NOT NULL')
688+
])
689+
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
690+
# #{AnnotateModels::PREFIX}
691+
#
692+
# Table name: `users`
693+
#
694+
# ### Columns
695+
#
696+
# Name | Type | Attributes
697+
# ----------- | ------------------ | ---------------------------
698+
# **`id`** | `integer` | `not null, primary key`
699+
# **`name`** | `string(50)` | `not null`
700+
#
701+
# ### Indexes
702+
#
703+
# * `index_rails_02e851e3b7`:
704+
# * **`id`**
705+
# * `index_rails_02e851e3b8` (_unique_ _where_ name IS NOT NULL):
706+
# * **`foreign_thing_id`**
707+
#
708+
EOS
709+
end
710+
711+
it 'should get schema info as Markdown with indexes with using clause other than btree' do
712+
klass = mock_class(:users,
713+
:id,
714+
[
715+
mock_column(:id, :integer),
716+
mock_column(:name, :string, limit: 50)
717+
],
718+
[
719+
mock_index('index_rails_02e851e3b7', ['id']),
720+
mock_index('index_rails_02e851e3b8',
721+
['foreign_thing_id'],
722+
{},
723+
false,
724+
nil,
725+
'hash')
726+
])
727+
expect(AnnotateModels.get_schema_info(klass, AnnotateModels::PREFIX, format_markdown: true, show_indexes: true)).to eql(<<-EOS)
728+
# #{AnnotateModels::PREFIX}
729+
#
730+
# Table name: `users`
731+
#
732+
# ### Columns
733+
#
734+
# Name | Type | Attributes
735+
# ----------- | ------------------ | ---------------------------
736+
# **`id`** | `integer` | `not null, primary key`
737+
# **`name`** | `string(50)` | `not null`
738+
#
739+
# ### Indexes
740+
#
741+
# * `index_rails_02e851e3b7`:
742+
# * **`id`**
743+
# * `index_rails_02e851e3b8` (_using_ hash):
744+
# * **`foreign_thing_id`**
745+
#
746+
EOS
747+
end
748+
531749
describe '#set_defaults' do
532750
it 'should default show_complete_foreign_keys to false' do
533751
expect(Annotate.true?(ENV['show_complete_foreign_keys'])).to be(false)

0 commit comments

Comments
 (0)