scheman 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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