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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +53 -0
- data/Rakefile +1 -0
- data/bin/mkxms-mssql +5 -0
- data/lib/mkxms/mssql/access_object_definition.rb +61 -0
- data/lib/mkxms/mssql/adoption_script_writer.rb +1486 -0
- data/lib/mkxms/mssql/check_constraint_handler.rb +56 -0
- data/lib/mkxms/mssql/database_handler.rb +339 -0
- data/lib/mkxms/mssql/default_constraint_handler.rb +46 -0
- data/lib/mkxms/mssql/engine.rb +88 -0
- data/lib/mkxms/mssql/exceptions.rb +10 -0
- data/lib/mkxms/mssql/filegroup_handler.rb +81 -0
- data/lib/mkxms/mssql/foreign_key_handler.rb +85 -0
- data/lib/mkxms/mssql/function_handler.rb +74 -0
- data/lib/mkxms/mssql/indented_string_builder.rb +199 -0
- data/lib/mkxms/mssql/index_column.rb +11 -0
- data/lib/mkxms/mssql/index_handler.rb +98 -0
- data/lib/mkxms/mssql/keylike_constraint_helper.rb +67 -0
- data/lib/mkxms/mssql/permission_handler.rb +115 -0
- data/lib/mkxms/mssql/primary_key_handler.rb +36 -0
- data/lib/mkxms/mssql/property_handler.rb +87 -0
- data/lib/mkxms/mssql/query_cursor.rb +111 -0
- data/lib/mkxms/mssql/role_handler.rb +55 -0
- data/lib/mkxms/mssql/schema_handler.rb +42 -0
- data/lib/mkxms/mssql/sql_string_manipulators.rb +46 -0
- data/lib/mkxms/mssql/statistics_handler.rb +59 -0
- data/lib/mkxms/mssql/stored_procedure_handler.rb +65 -0
- data/lib/mkxms/mssql/table_handler.rb +180 -0
- data/lib/mkxms/mssql/unique_constraint_handler.rb +32 -0
- data/lib/mkxms/mssql/utils.rb +83 -0
- data/lib/mkxms/mssql/version.rb +5 -0
- data/lib/mkxms/mssql/view_handler.rb +58 -0
- data/lib/mkxms/mssql.rb +62 -0
- data/mkxms-mssql.gemspec +26 -0
- data/spec/utils/indented_string_builder_spec.rb +218 -0
- data/spec/utils/query_cursor_spec.rb +57 -0
- 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
|