mkxms-mssql 1.0.0

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.
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