db_leftovers 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog ADDED
@@ -0,0 +1,6 @@
1
+ 1.4.0
2
+ -----
3
+
4
+ - Added support for functional indexes.
5
+
6
+
data/README.md CHANGED
@@ -44,7 +44,7 @@ Within the DSL file, the following methods are supported:
44
44
 
45
45
  ### index(table\_name, columns, [opts])
46
46
 
47
- This ensures that you have an index on the given table and column(s). The `columns` parameter can be either a string/symbol or a list of strings/symbols. Opts is a hash with the following possible keys:
47
+ This ensures that you have an index on the given table and column(s). The `columns` parameter can be either a symbol or a list of strings/symbols. (If you pass a single string for the `columns` parameter, it will be treated as the expression for a functional index rather than a column name.) Opts is a hash with the following possible keys:
48
48
 
49
49
  * `:name` The name of the index. Defaults to `index_`*table\_name*`_on_`*column\_names*, like the `add_index` method from Rails migrations.
50
50
 
@@ -54,11 +54,15 @@ This ensures that you have an index on the given table and column(s). The `colum
54
54
 
55
55
  * `:using` Lets you specify what kind of index to create. Default is `btree`, but if you're on Postgres you might also want `gist`, `gin`, or `hash`.
56
56
 
57
+ * `:function` Lets you specify an expression rather than a list of columns. If you give this option, you should pass an empty list of column names. Alternately, you can pass a string as the column name (rather than a symbol), and db\_leftovers will interpret it as a function.
58
+
57
59
  #### Examples
58
60
 
59
61
  index :books, :author_id
60
62
  index :books, [:publisher_id, :published_at]
61
63
  index :books, :isbn, :unique => true
64
+ index :authors, [], function: 'lower(name)'
65
+ index :authors, 'lower(name)'
62
66
 
63
67
  ### foreign\_key(from\_table, [from\_column], to\_table, [to\_column], [opts])
64
68
 
@@ -23,7 +23,7 @@ module DBLeftovers
23
23
  CREATE #{unique} INDEX #{idx.index_name}
24
24
  ON #{idx.table_name}
25
25
  #{using}
26
- (#{idx.column_names.join(', ')})
26
+ (#{idx.index_function || idx.column_names.join(', ')})
27
27
  #{where}
28
28
  EOQ
29
29
  execute_sql(sql)
@@ -3,23 +3,33 @@ module DBLeftovers
3
3
  # Just a struct to hold all the info for one index:
4
4
  class Index
5
5
  attr_accessor :table_name, :column_names, :index_name,
6
- :where_clause, :using_clause, :unique
6
+ :where_clause, :using_clause, :unique, :index_function
7
7
 
8
8
  def initialize(table_name, column_names, opts={})
9
9
  opts = {
10
10
  :where => nil,
11
+ :function => nil,
11
12
  :unique => false,
12
13
  :using => nil
13
14
  }.merge(opts)
14
15
  opts.keys.each do |k|
15
- raise "Unknown option: #{k}" unless [:where, :unique, :using, :name].include?(k)
16
+ raise "Unknown option: #{k}" unless [:where, :function, :unique, :using, :name].include?(k)
17
+ end
18
+ if column_names.is_a?(String) and opts[:function].nil?
19
+ opts[:function] = column_names
20
+ column_names = []
16
21
  end
17
22
  @table_name = table_name.to_s
18
23
  @column_names = [column_names].flatten.map{|x| x.to_s}
19
24
  @where_clause = opts[:where]
25
+ @index_function = opts[:function]
20
26
  @using_clause = opts[:using]
21
27
  @unique = !!opts[:unique]
22
28
  @index_name = (opts[:name] || choose_name(@table_name, @column_names)).to_s
29
+
30
+ raise "Indexes need a table!" unless @table_name
31
+ raise "Indexes need at least column or an expression!" unless (@column_names.any? or @index_function)
32
+ raise "Can't have both columns and an expression!" if (@column_names.size > 0 and @index_function)
23
33
  end
24
34
 
25
35
  def unique?
@@ -31,19 +41,30 @@ module DBLeftovers
31
41
  other.column_names == column_names and
32
42
  other.index_name == index_name and
33
43
  other.where_clause == where_clause and
44
+ other.index_function == index_function and
34
45
  other.using_clause == using_clause and
35
46
  other.unique == unique
36
47
  end
37
48
 
38
49
  def to_s
39
- "<#{@index_name}: #{@table_name}.[#{column_names.join(",")}] unique=#{@unique}, where=#{@where_clause}, using=#{@using_clause}>"
50
+ "<#{@index_name}: #{@table_name}.[#{column_names.join(",")}] unique=#{@unique}, where=#{@where_clause}, function=#{@index_function}, using=#{@using_clause}>"
40
51
  end
41
52
 
42
53
  private
43
54
 
44
55
  def choose_name(table_name, column_names)
56
+ topic = if column_names.any?
57
+ column_names.join("_and_")
58
+ else
59
+ index_function
60
+ end
61
+ ret = "index_#{table_name}_on_#{topic}"
62
+ ret = ret.gsub(/[^a-zA-Z0-9]/, '_').
63
+ gsub(/__+/, '_').
64
+ gsub(/^_/, '').
65
+ gsub(/_$/, '')
45
66
  # Max length in Postgres is 63; in MySQL 64:
46
- "index_#{table_name}_on_#{column_names.join('_and_')}"[0,63]
67
+ ret[0,63]
47
68
  end
48
69
 
49
70
  end
@@ -16,7 +16,8 @@ module DBLeftovers
16
16
  ix.indisunique AS is_unique,
17
17
  array_to_string(ix.indkey, ',') AS column_numbers,
18
18
  am.amname AS index_type,
19
- pg_get_expr(ix.indpred, ix.indrelid) AS where_clause
19
+ pg_get_expr(ix.indpred, ix.indrelid) AS where_clause,
20
+ pg_get_expr(ix.indexprs, ix.indrelid) AS index_function
20
21
  FROM pg_class t,
21
22
  pg_class i,
22
23
  pg_index ix,
@@ -37,10 +38,11 @@ module DBLeftovers
37
38
  ix.indrelid,
38
39
  ix.indkey,
39
40
  am.amname,
40
- ix.indpred
41
+ ix.indpred,
42
+ ix.indexprs
41
43
  ORDER BY t.relname, i.relname
42
44
  EOQ
43
- @conn.select_rows(sql).each do |indexrelid, indrelid, table_name, index_name, is_unique, column_numbers, index_method, where_clause|
45
+ @conn.select_rows(sql).each do |indexrelid, indrelid, table_name, index_name, is_unique, column_numbers, index_method, where_clause, index_function|
44
46
  where_clause = remove_outer_parens(where_clause) if where_clause
45
47
  index_method = nil if index_method == 'btree'
46
48
  ret[index_name] = Index.new(
@@ -48,6 +50,7 @@ module DBLeftovers
48
50
  column_names_for_index(indrelid, column_numbers.split(",")),
49
51
  unique: is_unique == 't',
50
52
  where: where_clause,
53
+ function: index_function,
51
54
  using: index_method,
52
55
  name: index_name
53
56
  )
@@ -128,6 +131,7 @@ module DBLeftovers
128
131
  private
129
132
 
130
133
  def column_names_for_index(table_id, column_numbers)
134
+ return [] if column_numbers == ['0']
131
135
  column_numbers.map do |c|
132
136
  sql = <<-EOQ
133
137
  SELECT attname
@@ -1,3 +1,3 @@
1
1
  module DbLeftovers
2
- VERSION = '1.3.3'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -102,6 +102,22 @@ describe DBLeftovers::PostgresDatabaseInterface do
102
102
  @db.lookup_all_constraints['books_have_positive_pages'].check.should == 'pages_count > 12'
103
103
  end
104
104
 
105
+ it "should allow functional indexes, specified as a string" do
106
+ DBLeftovers::Definition.define :db_interface => @db do
107
+ index :authors, 'lower(name)'
108
+ end
109
+ @db.lookup_all_indexes.size.should == 1
110
+ @db.lookup_all_indexes.keys.sort.should == ['index_authors_on_lower_name']
111
+ end
112
+
113
+ it "should allow functional indexes, specified with an option" do
114
+ DBLeftovers::Definition.define :db_interface => @db do
115
+ index :authors, [], function: 'lower(name)'
116
+ end
117
+ @db.lookup_all_indexes.size.should == 1
118
+ @db.lookup_all_indexes.keys.sort.should == ['index_authors_on_lower_name']
119
+ end
120
+
105
121
  end
106
122
  end
107
123
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db_leftovers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.3
4
+ version: 1.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -69,6 +69,7 @@ extra_rdoc_files: []
69
69
  files:
70
70
  - .document
71
71
  - .gitignore
72
+ - Changelog
72
73
  - Gemfile
73
74
  - Gemfile.lock
74
75
  - LICENSE.txt
@@ -111,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
112
  version: '0'
112
113
  segments:
113
114
  - 0
114
- hash: 3023613074233627262
115
+ hash: 1649859164079023412
115
116
  required_rubygems_version: !ruby/object:Gem::Requirement
116
117
  none: false
117
118
  requirements: