mkxms-mssql 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|