1-- ATL Transformation: Class to Relational
2-- This transformation converts a simplified UML class model into a relational database schema
3-- Based on the classic TTC benchmark case
4-- @path Class=/Class2Relational/class.ecore
5-- @path Relational=/Class2Relational/relational.ecore
6
7module Class2Relational;
8create OUT: Relational from IN: Class;
9
10-- ===========================================================================
11-- Helper: Get all attributes including inherited ones
12-- ===========================================================================
13helper context Class!Class def: allAttributes: Sequence(Class!Attribute) =
14 if self.superType.oclIsUndefined() then
15 self.attributes->asSequence()
16 else
17 self.superType.allAttributes->union(self.attributes->asSequence())
18 endif;
19
20-- ===========================================================================
21-- Helper: Check if a classifier is a DataType (primitive type)
22-- ===========================================================================
23helper context Class!Classifier def: isDataType: Boolean =
24 self.oclIsTypeOf(Class!DataType);
25
26-- ===========================================================================
27-- Helper: Map class type to SQL type name
28-- ===========================================================================
29helper context Class!DataType def: sqlTypeName: String =
30 if self.name = 'Integer' then
31 'INTEGER'
32 else if self.name = 'Boolean' then
33 'BOOLEAN'
34 else if self.name = 'String' then
35 'VARCHAR(255)'
36 else if self.name = 'Double' or self.name = 'Float' then
37 'DECIMAL(10,2)'
38 else if self.name = 'Date' then
39 'DATE'
40 else
41 'VARCHAR(255)'
42 endif endif endif endif endif;
43
44-- ===========================================================================
45-- Rule: Package -> Schema
46-- Transforms a class package into a database schema
47-- ===========================================================================
48rule Package2Schema {
49 from
50 p: Class!Package
51 to
52 s: Relational!Schema (
53 name <- p.name,
54 tables <- p.classifiers->select(c | c.oclIsTypeOf(Class!Class)),
55 types <- p.classifiers->select(c | c.oclIsTypeOf(Class!DataType))
56 )
57}
58
59-- ===========================================================================
60-- Rule: DataType -> Type
61-- Maps primitive data types to SQL types
62-- ===========================================================================
63rule DataType2Type {
64 from
65 dt: Class!DataType
66 to
67 t: Relational!Type (
68 name <- dt.sqlTypeName
69 )
70}
71
72-- ===========================================================================
73-- Rule: Class -> Table
74-- Transforms a class into a database table with primary key
75-- ===========================================================================
76rule Class2Table {
77 from
78 c: Class!Class (not c.isAbstract)
79 to
80 t: Relational!Table (
81 name <- c.name,
82 columns <- Sequence{key}->union(
83 c.allAttributes->select(a | not a.multiValued and a.type.isDataType)
84 ),
85 key <- Sequence{key}
86 ),
87 key: Relational!Column (
88 name <- 'id',
89 type <- thisModule.resolveTemp(
90 Class!DataType.allInstances()->any(dt | dt.name = 'Integer'),
91 't'
92 ),
93 keyOf <- t
94 )
95}
96
97-- ===========================================================================
98-- Rule: SingleValuedAttribute -> Column
99-- Transforms single-valued primitive attributes to table columns
100-- ===========================================================================
101rule SingleValuedAttribute2Column {
102 from
103 a: Class!Attribute (
104 not a.multiValued and
105 a.type.oclIsTypeOf(Class!DataType)
106 )
107 to
108 c: Relational!Column (
109 name <- a.name,
110 type <- a.type
111 )
112}
113
114-- ===========================================================================
115-- Rule: MultiValuedAttribute -> Table
116-- Transforms multi-valued attributes to separate tables with foreign key
117-- ===========================================================================
118rule MultiValuedAttribute2Table {
119 from
120 a: Class!Attribute (
121 a.multiValued and
122 a.type.oclIsTypeOf(Class!DataType)
123 )
124 to
125 t: Relational!Table (
126 name <- a.owner.name + '_' + a.name,
127 columns <- Sequence{id, value}
128 ),
129 id: Relational!Column (
130 name <- a.owner.name.toLower() + '_id',
131 type <- thisModule.resolveTemp(
132 Class!DataType.allInstances()->any(dt | dt.name = 'Integer'),
133 't'
134 )
135 ),
136 value: Relational!Column (
137 name <- a.name,
138 type <- a.type
139 )
140}
141
142-- ===========================================================================
143-- Rule: ClassAttribute -> ForeignKey
144-- Transforms class-typed attributes to foreign key columns
145-- ===========================================================================
146rule ClassAttribute2ForeignKey {
147 from
148 a: Class!Attribute (
149 not a.multiValued and
150 a.type.oclIsTypeOf(Class!Class)
151 )
152 to
153 fk: Relational!Column (
154 name <- a.name + '_id',
155 type <- thisModule.resolveTemp(
156 Class!DataType.allInstances()->any(dt | dt.name = 'Integer'),
157 't'
158 )
159 )
160}