mkxms-mssql 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +53 -0
  6. data/Rakefile +1 -0
  7. data/bin/mkxms-mssql +5 -0
  8. data/lib/mkxms/mssql/access_object_definition.rb +61 -0
  9. data/lib/mkxms/mssql/adoption_script_writer.rb +1486 -0
  10. data/lib/mkxms/mssql/check_constraint_handler.rb +56 -0
  11. data/lib/mkxms/mssql/database_handler.rb +339 -0
  12. data/lib/mkxms/mssql/default_constraint_handler.rb +46 -0
  13. data/lib/mkxms/mssql/engine.rb +88 -0
  14. data/lib/mkxms/mssql/exceptions.rb +10 -0
  15. data/lib/mkxms/mssql/filegroup_handler.rb +81 -0
  16. data/lib/mkxms/mssql/foreign_key_handler.rb +85 -0
  17. data/lib/mkxms/mssql/function_handler.rb +74 -0
  18. data/lib/mkxms/mssql/indented_string_builder.rb +199 -0
  19. data/lib/mkxms/mssql/index_column.rb +11 -0
  20. data/lib/mkxms/mssql/index_handler.rb +98 -0
  21. data/lib/mkxms/mssql/keylike_constraint_helper.rb +67 -0
  22. data/lib/mkxms/mssql/permission_handler.rb +115 -0
  23. data/lib/mkxms/mssql/primary_key_handler.rb +36 -0
  24. data/lib/mkxms/mssql/property_handler.rb +87 -0
  25. data/lib/mkxms/mssql/query_cursor.rb +111 -0
  26. data/lib/mkxms/mssql/role_handler.rb +55 -0
  27. data/lib/mkxms/mssql/schema_handler.rb +42 -0
  28. data/lib/mkxms/mssql/sql_string_manipulators.rb +46 -0
  29. data/lib/mkxms/mssql/statistics_handler.rb +59 -0
  30. data/lib/mkxms/mssql/stored_procedure_handler.rb +65 -0
  31. data/lib/mkxms/mssql/table_handler.rb +180 -0
  32. data/lib/mkxms/mssql/unique_constraint_handler.rb +32 -0
  33. data/lib/mkxms/mssql/utils.rb +83 -0
  34. data/lib/mkxms/mssql/version.rb +5 -0
  35. data/lib/mkxms/mssql/view_handler.rb +58 -0
  36. data/lib/mkxms/mssql.rb +62 -0
  37. data/mkxms-mssql.gemspec +26 -0
  38. data/spec/utils/indented_string_builder_spec.rb +218 -0
  39. data/spec/utils/query_cursor_spec.rb +57 -0
  40. metadata +142 -0
@@ -0,0 +1,55 @@
1
+ require 'mkxms/mssql/property_handler'
2
+ require 'mkxms/mssql/utils'
3
+
4
+ module Mkxms; end
5
+
6
+ module Mkxms::Mssql
7
+ class Role
8
+ include ExtendedProperties, Property::Hosting
9
+
10
+ def initialize(name, owner: nil)
11
+ @name = name
12
+ @owner = owner
13
+ @encompassing_roles = []
14
+ end
15
+
16
+ attr_accessor :name, :owner
17
+ attr_reader :encompassing_roles
18
+
19
+ def definition_sql
20
+ "CREATE ROLE #{name};" + extended_properties_sql.joined_on_new_lines
21
+ end
22
+
23
+ def authorization_sql
24
+ "ALTER AUTHORIZATION ON ROLE:: #{name} TO #{owner};" if owner
25
+ end
26
+
27
+ def membership_sql
28
+ encompassing_roles.map do |encompassing_role|
29
+ "EXEC sp_addrolemember '#{Utils.unquoted_name encompassing_role}', '#{Utils.unquoted_name name}';\n"
30
+ end.join('')
31
+ end
32
+
33
+ def property_subject_identifiers
34
+ ['USER', Utils.unquoted_name(name)]
35
+ end
36
+ end
37
+
38
+ class RoleHandler
39
+ include PropertyHandler::ElementHandler
40
+
41
+ def initialize(roles, node)
42
+ @role = Role.new(node.attributes['name'], owner: node.attributes['owner']).tap do |r|
43
+ roles << r
44
+ end
45
+ end
46
+
47
+ def extended_properties
48
+ @role.extended_properties
49
+ end
50
+
51
+ def handle_member_of_element(parse)
52
+ @role.encompassing_roles << parse.node.attributes['name']
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,42 @@
1
+ require 'mkxms/mssql/property_handler'
2
+
3
+ module Mkxms; end
4
+
5
+ module Mkxms::Mssql
6
+ class Schema
7
+ include ExtendedProperties, Property::Hosting
8
+
9
+ def initialize(name, owner: nil)
10
+ @name = name
11
+ @owner = owner
12
+ end
13
+
14
+ attr_accessor :name, :owner
15
+
16
+ def to_sql
17
+ if owner
18
+ "CREATE SCHEMA #{name} AUTHORIZATION #{owner};"
19
+ else
20
+ "CREATE SCHEMA #{name};"
21
+ end + extended_properties_sql.joined_on_new_lines
22
+ end
23
+
24
+ def property_subject_identifiers
25
+ ['SCHEMA', Utils.unquoted_name(name)]
26
+ end
27
+ end
28
+
29
+ class SchemaHandler
30
+ include PropertyHandler::ElementHandler
31
+
32
+ def initialize(schemas, node)
33
+ @schema = Schema.new(node.attributes['name'], owner: node.attributes['owner']).tap do |s|
34
+ schemas << s
35
+ end
36
+ end
37
+
38
+ def extended_properties
39
+ @schema.extended_properties
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,46 @@
1
+ require 'xmigra'
2
+
3
+ module Mkxms; end
4
+
5
+ module Mkxms::Mssql
6
+ module SqlStringManipulators
7
+ MSSQL = XMigra::MSSQLSpecifics
8
+
9
+ def dedent(s)
10
+ margin = nil
11
+ s.lines.map do |l|
12
+ case
13
+ when margin.nil? && l =~ /^ *$/
14
+ l
15
+ when margin.nil?
16
+ margin = /^ */.match(l).length
17
+ l[margin..-1]
18
+ when s =~/^\s*$/
19
+ l[margin..-1]
20
+ else
21
+ /^(?: *)(.*)/.match(l)[1]
22
+ end
23
+ end.join('')
24
+ end
25
+
26
+ def stresc(s)
27
+ s.gsub("'", "''")
28
+ end
29
+
30
+ def strlit(s)
31
+ MSSQL.string_literal(s)
32
+ end
33
+
34
+ def unquoted_identifier(s)
35
+ MSSQL.strip_identifier_quoting(s)
36
+ end
37
+
38
+ def bit_test(expr, expected)
39
+ "#{expr} = #{expected ? 1 : 0}"
40
+ end
41
+
42
+ def boolean_desc(_is, s)
43
+ (_is ? '' : 'not ') + s
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,59 @@
1
+ require 'mkxms/mssql/property_handler'
2
+ require 'mkxms/mssql/utils'
3
+
4
+ module Mkxms; end
5
+
6
+ module Mkxms::Mssql
7
+ class Statistics
8
+ extend Utils::FlagsQueries
9
+ include ExtendedProperties
10
+
11
+ def initialize(attrs)
12
+ @schema = attrs['in-schema']
13
+ @relation = attrs['on']
14
+ @name = attrs['name']
15
+ @columns = []
16
+ @flags = []
17
+
18
+ @flags << :manual_recompute if attrs['no-recompute']
19
+ end
20
+
21
+ attr_accessor :schema, :relation, :name
22
+ attr_reader :columns, :flags
23
+ flags_query :manual_recompute
24
+
25
+ def xmigra_params
26
+ [qualified_relation, @columns.join(', ')].tap do |result|
27
+ result << {'with' => 'NORECOMPUTE'} if manual_recompute?
28
+ end
29
+ end
30
+
31
+ def name_params_pair
32
+ [name, xmigra_params]
33
+ end
34
+
35
+ def qualified_relation
36
+ "#@schema.#@relation"
37
+ end
38
+ end
39
+
40
+ class StatisticsHandler
41
+ include PropertyHandler::ElementHandler
42
+
43
+ def initialize(statistics_objs, node)
44
+ a = node.attributes
45
+
46
+ @statistics = Statistics.new(a).tap do |s|
47
+ statistics_objs << s
48
+ end
49
+ end
50
+
51
+ def extended_properties
52
+ @statistics.extended_properties
53
+ end
54
+
55
+ def handle_column_element(parse)
56
+ @statistics.columns << parse.node.attributes['name']
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,65 @@
1
+ require 'mkxms/mssql/access_object_definition'
2
+ require 'mkxms/mssql/property_handler'
3
+ require 'mkxms/mssql/utils'
4
+
5
+ module Mkxms::Mssql
6
+ class StoredProcedure
7
+ include ExtendedProperties, Property::Hosting, Property::SchemaScoped
8
+ include Utils::SchemaQualifiedName
9
+
10
+ SQL_OBJECT_TYPE = 'PROCEDURE'
11
+
12
+ def initialize(attrs)
13
+ @schema = attrs['schema']
14
+ @name = attrs['name']
15
+ @definition = ''
16
+ @param_properties = Hash.new {|h, k| h[k] = ''}
17
+ end
18
+
19
+ attr_accessor :schema, :name
20
+ attr_reader :definition, :param_properties
21
+
22
+ def to_sql
23
+ mvdef = AccessObjectDefinition.replace_object_name(definition, "[{filename}]")
24
+ ([mvdef] + extended_properties_sql + param_properties_sql).join("\n")
25
+ end
26
+
27
+ def param_properties_sql
28
+ @param_properties.each_pair.map do |k, v|
29
+ Property.addition_sql(k[1], v, property_subject_identifiers + ['PARAMETER', Utils.unquoted_name(k[0])])
30
+ end
31
+ end
32
+ end
33
+
34
+ class StoredProcedureHandler
35
+ include PropertyHandler::ElementHandler
36
+
37
+ def initialize(procedures, node)
38
+ a = node.attributes
39
+
40
+ @procedure = StoredProcedure.new(a).tap do |sp|
41
+ procedures << sp
42
+ end
43
+ end
44
+
45
+ def extended_properties
46
+ @procedure.extended_properties
47
+ end
48
+
49
+ def handle_definition_element(parse); end
50
+
51
+ def handle_references_element(parse); end
52
+
53
+ def handle_param_property_element(parse); end
54
+
55
+ def handle_text(text, parent_element)
56
+ case [parent_element.namespace, parent_element.name]
57
+ when ['', 'definition']
58
+ @procedure.definition << text
59
+ when ['', 'param-property']
60
+ a = parent_element.attributes
61
+ @procedure.param_properties[[a['param'], a['property']]] << text
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,180 @@
1
+ require 'mkxms/mssql/exceptions'
2
+ require 'mkxms/mssql/property_handler'
3
+ require 'mkxms/mssql/utils'
4
+
5
+ module Mkxms; end
6
+
7
+ module Mkxms::Mssql
8
+ class Table
9
+ SQL_OBJECT_TYPE = 'TABLE'
10
+ include ExtendedProperties, Property::Hosting, Property::SchemaScoped
11
+
12
+ def initialize(schema, name)
13
+ @schema = schema
14
+ @name = name
15
+ @columns = []
16
+ end
17
+
18
+ attr_accessor :schema, :name, :owner, :heap_storage, :lob_storage
19
+ attr_reader :columns
20
+
21
+ def to_sql
22
+ lines = ["CREATE TABLE #{schema}.#{name} ("]
23
+ lines << columns.map{|c| " " + c.to_sql}.join(",\n")
24
+ lines << ")"
25
+
26
+ lines << "ON #{heap_storage}" if heap_storage
27
+ lines << "TEXTIMAGE_ON #{lob_storage}" if lob_storage
28
+
29
+ lines << ";"
30
+ lines << ""
31
+
32
+ if owner
33
+ lines << ["ALTER AUTHORIZATION ON OBJECT::#{schema}.#{name} TO #{owner};"]
34
+ lines << ""
35
+ end
36
+
37
+ lines.concat extended_properties_sql
38
+
39
+ columns.each do |col|
40
+ lines.concat subitem_extended_properties_sql(col)
41
+ end
42
+
43
+ return lines.join("\n")
44
+ end
45
+ end
46
+
47
+ class Column
48
+ SQL_OBJECT_TYPE = 'COLUMN'
49
+
50
+ include ExtendedProperties
51
+ extend Utils::FlagsQueries
52
+
53
+ def initialize(name)
54
+ @name = name
55
+ @flags = []
56
+ @type_info = {}
57
+ end
58
+
59
+ attr_accessor :name, :type, :collation, :computed_expression
60
+ attr_reader :flags, :type_info
61
+
62
+ flags_query :filestream, :nullable, :identity, :replicated, :rowguid, :persisted
63
+
64
+ def to_sql
65
+ parts = [name]
66
+ if computed_expression
67
+ parts << "AS " + computed_expression
68
+ parts << "PERSISTED" if persisted?
69
+ else
70
+ each_type_part {|part| parts << part}
71
+ end
72
+
73
+ return parts.join(' ')
74
+ end
75
+
76
+ def each_type_part
77
+ yield type
78
+ yield("COLLATE " + collation) if collation
79
+ yield(nullable? ? 'NULL' : 'NOT NULL')
80
+ if identity?
81
+ yield "IDENTITY"
82
+ yield("NOT FOR REPLICATION") unless replicated?
83
+ end
84
+ yield("ROWGUID") if rowguid?
85
+ end
86
+ end
87
+
88
+ class TableHandler
89
+ class ColumnHandler
90
+ include PropertyHandler::ElementHandler
91
+
92
+ def initialize(columns, node)
93
+ a = node.attributes
94
+
95
+ col_attrs = {}
96
+ use_attr = proc {|k| col_attrs[k.gsub('-', '_').to_sym] = node.attributes[k]}
97
+ col_type = %w[type-schema type].map {|k| use_attr[k]}.compact.join('.')
98
+
99
+ if a.has_key?('capacity')
100
+ col_type << "(%s)" % [use_attr['capacity']]
101
+ end
102
+
103
+ prec_scale = []
104
+ if a.has_key?('precision') || a.has_key?('scale')
105
+ prec_scale << use_attr['precision']
106
+ end
107
+ if a.has_key?('scale')
108
+ prec_scale << use_attr['scale']
109
+ end
110
+ unless prec_scale.empty?
111
+ col_type << "(%s)" % (prec_scale.join(', '))
112
+ end
113
+
114
+ if a.has_key?('xml_collection_id')
115
+ col_type << "(%s %s)" % [
116
+ xml_structure = (a['full-xml-document'] ? 'DOCUMENT' : 'CONTENT'),
117
+ a['xml_collection_id']
118
+ ]
119
+ col_attrs[:xml_validation] = {xml_structure.downcase => a['xml_collection_id']}
120
+ end
121
+
122
+ raise UnsupportedFeatureError.new("Column #{name} declared 'not-ansi-padded'") if a['not-ansi-padded']
123
+
124
+ @column = Column.new(a['name']).tap do |c|
125
+ c.type = col_type
126
+ c.collation = a['collation']
127
+ c.flags << :nullable if a['nullable']
128
+ c.flags << :replicated if a['replicated']
129
+ c.flags << :filestream if a['filestream']
130
+ c.type_info.update(col_attrs)
131
+ columns << c
132
+ end
133
+ end
134
+
135
+ attr_reader :column
136
+
137
+ def extended_properties
138
+ @column.extended_properties
139
+ end
140
+
141
+ def handle_computed_expression_element(parse)
142
+ column.flags << :persisted if parse.node.attributes['persisted']
143
+ # Handle expression in #handle_text
144
+ end
145
+
146
+ def handle_text(text, parent_element)
147
+ case %i[namespace name].map {|m| parent_element.send(m)}
148
+ when ['', 'computed-expression']
149
+ (column.computed_expression ||= '') << text
150
+ end
151
+ end
152
+ end
153
+
154
+ include PropertyHandler::ElementHandler
155
+
156
+ def initialize(tables, node)
157
+ a = node.attributes
158
+ @table = Table.new(a['schema'], a['name']).tap do |t|
159
+ tables << t
160
+ end
161
+ @table.owner = a['owner']
162
+ @table.heap_storage = a['rows-on']
163
+ @table.lob_storage = a['textimage-on']
164
+ @identity_column = a['identity']
165
+ @rowguid_column = a['rowguidcol']
166
+ end
167
+
168
+ def extended_properties
169
+ @table.extended_properties
170
+ end
171
+
172
+ def handle_column_element(parse)
173
+ parse.context = ColumnHandler.new(@table.columns, parse.node)
174
+ column = parse.context.column
175
+
176
+ column.flags << :identity if column.name.eql? @identity_column
177
+ column.flags << :rowguid if column.name.eql? @rowguid_column
178
+ end
179
+ end
180
+ end