mkxms-mssql 1.0.0 → 1.1.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 +4 -4
- data/README.md +4 -10
- data/lib/mkxms/mssql.rb +18 -0
- data/lib/mkxms/mssql/adoption_script_writer.rb +759 -91
- data/lib/mkxms/mssql/clr_aggregate_handler.rb +98 -0
- data/lib/mkxms/mssql/clr_assembly_handler.rb +92 -0
- data/lib/mkxms/mssql/clr_function_handler.rb +172 -0
- data/lib/mkxms/mssql/clr_impl.rb +58 -0
- data/lib/mkxms/mssql/clr_stored_procedure_handler.rb +88 -0
- data/lib/mkxms/mssql/clr_type_handler.rb +92 -0
- data/lib/mkxms/mssql/database_handler.rb +124 -3
- data/lib/mkxms/mssql/declaratives_creator.rb +206 -0
- data/lib/mkxms/mssql/dml_trigger_handler.rb +107 -0
- data/lib/mkxms/mssql/filegroup_handler.rb +1 -4
- data/lib/mkxms/mssql/function_handler.rb +1 -4
- data/lib/mkxms/mssql/indented_string_builder.rb +8 -2
- data/lib/mkxms/mssql/index_handler.rb +1 -4
- data/lib/mkxms/mssql/keywords.rb +492 -0
- data/lib/mkxms/mssql/primary_key_handler.rb +1 -4
- data/lib/mkxms/mssql/property_handler.rb +8 -0
- data/lib/mkxms/mssql/query_cursor.rb +12 -4
- data/lib/mkxms/mssql/references_handler.rb +24 -0
- data/lib/mkxms/mssql/role_handler.rb +1 -4
- data/lib/mkxms/mssql/scalar_type_handler.rb +108 -0
- data/lib/mkxms/mssql/schema_handler.rb +1 -4
- data/lib/mkxms/mssql/sql_string_manipulators.rb +4 -4
- data/lib/mkxms/mssql/statistics_handler.rb +1 -4
- data/lib/mkxms/mssql/stored_procedure_handler.rb +1 -4
- data/lib/mkxms/mssql/synonym_handler.rb +40 -0
- data/lib/mkxms/mssql/table_handler.rb +2 -8
- data/lib/mkxms/mssql/table_type_handler.rb +254 -0
- data/lib/mkxms/mssql/utils.rb +96 -0
- data/lib/mkxms/mssql/version.rb +1 -1
- data/lib/mkxms/mssql/view_handler.rb +1 -4
- data/spec/utils/indented_string_builder_spec.rb +21 -0
- data/spec/utils/query_cursor_spec.rb +2 -2
- data/spec/utils/sql_string_manipulators_spec.rb +59 -0
- metadata +18 -3
@@ -16,14 +16,11 @@ module Mkxms::Mssql
|
|
16
16
|
a = node.attributes
|
17
17
|
|
18
18
|
@pkey = PrimaryKey.new(a).tap do |c|
|
19
|
+
store_properties_on c
|
19
20
|
constraints << c
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def extended_properties
|
24
|
-
@pkey.extended_properties
|
25
|
-
end
|
26
|
-
|
27
24
|
def handle_column_element(parse)
|
28
25
|
a = parse.node.attributes
|
29
26
|
|
@@ -54,6 +54,14 @@ module Mkxms::Mssql
|
|
54
54
|
def handle_property_element(parse)
|
55
55
|
parse.context = PropertyHandler.new(self, parse.node.attributes)
|
56
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def store_properties_on(target)
|
60
|
+
define_singleton_method(:extended_properties) do
|
61
|
+
target.extended_properties
|
62
|
+
end
|
63
|
+
return target
|
64
|
+
end
|
57
65
|
end
|
58
66
|
|
59
67
|
def initialize(describable, attrs)
|
@@ -4,9 +4,10 @@ module Mkxms::Mssql
|
|
4
4
|
class QueryCursor
|
5
5
|
def initialize(select_statement, variables, options = {})
|
6
6
|
@select_statement = select_statement
|
7
|
-
@select_statement += ';' unless @select_statement
|
7
|
+
@select_statement += ';' unless @select_statement =~ /;\s*\Z/
|
8
8
|
@cursor = options[:cursor_name] || self.class.generated_cursor_name
|
9
9
|
@out = options[:output_to] || $stdout
|
10
|
+
@indented = @out.respond_to?(:indented) ? @out.method(:indented) : ->(&blk) {blk.call}
|
10
11
|
@global = options[:global]
|
11
12
|
@indent = options[:indent] || ' '
|
12
13
|
|
@@ -54,7 +55,7 @@ module Mkxms::Mssql
|
|
54
55
|
fetch_next
|
55
56
|
@out.puts "IF @@FETCH_STATUS = 0"
|
56
57
|
@out.puts "BEGIN"
|
57
|
-
extra_action.call
|
58
|
+
indented {extra_action.call}
|
58
59
|
@out.puts "END;"
|
59
60
|
end
|
60
61
|
|
@@ -64,12 +65,15 @@ module Mkxms::Mssql
|
|
64
65
|
def test_entry(opts = {})
|
65
66
|
opts = {} unless opts.kind_of? Hash
|
66
67
|
missing_action = expectation_failure_action(opts[:on_missing]) || proc {}
|
68
|
+
@out.puts
|
67
69
|
fetch_next
|
68
70
|
@out.puts "IF @@FETCH_STATUS <> 0"
|
69
71
|
@out.puts "BEGIN"
|
70
|
-
missing_action.call
|
72
|
+
indented {missing_action.call}
|
71
73
|
@out.puts "END ELSE BEGIN"
|
72
|
-
|
74
|
+
indented {
|
75
|
+
yield
|
76
|
+
}
|
73
77
|
@out.puts "END;"
|
74
78
|
end
|
75
79
|
|
@@ -103,6 +107,10 @@ module Mkxms::Mssql
|
|
103
107
|
@out.puts "CLOSE #{cursor_scope(false)} #@cursor; DEALLOCATE #{cursor_scope(false)} #@cursor;"
|
104
108
|
end
|
105
109
|
|
110
|
+
def indented(&blk)
|
111
|
+
@indented.call(&blk)
|
112
|
+
end
|
113
|
+
|
106
114
|
def self.generated_cursor_name
|
107
115
|
@gensym_number ||= 0
|
108
116
|
"gensym_cursor_#{@gensym_number += 1}"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'mkxms/mssql/utils'
|
2
|
+
|
3
|
+
module Mkxms; end
|
4
|
+
|
5
|
+
module Mkxms::Mssql
|
6
|
+
Reference = Struct.new(:schema, :name) do
|
7
|
+
include Utils::SchemaQualifiedName
|
8
|
+
end
|
9
|
+
|
10
|
+
module Dependencies
|
11
|
+
def dependencies
|
12
|
+
@dependencies ||= []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ReferencesHandler
|
17
|
+
module ElementHandler
|
18
|
+
def handle_references_element(parse)
|
19
|
+
a = parse.node.attributes
|
20
|
+
referrer.dependencies << Reference.new(a['schema'], a['name'])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -40,14 +40,11 @@ module Mkxms::Mssql
|
|
40
40
|
|
41
41
|
def initialize(roles, node)
|
42
42
|
@role = Role.new(node.attributes['name'], owner: node.attributes['owner']).tap do |r|
|
43
|
+
store_properties_on r
|
43
44
|
roles << r
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
def extended_properties
|
48
|
-
@role.extended_properties
|
49
|
-
end
|
50
|
-
|
51
48
|
def handle_member_of_element(parse)
|
52
49
|
@role.encompassing_roles << parse.node.attributes['name']
|
53
50
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'mkxms/mssql/property_handler'
|
2
|
+
require 'mkxms/mssql/utils'
|
3
|
+
|
4
|
+
module Mkxms; end
|
5
|
+
|
6
|
+
module Mkxms::Mssql
|
7
|
+
class ScalarType
|
8
|
+
include ExtendedProperties, Property::Hosting, Property::SchemaScoped
|
9
|
+
include Utils::SchemaQualifiedName
|
10
|
+
|
11
|
+
SQL_OBJECT_TYPE = 'TYPE'
|
12
|
+
|
13
|
+
def initialize(attrs)
|
14
|
+
a = attrs
|
15
|
+
@schema = a['schema']
|
16
|
+
@name = a['name']
|
17
|
+
@base_type = a['base-type']
|
18
|
+
@capacity = a['capacity']
|
19
|
+
@capacity = @capacity.to_i unless @capacity.nil? || @capacity == 'max'
|
20
|
+
@precision = a['precision']
|
21
|
+
@scale = a['scale']
|
22
|
+
@nullable = !!a['nullable']
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :schema, :name, :base_type, :capacity, :precision, :scale, :default
|
26
|
+
|
27
|
+
def nullable?
|
28
|
+
@nullable
|
29
|
+
end
|
30
|
+
|
31
|
+
def nullable=(val)
|
32
|
+
@nullable = !!val
|
33
|
+
end
|
34
|
+
|
35
|
+
def type_spec
|
36
|
+
base_type.dup.tap do |ts|
|
37
|
+
case
|
38
|
+
when capacity
|
39
|
+
ts << "(#{capacity})"
|
40
|
+
when precision
|
41
|
+
ts << "(#{[precision, scale].compact.join(", ")})"
|
42
|
+
end
|
43
|
+
ts << " NOT NULL" unless nullable?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_sql
|
48
|
+
[].tap do |lines|
|
49
|
+
lines << "CREATE TYPE #{qualified_name}"
|
50
|
+
lines << "FROM #{type_spec};"
|
51
|
+
|
52
|
+
if default
|
53
|
+
lines << default.to_sql
|
54
|
+
lines << "EXEC sp_bindefault #{default.qualified_name.sql_quoted}, #{qualified_name.sql_quoted};"
|
55
|
+
end
|
56
|
+
end.join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
def element_size
|
60
|
+
if %w[nchar nvarchar]
|
61
|
+
2
|
62
|
+
else
|
63
|
+
1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Default
|
69
|
+
include Utils::SchemaQualifiedName
|
70
|
+
|
71
|
+
def initialize(attrs)
|
72
|
+
a = attrs
|
73
|
+
@schema = a['schema']
|
74
|
+
@name = a['name']
|
75
|
+
@definition = ""
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_accessor :schema, :name
|
79
|
+
attr_reader :definition
|
80
|
+
|
81
|
+
def to_sql
|
82
|
+
"CREATE DEFAULT #{qualified_name} AS #{definition};"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class ScalarTypeHandler
|
87
|
+
include PropertyHandler::ElementHandler
|
88
|
+
|
89
|
+
def initialize(user_types, node)
|
90
|
+
a = node.attributes
|
91
|
+
ScalarType.new(a).tap do |t|
|
92
|
+
store_properties_on t
|
93
|
+
user_types << (@type = t)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def handle_default_element(parse)
|
98
|
+
@type.default = Default.new(parse.node.attributes)
|
99
|
+
end
|
100
|
+
|
101
|
+
def handle_text(text, parent_element)
|
102
|
+
case [parent_element.namespace, parent_element.name]
|
103
|
+
when ['', 'default']
|
104
|
+
@type.default.definition << text
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -31,12 +31,9 @@ module Mkxms::Mssql
|
|
31
31
|
|
32
32
|
def initialize(schemas, node)
|
33
33
|
@schema = Schema.new(node.attributes['name'], owner: node.attributes['owner']).tap do |s|
|
34
|
+
store_properties_on s
|
34
35
|
schemas << s
|
35
36
|
end
|
36
37
|
end
|
37
|
-
|
38
|
-
def extended_properties
|
39
|
-
@schema.extended_properties
|
40
|
-
end
|
41
38
|
end
|
42
39
|
end
|
@@ -13,13 +13,13 @@ module Mkxms::Mssql
|
|
13
13
|
when margin.nil? && l =~ /^ *$/
|
14
14
|
l
|
15
15
|
when margin.nil?
|
16
|
-
margin = /^ */.match(l).length
|
17
|
-
l[margin..-1]
|
18
|
-
when s =~/^\s*$/
|
16
|
+
margin = /^ */.match(l)[0].length
|
19
17
|
l[margin..-1]
|
20
18
|
else
|
21
|
-
/^(?:
|
19
|
+
/^(?: {0,#{margin}})(.*)/m.match(l)[1]
|
22
20
|
end
|
21
|
+
end.tap do |lines|
|
22
|
+
lines.shift if lines.first == "\n"
|
23
23
|
end.join('')
|
24
24
|
end
|
25
25
|
|
@@ -44,14 +44,11 @@ module Mkxms::Mssql
|
|
44
44
|
a = node.attributes
|
45
45
|
|
46
46
|
@statistics = Statistics.new(a).tap do |s|
|
47
|
+
store_properties_on s
|
47
48
|
statistics_objs << s
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
def extended_properties
|
52
|
-
@statistics.extended_properties
|
53
|
-
end
|
54
|
-
|
55
52
|
def handle_column_element(parse)
|
56
53
|
@statistics.columns << parse.node.attributes['name']
|
57
54
|
end
|
@@ -38,14 +38,11 @@ module Mkxms::Mssql
|
|
38
38
|
a = node.attributes
|
39
39
|
|
40
40
|
@procedure = StoredProcedure.new(a).tap do |sp|
|
41
|
+
store_properties_on sp
|
41
42
|
procedures << sp
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
def extended_properties
|
46
|
-
@procedure.extended_properties
|
47
|
-
end
|
48
|
-
|
49
46
|
def handle_definition_element(parse); end
|
50
47
|
|
51
48
|
def handle_references_element(parse); end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'mkxms/mssql/property_handler'
|
2
|
+
require 'mkxms/mssql/utils'
|
3
|
+
|
4
|
+
module Mkxms; end
|
5
|
+
|
6
|
+
module Mkxms::Mssql
|
7
|
+
class Synonym
|
8
|
+
include ExtendedProperties, Property::Hosting, Property::SchemaScoped
|
9
|
+
include Utils::SchemaQualifiedName
|
10
|
+
|
11
|
+
SQL_OBJECT_TYPE = 'SYNONYM'
|
12
|
+
|
13
|
+
def initialize(schema, name, referent)
|
14
|
+
@schema = schema
|
15
|
+
@name = name
|
16
|
+
@referent = referent
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :schema, :name, :referent
|
20
|
+
|
21
|
+
def to_sql
|
22
|
+
[].tap do |lines|
|
23
|
+
lines << "CREATE SYNONYM #{qualified_name} FOR #{referent};"
|
24
|
+
lines.concat extended_properties_sql
|
25
|
+
end.join("\n")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class SynonymHandler
|
30
|
+
include PropertyHandler::ElementHandler
|
31
|
+
|
32
|
+
def initialize(synonyms, node)
|
33
|
+
a = node.attributes
|
34
|
+
Synonym.new(a['schema'], a['name'], a['for']).tap do |syn|
|
35
|
+
store_properties_on syn
|
36
|
+
synonyms << (@synonym = syn)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -128,16 +128,13 @@ module Mkxms::Mssql
|
|
128
128
|
c.flags << :replicated if a['replicated']
|
129
129
|
c.flags << :filestream if a['filestream']
|
130
130
|
c.type_info.update(col_attrs)
|
131
|
+
store_properties_on c
|
131
132
|
columns << c
|
132
133
|
end
|
133
134
|
end
|
134
135
|
|
135
136
|
attr_reader :column
|
136
137
|
|
137
|
-
def extended_properties
|
138
|
-
@column.extended_properties
|
139
|
-
end
|
140
|
-
|
141
138
|
def handle_computed_expression_element(parse)
|
142
139
|
column.flags << :persisted if parse.node.attributes['persisted']
|
143
140
|
# Handle expression in #handle_text
|
@@ -156,6 +153,7 @@ module Mkxms::Mssql
|
|
156
153
|
def initialize(tables, node)
|
157
154
|
a = node.attributes
|
158
155
|
@table = Table.new(a['schema'], a['name']).tap do |t|
|
156
|
+
store_properties_on t
|
159
157
|
tables << t
|
160
158
|
end
|
161
159
|
@table.owner = a['owner']
|
@@ -165,10 +163,6 @@ module Mkxms::Mssql
|
|
165
163
|
@rowguid_column = a['rowguidcol']
|
166
164
|
end
|
167
165
|
|
168
|
-
def extended_properties
|
169
|
-
@table.extended_properties
|
170
|
-
end
|
171
|
-
|
172
166
|
def handle_column_element(parse)
|
173
167
|
parse.context = ColumnHandler.new(@table.columns, parse.node)
|
174
168
|
column = parse.context.column
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'mkxms/mssql/property_handler'
|
2
|
+
require 'mkxms/mssql/utils'
|
3
|
+
|
4
|
+
module Mkxms; end
|
5
|
+
|
6
|
+
module Mkxms::Mssql
|
7
|
+
class TableType
|
8
|
+
class Column
|
9
|
+
include ExtendedProperties
|
10
|
+
extend Utils::InitializedAttributes
|
11
|
+
|
12
|
+
SQL_OBJECT_TYPE = 'COLUMN'
|
13
|
+
|
14
|
+
def initialize(attrs)
|
15
|
+
a = attrs
|
16
|
+
@name = a['name']
|
17
|
+
@type_schema = a['type-schema']
|
18
|
+
@type_name = a['type']
|
19
|
+
@capacity = a['capacity']
|
20
|
+
@capacity = @capacity.to_i unless @capacity.nil? || @capacity == 'max'
|
21
|
+
@precision = a['precision']
|
22
|
+
@scale = a['scale']
|
23
|
+
@collation = a['collation']
|
24
|
+
@nullable = !!a['nullable']
|
25
|
+
@ansi_padded = !a['not-ansi-padded']
|
26
|
+
@full_xml_document = !!a['full-xml-document']
|
27
|
+
@xml_schema_collection = a['xml_collection_id']
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :name, :type_schema, :type_name, :capacity, :precision, :scale, :collation, :xml_schema_collection
|
31
|
+
attr_accessor :computed_expression
|
32
|
+
attr_init(:check_constraints) {[]}
|
33
|
+
|
34
|
+
def nullable?; @nullable; end
|
35
|
+
def nullable=(val); @nullable = !!val; end
|
36
|
+
|
37
|
+
def ansi_padded?; @ansi_padded; end
|
38
|
+
def ansi_padded=(val); @ansi_padded = !!val; end
|
39
|
+
|
40
|
+
def full_xml_document?; @full_xml_document; end
|
41
|
+
def full_xml_document=(val); @full_xml_document = !!val; end
|
42
|
+
|
43
|
+
def type_spec
|
44
|
+
[type_schema, type_name].compact.join('.').tap do |result|
|
45
|
+
result << "(#{capacity})" if capacity
|
46
|
+
result << " COLLATE #{collation}" if collation
|
47
|
+
result << "(#{[precision, scale].compact.join(', ')})" if precision
|
48
|
+
result << ' NOT NULL' unless nullable?
|
49
|
+
check_constraints.each do |c|
|
50
|
+
result << " #{c.to_sql}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def max_byte_consumption
|
56
|
+
if [nil, '[sys]'].include?(type_schema) && %w[[nchar] [nvarchar]].include?(type_name)
|
57
|
+
2 * capacity
|
58
|
+
else
|
59
|
+
capacity
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ConstraintColumn
|
65
|
+
def initialize(attrs)
|
66
|
+
@name = attrs['name']
|
67
|
+
@ascending = !attrs['desc']
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_accessor :name
|
71
|
+
|
72
|
+
def ascending?; @ascending; end
|
73
|
+
def descending?; !@ascending; end
|
74
|
+
def ascending=(val); @ascending = !!val; end
|
75
|
+
def descending=(val); @ascending = !val; end
|
76
|
+
|
77
|
+
def spec
|
78
|
+
"#{name} #{ascending? ? "ASC" : "DESC"}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class KeyConstraint
|
83
|
+
include ExtendedProperties
|
84
|
+
extend Utils::InitializedAttributes
|
85
|
+
|
86
|
+
SQL_OBJECT_TYPE = 'CONSTRAINT'
|
87
|
+
|
88
|
+
def initialize(attrs)
|
89
|
+
@type = attrs['type']
|
90
|
+
@clustered = !!attrs['clustered']
|
91
|
+
@ignore_duplicates = !!attrs['ignore-duplicates']
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_accessor :type, :ignore_duplicates
|
95
|
+
attr_init(:columns) {[]}
|
96
|
+
|
97
|
+
def clustered?; @clustered; end
|
98
|
+
def clustered=(val); @clustered = !!val; end
|
99
|
+
|
100
|
+
def ignore_duplicates?; @ignore_duplicates; end
|
101
|
+
def ignore_duplicates=(val); @ignore_duplicates = !!val; end
|
102
|
+
|
103
|
+
def to_sql
|
104
|
+
"#{type} #{clustered? ? "CLUSTERED" : "NONCLUSTERED"} (#{
|
105
|
+
columns.map(&:spec).join(', ')
|
106
|
+
})".tap do |result|
|
107
|
+
result << " WITH (IGNORE_DUP_KEY = ON)" if ignore_duplicates?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class CheckConstraint
|
113
|
+
include ExtendedProperties
|
114
|
+
extend Utils::InitializedAttributes
|
115
|
+
|
116
|
+
SQL_OBJECT_TYPE = 'CONSTRAINT'
|
117
|
+
|
118
|
+
def initialize(attrs)
|
119
|
+
end
|
120
|
+
|
121
|
+
attr_init(:expression) {''}
|
122
|
+
|
123
|
+
def type
|
124
|
+
"CHECK"
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_sql
|
128
|
+
"CHECK #{expression}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
include ExtendedProperties, Property::Hosting, Property::SchemaScoped
|
133
|
+
include Utils::SchemaQualifiedName
|
134
|
+
extend Utils::InitializedAttributes
|
135
|
+
|
136
|
+
SQL_OBJECT_TYPE = 'TYPE'
|
137
|
+
|
138
|
+
def initialize(attrs)
|
139
|
+
a = attrs
|
140
|
+
info_ver = (a['eyewkas_ver'] || 1.0).to_f
|
141
|
+
raise "mssql-eyewkas table-type ver. 1.1 or compatible required" if info_ver < 1.1 || info_ver >= 2
|
142
|
+
@schema = a['schema']
|
143
|
+
@name = a['name']
|
144
|
+
end
|
145
|
+
|
146
|
+
attr_accessor :schema, :name
|
147
|
+
attr_init(:columns, :constraints) {[]}
|
148
|
+
|
149
|
+
def to_sql
|
150
|
+
[].tap do |lines|
|
151
|
+
lines << "CREATE TYPE #{qualified_name} AS TABLE ("
|
152
|
+
columns.each_with_index do |col, i|
|
153
|
+
lines << " #{i == 0 ? " " : ","} #{col.name} #{col.type_spec}"
|
154
|
+
end
|
155
|
+
constraints.each do |c|
|
156
|
+
lines << " , #{c.to_sql}"
|
157
|
+
end
|
158
|
+
lines << ");"
|
159
|
+
lines << extended_properties_sql
|
160
|
+
columns.each do |col|
|
161
|
+
lines << subitem_extended_properties_sql(col)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class TableTypeColumnHandler
|
168
|
+
include PropertyHandler::ElementHandler
|
169
|
+
|
170
|
+
def initialize(column)
|
171
|
+
store_properties_on(@column = column)
|
172
|
+
end
|
173
|
+
|
174
|
+
def handle_computed_expression_element(parse)
|
175
|
+
# Do nothing
|
176
|
+
end
|
177
|
+
|
178
|
+
def handle_check_constraint_element(parse)
|
179
|
+
parse.delegate_to TableTypeCheckConstraintHandler, @column.check_constraints
|
180
|
+
end
|
181
|
+
|
182
|
+
def handle_text(text, parent_element)
|
183
|
+
case [parent_element.namespace, parent_element.name]
|
184
|
+
when ['', 'computed-expression']
|
185
|
+
(@column.computed_expression ||= '') << text
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class TableTypeCheckConstraintHandler
|
191
|
+
def initialize(constraints, node)
|
192
|
+
TableType::CheckConstraint.new(node.attributes).tap do |c|
|
193
|
+
constraints << (@constraint = c)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def handle_expression_element(parse)
|
198
|
+
# do nothing
|
199
|
+
end
|
200
|
+
|
201
|
+
def handle_text(text, parent_element)
|
202
|
+
case [parent_element.namespace, parent_element.name]
|
203
|
+
when ['', 'expression']
|
204
|
+
@constraint.expression << text
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def handle_property_element(parse)
|
209
|
+
raise "Properties on table type constraints are unsupported"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class TableTypeKeyConstraintHandler
|
214
|
+
def initialize(constraints, node)
|
215
|
+
TableType::KeyConstraint.new(node.attributes).tap do |c|
|
216
|
+
constraints << (@constraint = c)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def handle_column_element(parse)
|
221
|
+
@constraint.columns << TableType::ConstraintColumn.new(parse.node.attributes)
|
222
|
+
end
|
223
|
+
|
224
|
+
def handle_property_element(parse)
|
225
|
+
raise "Properties on table type constraints are unsupported"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class TableTypeHandler
|
230
|
+
include PropertyHandler::ElementHandler
|
231
|
+
|
232
|
+
def initialize(user_types, node)
|
233
|
+
TableType.new(node.attributes).tap do |tt|
|
234
|
+
user_types << store_properties_on(@type = tt)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def handle_column_element(parse)
|
239
|
+
a = parse.node.attributes
|
240
|
+
TableType::Column.new(parse.node.attributes).tap do |c|
|
241
|
+
@type.columns << c
|
242
|
+
parse.context = TableTypeColumnHandler.new(c)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def handle_key_constraint_element(parse)
|
247
|
+
parse.delegate_to TableTypeKeyConstraintHandler, @type.constraints
|
248
|
+
end
|
249
|
+
|
250
|
+
def handle_check_constraint_element(parse)
|
251
|
+
parse.delegate_to TableTypeCheckConstraintHandler, @type.constraints
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|