Datei: NDODLL/SchemaTransitionGenerator.cs

Last Commit (e4584c5)
1 //
2 // Copyright (c) 2002-2024 Mirko Matytschak
3 // (www.netdataobjects.de)
4 //
5 // Author: Mirko Matytschak
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10 // Software, and to permit persons to whom the Software is furnished to do so, subject to the following
11 // conditions:
12
13 // The above copyright notice and this permission notice shall be included in all copies or substantial portions
14 // of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22
23 using System;
24 using System.Text;
25 using System.Collections.Generic;
26 using System.Data;
27 using System.Linq;
28 using System.Xml.Linq;
29 using NDO.Mapping;
30 using NDOInterfaces;
31 using NDO.ProviderFactory;
32
33 namespace NDO
34 {
35 ····/// <summary>
36 ····/// The SchemaTransitionGenerator class transforms an Xml description to SQL DDL statements for a concrete Database.
37 ····/// </summary>
38 ····public class SchemaTransitionGenerator
39 ····{
40 ········private readonly IProvider provider;
41 ········private readonly ISqlGenerator concreteGenerator;
42 ········private readonly NDOMapping mappings;
43
44 ········/// <summary>
45 ········/// SchemaTransitionGenerator constructor
46 ········/// </summary>
47 ········/// <param name="providerFactory">Factory to get the concrete provider for the database.</param>
48 ········/// <param name="providerName">The name of the concrete provider.</param>
49 ········/// <param name="mappings">The mapping information of the current application.</param>
50 ········public SchemaTransitionGenerator( INDOProviderFactory providerFactory, string providerName, NDOMapping mappings )
51 ········{
52 ············this.provider = providerFactory[providerName];
53 ············this.concreteGenerator = providerFactory.Generators[providerName];
54 ············this.mappings = mappings;
55 ········}
56
57 ········/// <summary>
58 ········/// Transforms an Xml description to SQL DDL statements for a concrete Database.
59 ········/// </summary>
60 ········/// <param name="transElement"></param>
61 ········/// <returns></returns>
62 ········public string Generate(XElement transElement)
63 ········{
64 ············StringBuilder sb = new StringBuilder();
65 ············foreach(XElement actionElement in transElement.Elements())
66 ············{
67 ················if (actionElement.Name == "DropTable")
68 ················{
69 ····················sb.Append( DropTable( actionElement ) );
70 ················}
71 ················else if (actionElement.Name == "CreateTable")
72 ················{
73 ····················sb.Append( CreateTable( actionElement ) );
74 ················}
75 ················else if (actionElement.Name == "AlterTable")
76 ················{
77 ····················sb.Append( ChangeTable( actionElement ) );
78 ················}
79 ················else if (actionElement.Name == "CreateIndex")
80 ················{
81 ····················sb.Append( CreateIndex( actionElement ) );
82 ················}
83 ············}
84 ············return sb.ToString();
85 ········}
86
87 ········string ChangeTable(XElement actionElement)
88 ········{
89 ············List<XElement> addedColumns = actionElement.Elements("AddColumn").ToList();
90 ············List<XElement> removedColumns = actionElement.Elements("DropColumn").ToList();
91 ············List<XElement> changedColumns = actionElement.Elements("AlterColumn").ToList();
92
93 ············if (addedColumns.Count == 0 && removedColumns.Count == 0 && changedColumns.Count == 0)
94 ················return String.Empty;
95
96 ············StringBuilder sb = new StringBuilder();
97
98 ············string rawTableName = actionElement.Attribute( "name" ).Value;
99 ············string tableName = this.provider.GetQualifiedTableName(rawTableName);
100 ············string alterString = "ALTER TABLE " + tableName + ' ';
101
102 ············foreach(XElement columnElement in addedColumns)
103 ············{
104 ················sb.Append(alterString);
105 ················sb.Append(concreteGenerator.AddColumn());
106 ················sb.Append(' ');
107 sb. Append( CreateColumn( columnElement, GetClassForTable( rawTableName, this. mappings) , this. provider, false) ) ;
108 ················sb.Append(";\n");
109 ············}
110
111 ············foreach(XElement columnElement in removedColumns)
112 ············{
113 ················sb.Append(alterString);
114 ················sb.Append( concreteGenerator.RemoveColumn( provider.GetQuotedName( columnElement.Attribute( "name" ).Value ) ) );
115 ················sb.Append(";\n");
116 ············}
117
118 ············foreach(XElement columnElement in changedColumns)
119 ············{
120 ················sb.Append(alterString);
121 ················sb.Append(concreteGenerator.AlterColumnType());
122 ················sb.Append(' ');
123 sb. Append( CreateColumn( columnElement, GetClassForTable( rawTableName, this. mappings) , this. provider, false) ) ;
124 ················sb.Append(";\n");
125 ············}
126
127 ············return sb.ToString();
128 ········}
129
130 ········string RenameColumn(string tableName, string oldColumn, string newColumn, string typeString)
131 ········{
132 ············string s = concreteGenerator.RenameColumn(tableName, oldColumn, newColumn, typeString);
133 ············
134 ············if (s != string.Empty)
135 ············return s;
136 ········
137 ············string alterString = "ALTER TABLE " + tableName + ' ';
138
139 ············StringBuilder sb = new StringBuilder(alterString);
140 ············sb.Append(concreteGenerator.AddColumn());
141 ············sb.Append(' ');
142 ············sb.Append(newColumn);
143 ············sb.Append(' ');
144 ············sb.Append(typeString);
145 ············sb.Append(";\n");
146
147 ············sb.Append("UPDATE ");
148 ············sb.Append(tableName);
149 ············sb.Append(" SET ");············
150 ············sb.Append(newColumn);
151 ············sb.Append(" = ");
152 ············sb.Append(oldColumn);
153 ············sb.Append(";\n");
154
155 ············sb.Append(alterString);
156 ············sb.Append(concreteGenerator.RemoveColumn(oldColumn));
157 ············sb.Append(';');
158 ············return sb.ToString();
159 ········}
160
 
 
 
 
 
161 ········protected string DropTable(XElement actionElement)
162 ········{
163 ············string tableName = this.provider .GetQualifiedTableName( actionElement.Attribute( "name" ).Value );
164 ············return concreteGenerator.DropTable( tableName );
165 ········}
166
 
 
 
 
 
 
167 ········protected string CreateTable(XElement actionElement)
168 ········{
169 ············StringBuilder sb = new StringBuilder();
170 ············IProvider provider = NDOProviderFactory.Instance[concreteGenerator.ProviderName];
171 ············if (provider == null)
172 ················throw new Exception("Can't find NDO provider '" + concreteGenerator.ProviderName + "'.");
173
174 ············string dtTableName = actionElement.Attribute( "name" ).Value;
175
176 ············string tableName = this.provider.GetQualifiedTableName( dtTableName );
177
178 ············Class cl = FindClass(dtTableName, mappings);
179
180 ············if (cl != null)
181 ············{
182 ················Connection conn = mappings.FindConnection(cl);
183 ················if (conn != null)
184 ····················concreteGenerator.ConnectToDatabase(conn.Name);
185 ············}
186
187 ············sb.Append(concreteGenerator.BeginnTable(tableName));
188 ············sb.Append('\n');
189
190 ············List<XElement> columnElements = actionElement.Elements( "CreateColumn" ).ToList();
191
192 ············int vorletzterIndex = columnElements.Count - 1;
193 ············List<XElement> primaryKeyColumns = columnElements.Where( e =>
194 ············{
195 ················XAttribute attr;
196 ················return (attr = e.Attribute( "isPrimary" )) != null && String.Compare( attr.Value, "true", true ) == 0;
197 ············} ).ToList();
198
199 ············bool hasPrimaryKeyColumns = primaryKeyColumns.Count > 0;
200
201 ············for (int i = 0; i < columnElements.Count; i++)
202 ············{
203 ················XElement columnElement = columnElements[i];
204 ················string columnName = columnElement.Attribute("name").Value;
205
206 ················bool isPrimary = primaryKeyColumns.Any( e => e.Attribute( "name" ).Value == columnName );
207
208 ················sb.Append(CreateColumn(columnElement, cl, provider, isPrimary));
209 ················if (i < vorletzterIndex)
210 ················{
211 ····················sb.Append(",");
212 ····················sb.Append('\n');
213 ················}
214 ············}
215
216 ············if(concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.InTable
217 ················&& hasPrimaryKeyColumns)
218 ············{
219 ················sb.Append(",");
220 ················sb.Append('\n');
221 ············}
222
223 ············if (hasPrimaryKeyColumns && concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.InTable)
224 ············{
225 ················sb.Append(CreatePrimaryKeyConstraint(primaryKeyColumns, dtTableName, provider));
226 ············}
227 ············else
228 ············{
229 ················sb.Append('\n');
230 ············}
231 ············sb.Append(concreteGenerator.EndTable(tableName));
232 ············sb.Append('\n');
233
234 ············//············CreateIndex(primaryKeyColumns, sb, dt, provider);
235
236 ············if (hasPrimaryKeyColumns && concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.AfterTable)
237 ············{
238 ················sb.Append(CreatePrimaryKeyConstraint(primaryKeyColumns, dtTableName, provider));
239 ············}
240
241 ············sb.Append('\n');
242
243 ············return sb.ToString();
244 ········}
245
246 ········string CreateIndex(XElement actionElement)
247 ········{
248 ············//<CreateIndex name="xxx" unique="True|False" fulltext="True|False" onTable="TableName">
249 ············//··<Column name="xxx" desc="True|False">
250 ············//</CreateIndex>
251 ············StringBuilder sb = new StringBuilder();
252 ············IProvider provider = NDOProviderFactory.Instance[concreteGenerator.ProviderName];
253 ············if (provider == null)
254 ················throw new Exception( "Can't find NDO provider '" + concreteGenerator.ProviderName + "'." );
255
256 ············string tableName··= this.provider.GetQualifiedTableName( actionElement.Attribute( "onTable" ).Value );
257 ············string indexName = this.provider.GetQualifiedTableName( actionElement.Attribute( "name" ).Value );
258
259 ············sb.Append( "CREATE " );
260
261 ············// NDO doesn't check, if these keywords are supported by the database.
262 ············if (String.Compare( actionElement.Attribute( "unique" )?.Value, "true", true ) == 0)
263 ················sb.Append( "UNIQUE " );
264 ············if (String.Compare( actionElement.Attribute( "fulltext" )?.Value, "true", true ) == 0)
265 ················sb.Append( "FULLTEXT " );
266
267 ············sb.Append( "INDEX " );
268 ············sb.Append( indexName );
269 ············sb.Append( " ON " );
270 ············sb.Append( tableName );
271 ············sb.Append( " (" );
272
273 ············var columns = actionElement.Elements( "Column" ).ToList();
274
275 ············var lastIndex = columns.Count - 1;
276 ············for (int i = 0; i <= lastIndex; i++)
277 ············{
278 ················var columnElement = columns[i];
279 ················var columnName = provider.GetQuotedName( columnElement.Attribute( "name" )?.Value );
280 ················
281 ················if (columnName == null)
282 ····················throw new Exception( "Column element of CreateIndex needs a name attribute" );
283
284 ················sb.Append( columnName );
285 ················
286 ················// NDO doesn't check, if DESC is supported by the database.
287 ················// We also assume, that ASC is the standard case, so we don't emit the ASC keyword.
288 ················var desc = String.Compare( columnElement.Attribute( "desc" )?.Value, "true", true ) == 0;
289 ················if (desc)
290 ····················sb.Append( " DESC" );
291
292 ················if (i < lastIndex)
293 ····················sb.Append( ", " );
294 ············}
295
296 ············sb.Append( ")" );
297
298 ············return sb.ToString();
299 ········}
300
301 protected NDO. Mapping. Class FindClass( string tableName, NDOMapping mappings)
 
 
 
 
 
 
302 ········{
303 ············Class result = null;
304 ············foreach(Class cl in mappings.Classes)
305 ············{
306 ················if (cl.TableName == tableName)
307 ················{
308 ····················result = cl;
309 ····················break;
310 ················}
311 ············}
312 ············return result;
313 ········}
314
315 ········static bool IsNumeric( Type type )
316 ········{
317 ············switch (Type.GetTypeCode( type ))
318 ············{
319 ················case TypeCode.Byte:
320 ················case TypeCode.SByte:
321 ················case TypeCode.UInt16:
322 ················case TypeCode.UInt32:
323 ················case TypeCode.UInt64:
324 ················case TypeCode.Int16:
325 ················case TypeCode.Int32:
326 ················case TypeCode.Int64:
327 ················case TypeCode.Decimal:
328 ················case TypeCode.Double:
329 ················case TypeCode.Single:
330 ····················return true;
331 ················case TypeCode.Object:
332 ····················if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof( Nullable<> ))
333 ····················{
334 ························return IsNumeric(Nullable.GetUnderlyingType( type ));
335 ····················}
336 ····················return false;
337 ················default:
338 ····················return false;
339 ············}
340 ········}
341
 
 
 
 
 
 
 
 
342 ········protected string CreateColumn(XElement columnElement, Class cl, IProvider provider, bool isPrimary)
343 ········{
344 ············string rawName = columnElement.Attribute( "name" ).Value;
345 ············Type dcDataType = Type.GetType( columnElement.Attribute( "type" ).Value );
346 ············string name = provider.GetQuotedName(rawName);
347 ············string columnType = columnElement.Attribute( "dbtype" )?.Value;
348 ············if (columnType == String.Empty)··// Make sure the column type will be infered, if it is an empty string
349 ················columnType = null;
350 ············string width = columnElement.Attribute("size") == null ? null : columnElement.Attribute("size").Value;
351 ············string precision = null;
352 ············bool autoIncrement = false;
353 ············StringBuilder sb = new StringBuilder();
354 ············bool allowNull = true;
355 ············string defaultValue = columnElement.Attribute( "default" )?.Value;
356
357 ············if (cl != null)
358 ············{
359 ················Field field = FindField(rawName, cl);
360
361 ················if (null != field)
362 ················{
363 ····················if ( !String.IsNullOrEmpty(columnType) && null != field.Column.DbType)
364 ························columnType = field.Column.DbType;
365 ····················if (0 != field.Column.Size && !field.Column.IgnoreColumnSizeInDDL)
366 ····················{
367 ························int dl = field.Column.Size;
368 ························if (dl == -1)
369 ····························width = "max";
370 ························else
371 ····························width = dl.ToString();
372 ····················}
373 ····················if (0 != field.Column.Precision && !field.Column.IgnoreColumnSizeInDDL)
374 ························precision = field.Column.Precision.ToString();
375 ····················allowNull = field.Column.AllowDbNull;
376 ················}
377 ················else if (cl.TimeStampColumn == rawName)
378 ················{
379 ····················if (!provider.SupportsNativeGuidType)
380 ························width = "36";
381 ················}
382 ················else if (isPrimary && columnElement.Attribute( "autoIncrement" ) != null && String.Compare(columnElement.Attribute( "autoIncrement" ).Value, "true", true) == 0)
383 ················{
384 ····················autoIncrement = true;
385 ················}
386 ············}
387 ············if (null == columnType)
388 ············{
389 ················try
390 ················{
391 ····················columnType = concreteGenerator.DbTypeFromType(dcDataType);
392 ················}
393 ················catch
394 ················{
395 ····················System.Diagnostics.Debug.Write("");
396 ················}
397 ············}
398
399 ············if (null == width)
400 ············{
401 ················int dl = provider.GetDefaultLength(dcDataType);
402 ················if (dl != 0)
403 ················{
404 ····················if (dl == -1)
405 ························width = "max";
406 ····················else
407 ························width = dl.ToString();
408 ················}
409 ············}
410 ············
411 ············// Because there is no GetDefaultPrecision in the provider...
412 ············// We assume the field to represent currency data
413 ············if (precision == null && dcDataType == typeof(decimal))
414 ············{
415 ················precision = "2";
416 ············}
417
418 ············if (columnType != null)
419 ············{
420 ················if (!concreteGenerator.LengthAllowed(columnType))
421 ····················width = null;
422 ············}
423 ············else
424 ············{
425 ················if (!concreteGenerator.LengthAllowed(dcDataType))
426 ····················width = null;
427 ············}
428
429
430 ············if (autoIncrement && concreteGenerator.HasSpecialAutoIncrementColumnFormat)
431 ················sb.Append(concreteGenerator.AutoIncrementColumn(name, dcDataType, columnType, width, isPrimary));
432 ············else if(isPrimary && concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.InColumn)
433 ················sb.Append(concreteGenerator.PrimaryKeyColumn(name, dcDataType, columnType, width));
434 ············else if (width != null && precision != null)
435 ················sb.Append(name + " " + columnType + "(" + width + "," + precision + ")");
436 ············else if (width != null)
437 ················sb.Append(name + " " + columnType + "(" + width + ")");············
438 ············else
439 ················sb.Append(name + " " + columnType);
440
441 ············sb.Append(" ");
442
443 ············if (defaultValue != null)
444 ············{
445 ················sb.Append( "DEFAULT " );
446 ················if (IsNumeric( dcDataType ))
447 ····················sb.Append( defaultValue);
448 ················else
449 ················{
450 ····················sb.Append( '\'' );
451 ····················sb.Append( defaultValue.Replace( "'", "''" ) );
452 ····················sb.Append( '\'' );
453 ················}
454 ················sb.Append( ' ' );
455 ············}
456
457 ············sb.Append( concreteGenerator.NullExpression( allowNull && columnElement.Attribute( "allowNull" ) != null && String.Compare( columnElement.Attribute( "allowNull" ).Value, "true", true ) == 0 ) );
458 ············return sb.ToString();
459 ········}
460
 
 
 
 
 
 
 
461 ········protected string CreatePrimaryKeyConstraint(List<XElement> primaryKeyColumns, string tableName, IProvider provider)
462 ········{
463 ············if (primaryKeyColumns.Count == 0)
464 ················return string.Empty;
465 ············string[] strArr = tableName.Split('.');
466 ············string constraintName = provider.GetQuotedName("PK_" + strArr[strArr.Length - 1]);
467 ············DataColumn[] pkColumns = (from pk in primaryKeyColumns select new DataColumn( pk.Attribute( "name" ).Value )).ToArray();
468 ············return concreteGenerator.CreatePrimaryKeyConstraint(pkColumns, constraintName, provider.GetQualifiedTableName(tableName)) + '\n';
469 ········}
470
 
 
 
 
 
 
471 ········protected Field FindField (string columnName, Class cl)
472 ········{
473 ············Field result = null;
474 ············foreach (Field field in cl.Fields)
475 ············{
476 ················if (field.Column.Name == columnName)
477 ················{
478 ····················result = field;
479 ····················break;
480 ················}
481 ············}
482 ············return result;
483 ········}
484
485 ········protected Class GetClassForTable(string tableName, NDOMapping mapping)
486 ········{
487 ············foreach(Class cl in mapping.Classes)
488 ················if (cl.TableName == tableName)
489 ····················return cl;
490 ············return null;
491 ········}
492 ····}
493 }
494
New Commit (3917c60)
1 //
2 // Copyright (c) 2002-2024 Mirko Matytschak
3 // (www.netdataobjects.de)
4 //
5 // Author: Mirko Matytschak
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10 // Software, and to permit persons to whom the Software is furnished to do so, subject to the following
11 // conditions:
12
13 // The above copyright notice and this permission notice shall be included in all copies or substantial portions
14 // of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22
23 using System;
24 using System.Text;
25 using System.Collections.Generic;
26 using System.Data;
27 using System.Linq;
28 using System.Xml.Linq;
29 using NDO.Mapping;
30 using NDOInterfaces;
31 using NDO.ProviderFactory;
32
33 namespace NDO
34 {
35 ····/// <summary>
36 ····/// The SchemaTransitionGenerator class transforms an Xml description to SQL DDL statements for a concrete Database.
37 ····/// </summary>
38 ····public class SchemaTransitionGenerator
39 ····{
40 ········private readonly IProvider provider;
41 ········private readonly ISqlGenerator concreteGenerator;
42 ········private readonly NDOMapping mappings;
43
44 ········/// <summary>
45 ········/// SchemaTransitionGenerator constructor
46 ········/// </summary>
47 ········/// <param name="providerFactory">Factory to get the concrete provider for the database.</param>
48 ········/// <param name="providerName">The name of the concrete provider.</param>
49 ········/// <param name="mappings">The mapping information of the current application.</param>
50 ········public SchemaTransitionGenerator( INDOProviderFactory providerFactory, string providerName, NDOMapping mappings )
51 ········{
52 ············this.provider = providerFactory[providerName];
53 ············this.concreteGenerator = providerFactory.Generators[providerName];
54 ············this.mappings = mappings;
55 ········}
56
57 ········/// <summary>
58 ········/// Transforms an Xml description to SQL DDL statements for a concrete Database.
59 ········/// </summary>
60 ········/// <param name="transElement"></param>
61 ········/// <returns></returns>
62 ········public string Generate(XElement transElement)
63 ········{
64 ············StringBuilder sb = new StringBuilder();
65 ············foreach(XElement actionElement in transElement.Elements())
66 ············{
67 ················if (actionElement.Name == "DropTable")
68 ················{
69 ····················sb.Append( DropTable( actionElement ) );
70 ················}
71 ················else if (actionElement.Name == "CreateTable")
72 ················{
73 ····················sb.Append( CreateTable( actionElement ) );
74 ················}
75 ················else if (actionElement.Name == "AlterTable")
76 ················{
77 ····················sb.Append( ChangeTable( actionElement ) );
78 ················}
79 ················else if (actionElement.Name == "CreateIndex")
80 ················{
81 ····················sb.Append( CreateIndex( actionElement ) );
82 ················}
83 ············}
84 ············return sb.ToString();
85 ········}
86
87 ········string ChangeTable(XElement actionElement)
88 ········{
89 ············List<XElement> addedColumns = actionElement.Elements("AddColumn").ToList();
90 ············List<XElement> removedColumns = actionElement.Elements("DropColumn").ToList();
91 ············List<XElement> changedColumns = actionElement.Elements("AlterColumn").ToList();
92
93 ············if (addedColumns.Count == 0 && removedColumns.Count == 0 && changedColumns.Count == 0)
94 ················return String.Empty;
95
96 ············StringBuilder sb = new StringBuilder();
97
98 ············string rawTableName = actionElement.Attribute( "name" ).Value;
99 ············string tableName = this.provider.GetQualifiedTableName(rawTableName);
100 ············string alterString = "ALTER TABLE " + tableName + ' ';
101
102 ············foreach(XElement columnElement in addedColumns)
103 ············{
104 ················sb.Append(alterString);
105 ················sb.Append(concreteGenerator.AddColumn());
106 ················sb.Append(' ');
107 sb. Append( CreateColumn( columnElement, FindClass( rawTableName, this. mappings) , this. provider, false) ) ;
108 ················sb.Append(";\n");
109 ············}
110
111 ············foreach(XElement columnElement in removedColumns)
112 ············{
113 ················sb.Append(alterString);
114 ················sb.Append( concreteGenerator.RemoveColumn( provider.GetQuotedName( columnElement.Attribute( "name" ).Value ) ) );
115 ················sb.Append(";\n");
116 ············}
117
118 ············foreach(XElement columnElement in changedColumns)
119 ············{
120 ················sb.Append(alterString);
121 ················sb.Append(concreteGenerator.AlterColumnType());
122 ················sb.Append(' ');
123 sb. Append( CreateColumn( columnElement, FindClass( rawTableName, this. mappings) , this. provider, false) ) ;
124 ················sb.Append(";\n");
125 ············}
126
127 ············return sb.ToString();
128 ········}
129
130 ········string RenameColumn(string tableName, string oldColumn, string newColumn, string typeString)
131 ········{
132 ············string s = concreteGenerator.RenameColumn(tableName, oldColumn, newColumn, typeString);
133 ············
134 ············if (s != string.Empty)
135 ············return s;
136 ········
137 ············string alterString = "ALTER TABLE " + tableName + ' ';
138
139 ············StringBuilder sb = new StringBuilder(alterString);
140 ············sb.Append(concreteGenerator.AddColumn());
141 ············sb.Append(' ');
142 ············sb.Append(newColumn);
143 ············sb.Append(' ');
144 ············sb.Append(typeString);
145 ············sb.Append(";\n");
146
147 ············sb.Append("UPDATE ");
148 ············sb.Append(tableName);
149 ············sb.Append(" SET ");············
150 ············sb.Append(newColumn);
151 ············sb.Append(" = ");
152 ············sb.Append(oldColumn);
153 ············sb.Append(";\n");
154
155 ············sb.Append(alterString);
156 ············sb.Append(concreteGenerator.RemoveColumn(oldColumn));
157 ············sb.Append(';');
158 ············return sb.ToString();
159 ········}
160
161 ········/// <summary>
162 ········/// Drops a table
163 ········/// </summary>
164 ········/// <param name="actionElement"></param>
165 ········/// <returns></returns>
166 ········protected string DropTable(XElement actionElement)
167 ········{
168 ············string tableName = this.provider .GetQualifiedTableName( actionElement.Attribute( "name" ).Value );
169 ············return concreteGenerator.DropTable( tableName );
170 ········}
171
172 ········/// <summary>
173 ········/// Creates a table
174 ········/// </summary>
175 ········/// <param name="actionElement"></param>
176 ········/// <returns></returns>
177 ········/// <exception cref="Exception"></exception>
178 ········protected string CreateTable(XElement actionElement)
179 ········{
180 ············StringBuilder sb = new StringBuilder();
181 ············IProvider provider = NDOProviderFactory.Instance[concreteGenerator.ProviderName];
182 ············if (provider == null)
183 ················throw new Exception("Can't find NDO provider '" + concreteGenerator.ProviderName + "'.");
184
185 ············string dtTableName = actionElement.Attribute( "name" ).Value;
186
187 ············string tableName = this.provider.GetQualifiedTableName( dtTableName );
188
189 ············Class cl = FindClass(dtTableName, mappings);
190
191 ············if (cl != null)
192 ············{
193 ················Connection conn = mappings.FindConnection(cl);
194 ················if (conn != null)
195 ····················concreteGenerator.ConnectToDatabase(conn.Name);
196 ············}
197
198 ············sb.Append(concreteGenerator.BeginnTable(tableName));
199 ············sb.Append('\n');
200
201 ············List<XElement> columnElements = actionElement.Elements( "CreateColumn" ).ToList();
202
203 ············int vorletzterIndex = columnElements.Count - 1;
204 ············List<XElement> primaryKeyColumns = columnElements.Where( e =>
205 ············{
206 ················XAttribute attr;
207 ················return (attr = e.Attribute( "isPrimary" )) != null && String.Compare( attr.Value, "true", true ) == 0;
208 ············} ).ToList();
209
210 ············bool hasPrimaryKeyColumns = primaryKeyColumns.Count > 0;
211
212 ············for (int i = 0; i < columnElements.Count; i++)
213 ············{
214 ················XElement columnElement = columnElements[i];
215 ················string columnName = columnElement.Attribute("name").Value;
216
217 ················bool isPrimary = primaryKeyColumns.Any( e => e.Attribute( "name" ).Value == columnName );
218
219 ················sb.Append(CreateColumn(columnElement, cl, provider, isPrimary));
220 ················if (i < vorletzterIndex)
221 ················{
222 ····················sb.Append(",");
223 ····················sb.Append('\n');
224 ················}
225 ············}
226
227 ············if(concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.InTable
228 ················&& hasPrimaryKeyColumns)
229 ············{
230 ················sb.Append(",");
231 ················sb.Append('\n');
232 ············}
233
234 ············if (hasPrimaryKeyColumns && concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.InTable)
235 ············{
236 ················sb.Append(CreatePrimaryKeyConstraint(primaryKeyColumns, dtTableName, provider));
237 ············}
238 ············else
239 ············{
240 ················sb.Append('\n');
241 ············}
242 ············sb.Append(concreteGenerator.EndTable(tableName));
243 ············sb.Append('\n');
244
245 ············//············CreateIndex(primaryKeyColumns, sb, dt, provider);
246
247 ············if (hasPrimaryKeyColumns && concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.AfterTable)
248 ············{
249 ················sb.Append(CreatePrimaryKeyConstraint(primaryKeyColumns, dtTableName, provider));
250 ············}
251
252 ············sb.Append('\n');
253
254 ············return sb.ToString();
255 ········}
256
257 ········string CreateIndex(XElement actionElement)
258 ········{
259 ············//<CreateIndex name="xxx" unique="True|False" fulltext="True|False" onTable="TableName">
260 ············//··<Column name="xxx" desc="True|False">
261 ············//</CreateIndex>
262 ············StringBuilder sb = new StringBuilder();
263 ············IProvider provider = NDOProviderFactory.Instance[concreteGenerator.ProviderName];
264 ············if (provider == null)
265 ················throw new Exception( "Can't find NDO provider '" + concreteGenerator.ProviderName + "'." );
266
267 ············string tableName··= this.provider.GetQualifiedTableName( actionElement.Attribute( "onTable" ).Value );
268 ············string indexName = this.provider.GetQualifiedTableName( actionElement.Attribute( "name" ).Value );
269
270 ············sb.Append( "CREATE " );
271
272 ············// NDO doesn't check, if these keywords are supported by the database.
273 ············if (String.Compare( actionElement.Attribute( "unique" )?.Value, "true", true ) == 0)
274 ················sb.Append( "UNIQUE " );
275 ············if (String.Compare( actionElement.Attribute( "fulltext" )?.Value, "true", true ) == 0)
276 ················sb.Append( "FULLTEXT " );
277
278 ············sb.Append( "INDEX " );
279 ············sb.Append( indexName );
280 ············sb.Append( " ON " );
281 ············sb.Append( tableName );
282 ············sb.Append( " (" );
283
284 ············var columns = actionElement.Elements( "Column" ).ToList();
285
286 ············var lastIndex = columns.Count - 1;
287 ············for (int i = 0; i <= lastIndex; i++)
288 ············{
289 ················var columnElement = columns[i];
290 ················var columnName = provider.GetQuotedName( columnElement.Attribute( "name" )?.Value );
291 ················
292 ················if (columnName == null)
293 ····················throw new Exception( "Column element of CreateIndex needs a name attribute" );
294
295 ················sb.Append( columnName );
296 ················
297 ················// NDO doesn't check, if DESC is supported by the database.
298 ················// We also assume, that ASC is the standard case, so we don't emit the ASC keyword.
299 ················var desc = String.Compare( columnElement.Attribute( "desc" )?.Value, "true", true ) == 0;
300 ················if (desc)
301 ····················sb.Append( " DESC" );
302
303 ················if (i < lastIndex)
304 ····················sb.Append( ", " );
305 ············}
306
307 ············sb.Append( ")" );
308
309 ············return sb.ToString();
310 ········}
311
312 /// <summary>
313 ········/// Finds a Class mapping object, if the mapped table name matches the given table name.
314 ········/// </summary>
315 ········/// <param name="tableName"></param>
316 ········/// <param name="mappings"></param>
317 ········/// <returns></returns>
318 ········protected Class FindClass(string tableName, NDOMapping mappings)
319 ········{
320 ············Class result = null;
321 ············foreach(Class cl in mappings.Classes)
322 ············{
323 ················if (cl.TableName == tableName)
324 ················{
325 ····················result = cl;
326 ····················break;
327 ················}
328 ············}
329 ············return result;
330 ········}
331
332 ········static bool IsNumeric( Type type )
333 ········{
334 ············switch (Type.GetTypeCode( type ))
335 ············{
336 ················case TypeCode.Byte:
337 ················case TypeCode.SByte:
338 ················case TypeCode.UInt16:
339 ················case TypeCode.UInt32:
340 ················case TypeCode.UInt64:
341 ················case TypeCode.Int16:
342 ················case TypeCode.Int32:
343 ················case TypeCode.Int64:
344 ················case TypeCode.Decimal:
345 ················case TypeCode.Double:
346 ················case TypeCode.Single:
347 ····················return true;
348 ················case TypeCode.Object:
349 ····················if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof( Nullable<> ))
350 ····················{
351 ························return IsNumeric(Nullable.GetUnderlyingType( type ));
352 ····················}
353 ····················return false;
354 ················default:
355 ····················return false;
356 ············}
357 ········}
358
359 ········/// <summary>
360 ········/// Creates a column of an existing table
361 ········/// </summary>
362 ········/// <param name="columnElement"></param>
363 ········/// <param name="cl"></param>
364 ········/// <param name="provider"></param>
365 ········/// <param name="isPrimary"></param>
366 ········/// <returns></returns>
367 ········protected string CreateColumn(XElement columnElement, Class cl, IProvider provider, bool isPrimary)
368 ········{
369 ············string rawName = columnElement.Attribute( "name" ).Value;
370 ············Type dcDataType = Type.GetType( columnElement.Attribute( "type" ).Value );
371 ············string name = provider.GetQuotedName(rawName);
372 ············string columnType = columnElement.Attribute( "dbtype" )?.Value;
373 ············if (columnType == String.Empty)··// Make sure the column type will be infered, if it is an empty string
374 ················columnType = null;
375 ············string width = columnElement.Attribute("size") == null ? null : columnElement.Attribute("size").Value;
376 ············string precision = null;
377 ············bool autoIncrement = false;
378 ············StringBuilder sb = new StringBuilder();
379 ············bool allowNull = true;
380 ············string defaultValue = columnElement.Attribute( "default" )?.Value;
381
382 ············if (cl != null)
383 ············{
384 ················Field field = FindField(rawName, cl);
385
386 ················if (null != field)
387 ················{
388 ····················if ( !String.IsNullOrEmpty(columnType) && null != field.Column.DbType)
389 ························columnType = field.Column.DbType;
390 ····················if (0 != field.Column.Size && !field.Column.IgnoreColumnSizeInDDL)
391 ····················{
392 ························int dl = field.Column.Size;
393 ························if (dl == -1)
394 ····························width = "max";
395 ························else
396 ····························width = dl.ToString();
397 ····················}
398 ····················if (0 != field.Column.Precision && !field.Column.IgnoreColumnSizeInDDL)
399 ························precision = field.Column.Precision.ToString();
400 ····················allowNull = field.Column.AllowDbNull;
401 ················}
402 ················else if (cl.TimeStampColumn == rawName)
403 ················{
404 ····················if (!provider.SupportsNativeGuidType)
405 ························width = "36";
406 ················}
407 ················else if (isPrimary && columnElement.Attribute( "autoIncrement" ) != null && String.Compare(columnElement.Attribute( "autoIncrement" ).Value, "true", true) == 0)
408 ················{
409 ····················autoIncrement = true;
410 ················}
411 ············}
412 ············if (null == columnType)
413 ············{
414 ················try
415 ················{
416 ····················columnType = concreteGenerator.DbTypeFromType(dcDataType);
417 ················}
418 ················catch
419 ················{
420 ····················System.Diagnostics.Debug.Write("");
421 ················}
422 ············}
423
424 ············if (null == width)
425 ············{
426 ················int dl = provider.GetDefaultLength(dcDataType);
427 ················if (dl != 0)
428 ················{
429 ····················if (dl == -1)
430 ························width = "max";
431 ····················else
432 ························width = dl.ToString();
433 ················}
434 ············}
435 ············
436 ············// Because there is no GetDefaultPrecision in the provider...
437 ············// We assume the field to represent currency data
438 ············if (precision == null && dcDataType == typeof(decimal))
439 ············{
440 ················precision = "2";
441 ············}
442
443 ············if (columnType != null)
444 ············{
445 ················if (!concreteGenerator.LengthAllowed(columnType))
446 ····················width = null;
447 ············}
448 ············else
449 ············{
450 ················if (!concreteGenerator.LengthAllowed(dcDataType))
451 ····················width = null;
452 ············}
453
454
455 ············if (autoIncrement && concreteGenerator.HasSpecialAutoIncrementColumnFormat)
456 ················sb.Append(concreteGenerator.AutoIncrementColumn(name, dcDataType, columnType, width, isPrimary));
457 ············else if(isPrimary && concreteGenerator.PrimaryConstraintPlacement == PrimaryConstraintPlacement.InColumn)
458 ················sb.Append(concreteGenerator.PrimaryKeyColumn(name, dcDataType, columnType, width));
459 ············else if (width != null && precision != null)
460 ················sb.Append(name + " " + columnType + "(" + width + "," + precision + ")");
461 ············else if (width != null)
462 ················sb.Append(name + " " + columnType + "(" + width + ")");············
463 ············else
464 ················sb.Append(name + " " + columnType);
465
466 ············sb.Append(" ");
467
468 ············if (defaultValue != null)
469 ············{
470 ················sb.Append( "DEFAULT " );
471 ················if (IsNumeric( dcDataType ))
472 ····················sb.Append( defaultValue);
473 ················else
474 ················{
475 ····················sb.Append( '\'' );
476 ····················sb.Append( defaultValue.Replace( "'", "''" ) );
477 ····················sb.Append( '\'' );
478 ················}
479 ················sb.Append( ' ' );
480 ············}
481
482 ············sb.Append( concreteGenerator.NullExpression( allowNull && columnElement.Attribute( "allowNull" ) != null && String.Compare( columnElement.Attribute( "allowNull" ).Value, "true", true ) == 0 ) );
483 ············return sb.ToString();
484 ········}
485
486 ········/// <summary>
487 ········/// Generates SQL code for creating a PK Constraint
488 ········/// </summary>
489 ········/// <param name="primaryKeyColumns"></param>
490 ········/// <param name="tableName"></param>
491 ········/// <param name="provider"></param>
492 ········/// <returns></returns>
493 ········protected string CreatePrimaryKeyConstraint(List<XElement> primaryKeyColumns, string tableName, IProvider provider)
494 ········{
495 ············if (primaryKeyColumns.Count == 0)
496 ················return string.Empty;
497 ············string[] strArr = tableName.Split('.');
498 ············string constraintName = provider.GetQuotedName("PK_" + strArr[strArr.Length - 1]);
499 ············DataColumn[] pkColumns = (from pk in primaryKeyColumns select new DataColumn( pk.Attribute( "name" ).Value )).ToArray();
500 ············return concreteGenerator.CreatePrimaryKeyConstraint(pkColumns, constraintName, provider.GetQualifiedTableName(tableName)) + '\n';
501 ········}
502
503 ········/// <summary>
504 ········/// Finds a field mapping for the given column name
505 ········/// </summary>
506 ········/// <param name="columnName"></param>
507 ········/// <param name="cl"></param>
508 ········/// <returns></returns>
509 ········protected Field FindField (string columnName, Class cl)
510 ········{
511 ············Field result = null;
512 ············foreach (Field field in cl.Fields)
513 ············{
514 ················if (field.Column.Name == columnName)
515 ················{
516 ····················result = field;
517 ····················break;
518 ················}
519 ············}
520 ············return result;
 
 
 
 
 
 
 
 
521 ········}
522 ····}
523 }
524