scheman 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7c96927177dfbde154defcf9c902c953c60a580a
4
- data.tar.gz: 51e98f13cdf9ea43eec4a515be4b883ab855b880
3
+ metadata.gz: 3e73fa1b30a5a2616d7b7a44c8b3b2dfd750e3d9
4
+ data.tar.gz: dba7df4899f8acaf89b4f55bd611403c773e0768
5
5
  SHA512:
6
- metadata.gz: 09fb8220340760be61f9a6ad057313604657dc4425fb97bc0ac10bf41c05295e7800da6a2ae0bfb8857dc407393a3edd5c6892b5af81ffb783a8c69acdf14ffe
7
- data.tar.gz: a1a90f9957eec8d37e83a367d5d3417016a1032143702c3e6c26834cbf3b678797f289b855c757faf05d0fc14923a986ff6606ea7c5b262454e7e686faee5d3b
6
+ metadata.gz: 4830bd819919bfb956a0b17a1068300071ba6e1974ac09ee4e9cfd03980043fa80630214f742f91218e8ec107843244d3fd55ba0396d0fe653c1322c4b7adb6d
7
+ data.tar.gz: 84543f84774b5ca849df3689d3738b1b89465d14a22ca2d30e32a6e35b4ea17155a71e79073f01ae4b45976f9d4cab8cdb3764d79d594fbfd98ad476df56048f
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## 0.0.2
2
+ * Support ADD INDEX
3
+ * Support DROP INDEX
4
+ * Detects altered column
5
+
6
+ ## 0.0.1
7
+ * 1st Release
data/README.md CHANGED
@@ -1,30 +1,32 @@
1
1
  # Scheman
2
- Manage database schema based on schema definition file.
2
+ SQL schema parser.
3
3
 
4
4
  ## Usage
5
- Creates Diff with 2 schema files and logs out their diff.
5
+ Create diff from 2 schema files.
6
6
 
7
7
  ```ruby
8
+ require "scheman"
9
+
8
10
  before = <<-SQL
9
11
  CREATE TABLE `table1` (
10
- `column1` INTEGER NOT NULL AUTO INCREMENT,
11
- PRIMARY KEY (`column1`)
12
+ `column1` INTEGER(11) PRIMARY KEY NOT NULL AUTO INCREMENT
12
13
  );
13
14
 
14
15
  CREATE TABLE `table2` (
15
- `column1` INTEGER NOT NULL AUTO INCREMENT,
16
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
16
17
  PRIMARY KEY (`column1`)
17
18
  );
18
19
  SQL
19
20
 
20
21
  after = <<-SQL
21
22
  CREATE TABLE `table1` (
23
+ `column1` CHAR(11) NOT NULL AUTO INCREMENT,
22
24
  `column2` VARCHAR(255) NOT NULL,
23
- PRIMARY KEY (`column1`)
25
+ PRIMARY KEY (`column2`)
24
26
  );
25
27
 
26
28
  CREATE TABLE `table3` (
27
- `column1` INTEGER NOT NULL AUTO INCREMENT,
29
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
28
30
  PRIMARY KEY (`column1`)
29
31
  );
30
32
  SQL
@@ -40,13 +42,14 @@ BEGIN;
40
42
  SET foreign_key_checks=0;
41
43
 
42
44
  CREATE TABLE `table3` (
43
- `column1` INTEGER NOT NULL AUTO INCREMENT,
45
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
44
46
  PRIMARY KEY (`column1`)
45
47
  );
46
48
 
47
- ALTER TABLE `table1` ADD COLUMN `column2` VARCHAR(255) NOT NULL;
48
-
49
- ALTER TABLE `table1` DROP COLUMN `column1`;
49
+ ALTER TABLE `table1` ADD COLUMN `column2` VARCHAR(255) NOT NULL,
50
+ CHANGE COLUMN `column1` CHAR(11) NOT NULL AUTO INCREMENT,
51
+ DROP PRIMARY KEY,
52
+ ADD PRIMARY KEY `column2`;
50
53
 
51
54
  DROP TABLE `table2`;
52
55
 
data/lib/scheman/diff.rb CHANGED
@@ -34,10 +34,14 @@ module Scheman
34
34
 
35
35
  private
36
36
 
37
- # TODO
38
37
  # @return [Array<Hash>] ALTER TABLE statements we need to apply
39
38
  def alter_tables
40
- after_schema.tables.inject([]) do |result, after_table|
39
+ drop_fields + add_fields + alter_fields + drop_indices + add_indices
40
+ end
41
+
42
+ # @return [Array<Hash>] ALTER TABLE statements for adding new fields
43
+ def add_fields
44
+ after_schema.tables.each_with_object([]) do |after_table, result|
41
45
  if before_table = before_schema.tables_indexed_by_name[after_table.name]
42
46
  after_table.fields.each do |after_field|
43
47
  unless before_table.fields_indexed_by_name[after_field.name]
@@ -46,7 +50,14 @@ module Scheman
46
50
  }
47
51
  end
48
52
  end
53
+ end
54
+ end
55
+ end
49
56
 
57
+ # @return [Array<Hash>] ALTER TABLE statements for dropping fields
58
+ def drop_fields
59
+ after_schema.tables.each_with_object([]) do |after_table, result|
60
+ if before_table = before_schema.tables_indexed_by_name[after_table.name]
50
61
  before_table.fields.each do |before_field|
51
62
  unless after_table.fields_indexed_by_name[before_field.name]
52
63
  result << {
@@ -55,7 +66,47 @@ module Scheman
55
66
  end
56
67
  end
57
68
  end
58
- result
69
+ end
70
+ end
71
+
72
+ # @return [Array<Hash>] ALTER TABLE statements for altering fields
73
+ def alter_fields
74
+ after_schema.tables.each_with_object([]) do |after_table, result|
75
+ if before_table = before_schema.tables_indexed_by_name[after_table.name]
76
+ after_table.fields.each do |after_field|
77
+ if before_field = before_table.fields_indexed_by_name[after_field.name]
78
+ result << {
79
+ alter_field: after_field.to_hash.merge(table_name: after_table.name),
80
+ }
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # @return [Array<Hash>] ALTER TABLE statements for adding indices
88
+ def add_indices
89
+ after_schema.tables.each_with_object([]) do |after_table, result|
90
+ if before_table = before_schema.tables_indexed_by_name[after_table.name]
91
+ (after_table.indices - before_table.indices).each do |index|
92
+ result << {
93
+ add_index: index.merge(table_name: after_table.name),
94
+ }
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # @return [Array<Hash>] ALTER TABLE statements for dropping indices
101
+ def drop_indices
102
+ after_schema.tables.each_with_object([]) do |after_table, result|
103
+ if before_table = before_schema.tables_indexed_by_name[after_table.name]
104
+ (before_table.indices - after_table.indices).each do |index|
105
+ result << {
106
+ drop_index: index.merge(table_name: after_table.name),
107
+ }
108
+ end
109
+ end
59
110
  end
60
111
  end
61
112
 
@@ -266,12 +266,7 @@ module Scheman
266
266
  auto_increment_qualifier |
267
267
  character_set_qualifier |
268
268
  collate_qualifier |
269
- unique_key_qualifier |
270
- key_qualifier
271
- end
272
-
273
- rule(:key_qualifier) do
274
- word_index.as(:key_qualifier)
269
+ unique_key_qualifier
275
270
  end
276
271
 
277
272
  rule(:unique_key_qualifier) do
@@ -287,7 +282,7 @@ module Scheman
287
282
  end
288
283
 
289
284
  rule(:primary_key_qualifier) do
290
- case_insensitive_str("primary key").as(:primary_key_qualifier)
285
+ (word_index | case_insensitive_str("primary key")).as(:primary_key_qualifier)
291
286
  end
292
287
 
293
288
  rule(:null_qualifier) do
@@ -20,7 +20,6 @@ module Scheman
20
20
  @tables_indexed_by_name ||= tables.index_by(&:name)
21
21
  end
22
22
 
23
- # TODO: We might want to calculate DROP TABLE and ALTER TABLE against to created tables
24
23
  # @return [Array<Scheman::Schema::Table>] All tables to be created after applying this schema
25
24
  def tables
26
25
  @tables ||= create_tables.map do |create_table|
@@ -43,22 +42,52 @@ module Scheman
43
42
  @table[:name]
44
43
  end
45
44
 
46
- # @return [Array]
45
+ # @return [Array<Field>]
47
46
  def fields
48
47
  @table[:fields].map do |field|
49
- Field.new(field[:field])
48
+ Field.new(field: field[:field], table: self)
50
49
  end
51
50
  end
52
51
 
53
- # @return [Hash]
52
+ # @return [Hash{String => Field}]
54
53
  def fields_indexed_by_name
55
54
  @fields_indexed_by_name ||= fields.index_by(&:name)
56
55
  end
56
+
57
+ # @return [Array<Hash>]
58
+ def indices
59
+ @indices ||= indices_from_definitions + indices_from_fields
60
+ end
61
+
62
+ private
63
+
64
+ def indices_from_definitions
65
+ @table[:indices].map do |hash|
66
+ hash[:index]
67
+ end
68
+ end
69
+
70
+ # @return [Array<Hash>]
71
+ def indices_from_fields
72
+ fields.map(&:index).compact
73
+ end
57
74
  end
58
75
 
59
76
  class Field
60
- def initialize(field)
77
+ def initialize(field: nil, table: nil)
61
78
  @field = field
79
+ @table = table
80
+ end
81
+
82
+ # @note Overridden
83
+ # @return [true, false]
84
+ def ==(field)
85
+ type == field.type && size == field.size && qualifiers == field.qualifiers
86
+ end
87
+
88
+ # @return [Hash]
89
+ def to_hash
90
+ @field.merge(qualifiers: qualifiers)
62
91
  end
63
92
 
64
93
  # @return [String]
@@ -66,9 +95,47 @@ module Scheman
66
95
  @field[:name]
67
96
  end
68
97
 
69
- # @return [Hash]
70
- def to_hash
71
- @field
98
+ # @return [String] Lower-cased type name
99
+ # @example
100
+ # "varchar"
101
+ def type
102
+ @field[:type]
103
+ end
104
+
105
+ # @note Size can be 2 values but not supported yet
106
+ # @return [String, nil]
107
+ def size
108
+ field[:size]
109
+ end
110
+
111
+ # @return [Array<Hash>] Sorted qualifiers, without index-related types
112
+ def qualifiers
113
+ @qualifiers ||= @field[:qualifiers].reject do |qualifier|
114
+ %w[primary_key unique_key].include?(qualifier[:qualifier][:type])
115
+ end
116
+ end
117
+
118
+ # @return [Hash] Index defined as a field qualifier
119
+ def index
120
+ @field[:qualifiers].find do |qualifier|
121
+ case qualifier[:qualifier][:type]
122
+ when "primary_key"
123
+ break {
124
+ column: name,
125
+ name: nil,
126
+ primary: true,
127
+ type: nil,
128
+ }
129
+ when "unique_key"
130
+ break {
131
+ column: name,
132
+ name: nil,
133
+ primary: nil,
134
+ type: nil,
135
+ unique: true,
136
+ }
137
+ end
138
+ end
72
139
  end
73
140
  end
74
141
  end
@@ -1,3 +1,3 @@
1
1
  module Scheman
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -67,6 +67,18 @@ module Scheman
67
67
  rule(drop_field: subtree(:drop_field)) do
68
68
  DropField.new(drop_field)
69
69
  end
70
+
71
+ rule(alter_field: subtree(:alter_field)) do
72
+ AlterField.new(alter_field)
73
+ end
74
+
75
+ rule(add_index: subtree(:add_index)) do
76
+ AddIndex.new(add_index)
77
+ end
78
+
79
+ rule(drop_index: subtree(:drop_index)) do
80
+ DropIndex.new(drop_index)
81
+ end
70
82
  end
71
83
 
72
84
  class Node
@@ -81,7 +93,18 @@ module Scheman
81
93
  end
82
94
  end
83
95
 
84
- class AlterTables < Statements
96
+ class AlterTables < Node
97
+ def to_s
98
+ alter_tables.join("\n\n")
99
+ end
100
+
101
+ private
102
+
103
+ def alter_tables
104
+ @element.group_by(&:table_name).map do |table_name, alter_tables|
105
+ %<ALTER TABLE `#{table_name}` #{alter_tables.join(",\n ")};>
106
+ end.sort
107
+ end
85
108
  end
86
109
 
87
110
  class DropTables < Statements
@@ -103,34 +126,38 @@ module Scheman
103
126
  end
104
127
 
105
128
  class AlterTable < Node
106
- def to_s
107
- "TODO"
129
+ def table_name
130
+ @element[:table_name]
108
131
  end
109
132
  end
110
133
 
111
- class AlterField < Node
134
+ class AlterField < AlterTable
135
+ def to_s
136
+ "CHANGE COLUMN #{field_definition}"
137
+ end
138
+
112
139
  private
113
140
 
114
- def table_name
115
- @element[:table_name]
141
+ def field_definition
142
+ Field.new(@element)
116
143
  end
117
144
  end
118
145
 
119
- class AddField < AlterField
146
+ class AddField < AlterTable
120
147
  def to_s
121
- "ALTER TABLE `#{table_name}` ADD COLUMN #{field};"
148
+ "ADD COLUMN #{field_definition}"
122
149
  end
123
150
 
124
151
  private
125
152
 
126
- def field
153
+ def field_definition
127
154
  Field.new(@element)
128
155
  end
129
156
  end
130
157
 
131
- class DropField < AlterField
158
+ class DropField < AlterTable
132
159
  def to_s
133
- "ALTER TABLE `#{table_name}` DROP COLUMN `#{field_name}`;"
160
+ "DROP COLUMN `#{field_name}`"
134
161
  end
135
162
 
136
163
  private
@@ -140,6 +167,61 @@ module Scheman
140
167
  end
141
168
  end
142
169
 
170
+ class AddIndex < AlterTable
171
+ def to_s
172
+ "ADD #{index_definition_name} #{index}"
173
+ end
174
+
175
+ private
176
+
177
+ def index_name
178
+ @element[:name]
179
+ end
180
+
181
+ def index_definition_name
182
+ case
183
+ when @element[:primary]
184
+ "PRIMARY KEY"
185
+ when @element[:unique]
186
+ "UNIQUE KEY"
187
+ else
188
+ "KEY"
189
+ end
190
+ end
191
+
192
+ def index_type
193
+ @element[:type]
194
+ end
195
+
196
+ def index_column
197
+ @element[:column]
198
+ end
199
+
200
+ def index
201
+ str = ""
202
+ str << "#{index_name} " if index_name
203
+ str << "#{index_type} " if index_type
204
+ str << "`#{index_column}`"
205
+ end
206
+ end
207
+
208
+ class DropIndex < AlterTable
209
+ def to_s
210
+ if @element[:primary]
211
+ "DROP PRIMARY KEY"
212
+ else
213
+ "DROP INDEX `#{index_name}`"
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ # TODO How to refer to an automatically named index name?
220
+ def index_name
221
+ @element[:name] || @element[:column]
222
+ end
223
+ end
224
+
143
225
  class CreateTable < Node
144
226
  def to_s
145
227
  str = ""
data/scheman.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Scheman::VERSION
8
8
  spec.authors = ["Ryo Nakamura"]
9
9
  spec.email = ["r7kamura@gmail.com"]
10
- spec.summary = "Manage database schema based on schema definition file."
10
+ spec.summary = "SQL schema parser."
11
11
  spec.homepage = "https://github.com/r7kamura/scheman"
12
12
  spec.license = "MIT"
13
13
 
@@ -20,12 +20,11 @@ describe Scheman::Diff do
20
20
  let(:before_schema) do
21
21
  <<-EOS.strip_heredoc
22
22
  CREATE TABLE `table1` (
23
- `column1` INTEGER NOT NULL AUTO INCREMENT,
24
- PRIMARY KEY (`column1`)
23
+ `column1` INTEGER(11) PRIMARY KEY NOT NULL AUTO INCREMENT
25
24
  );
26
25
 
27
26
  CREATE TABLE `table2` (
28
- `column1` INTEGER NOT NULL AUTO INCREMENT,
27
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
29
28
  PRIMARY KEY (`column1`)
30
29
  );
31
30
  EOS
@@ -34,12 +33,13 @@ describe Scheman::Diff do
34
33
  let(:after_schema) do
35
34
  <<-EOS.strip_heredoc
36
35
  CREATE TABLE `table1` (
36
+ `column1` CHAR(11) NOT NULL AUTO INCREMENT,
37
37
  `column2` VARCHAR(255) NOT NULL,
38
- PRIMARY KEY (`column1`)
38
+ PRIMARY KEY (`column2`)
39
39
  );
40
40
 
41
41
  CREATE TABLE `table3` (
42
- `column1` INTEGER NOT NULL AUTO INCREMENT,
42
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
43
43
  PRIMARY KEY (`column1`)
44
44
  );
45
45
  EOS
@@ -57,13 +57,14 @@ describe Scheman::Diff do
57
57
  SET foreign_key_checks=0;
58
58
 
59
59
  CREATE TABLE `table3` (
60
- `column1` INTEGER NOT NULL AUTO INCREMENT,
60
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
61
61
  PRIMARY KEY (`column1`)
62
62
  );
63
63
 
64
- ALTER TABLE `table1` ADD COLUMN `column2` VARCHAR(255) NOT NULL;
65
-
66
- ALTER TABLE `table1` DROP COLUMN `column1`;
64
+ ALTER TABLE `table1` ADD COLUMN `column2` VARCHAR(255) NOT NULL,
65
+ CHANGE COLUMN `column1` CHAR(11) NOT NULL AUTO INCREMENT,
66
+ DROP PRIMARY KEY,
67
+ ADD PRIMARY KEY `column2`;
67
68
 
68
69
  DROP TABLE `table2`;
69
70
 
@@ -29,7 +29,7 @@ describe Scheman::Parsers::Mysql do
29
29
  CREATE DATABASE database_name;
30
30
 
31
31
  CREATE TABLE `table1` (
32
- `column1` INTEGER NOT NULL AUTO INCREMENT,
32
+ `column1` INTEGER(11) NOT NULL AUTO INCREMENT,
33
33
  `column2` VARCHAR(255) NOT NULL,
34
34
  PRIMARY KEY (`column1`)
35
35
  );
@@ -55,7 +55,7 @@ describe Scheman::Parsers::Mysql do
55
55
  field: {
56
56
  name: "column1",
57
57
  type: "integer",
58
- values: [],
58
+ values: ["11"],
59
59
  qualifiers: [
60
60
  {
61
61
  qualifier: {
@@ -161,6 +161,26 @@ describe Scheman::Parsers::Mysql do
161
161
  end
162
162
  end
163
163
 
164
+ context "with KEY field qualifier" do
165
+ let(:str) do
166
+ "CREATE TABLE `table1` (`column1` INTEGER KEY);"
167
+ end
168
+
169
+ it "succeeds in parse" do
170
+ subject[0][:create_table][:fields][0][:field][:qualifiers][0][:qualifier][:type].should == "primary_key"
171
+ end
172
+ end
173
+
174
+ context "with INDEX field qualifier" do
175
+ let(:str) do
176
+ "CREATE TABLE `table1` (`column1` INTEGER INDEX);"
177
+ end
178
+
179
+ it "succeeds in parse" do
180
+ subject[0][:create_table][:fields][0][:field][:qualifiers][0][:qualifier][:type].should == "primary_key"
181
+ end
182
+ end
183
+
164
184
  context "with UNSIGNED field type qualifier" do
165
185
  let(:str) do
166
186
  "CREATE TABLE `table1` (`column1` INTEGER UNSIGNED);"
@@ -221,26 +241,6 @@ describe Scheman::Parsers::Mysql do
221
241
  end
222
242
  end
223
243
 
224
- context "with KEY field qualifier" do
225
- let(:str) do
226
- "CREATE TABLE `table1` (`column1` INTEGER KEY);"
227
- end
228
-
229
- it "succeeds in parse" do
230
- subject[0][:create_table][:fields][0][:field][:qualifiers][0][:qualifier][:type].should == "key"
231
- end
232
- end
233
-
234
- context "with INDEX field qualifier" do
235
- let(:str) do
236
- "CREATE TABLE `table1` (`column1` INTEGER INDEX);"
237
- end
238
-
239
- it "succeeds in parse" do
240
- subject[0][:create_table][:fields][0][:field][:qualifiers][0][:qualifier][:type].should == "key"
241
- end
242
- end
243
-
244
244
  context "with PRIMARY KEY" do
245
245
  let(:str) do
246
246
  <<-EOS.strip_heredoc
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scheman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-06 00:00:00.000000000 Z
11
+ date: 2014-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -102,6 +102,7 @@ extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
104
  - ".gitignore"
105
+ - CHANGELOG.md
105
106
  - Gemfile
106
107
  - LICENSE.txt
107
108
  - README.md
@@ -143,7 +144,7 @@ rubyforge_project:
143
144
  rubygems_version: 2.2.2
144
145
  signing_key:
145
146
  specification_version: 4
146
- summary: Manage database schema based on schema definition file.
147
+ summary: SQL schema parser.
147
148
  test_files:
148
149
  - spec/scheman/diff_spec.rb
149
150
  - spec/scheman/parsers/mysql_spec.rb