DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
AlterTableStatement.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 
21 using Deveel.Data.Security;
24 using Deveel.Data.Sql.Tables;
25 
26 namespace Deveel.Data.Sql.Statements {
27  [Serializable]
29  public AlterTableStatement(ObjectName tableName, IAlterTableAction action) {
30  if (tableName == null)
31  throw new ArgumentNullException("tableName");
32  if (action == null)
33  throw new ArgumentNullException("action");
34 
35  TableName = tableName;
36  Action = action;
37  }
38 
40  TableName = data.GetValue<ObjectName>("TableName");
41  Action = data.GetValue<IAlterTableAction>("Action");
42  }
43 
44  public ObjectName TableName { get; private set; }
45 
46  public IAlterTableAction Action { get; private set; }
47 
49  var tableName = context.Query.ResolveTableName(TableName);
50  return new AlterTableStatement(tableName, Action);
51  }
52 
54  var action = Action;
55  if (action is IPreparable)
56  action = (IAlterTableAction)(action as IPreparable).Prepare(preparer);
57 
58  return new AlterTableStatement(TableName, action);
59  }
60 
61  private static void CheckColumnConstraint(string columnName, string[] columns, ObjectName table, string constraintName) {
62  foreach (string column in columns) {
63  if (columnName.Equals(column)) {
65  "Constraint violation (" + constraintName +
66  ") dropping column " + columnName + " because of " +
67  "referential constraint in " + table);
68  }
69  }
70 
71  }
72 
73  private bool CheckColumnNamesMatch(IRequest context, String col1, String col2) {
74  var comparison = context.Query.IgnoreIdentifiersCase() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
75  return col1.Equals(col2, comparison);
76  }
77 
78  protected override void GetData(SerializeData data) {
79  data.SetValue("TableName", TableName);
80  data.SetValue("Action", Action);
81  }
82 
83  protected override void ExecuteStatement(ExecutionContext context) {
84  if (!context.Request.Query.UserCanAlterTable(TableName))
85  throw new InvalidAccessException(context.Request.Query.UserName(), TableName);
86 
87  var table = context.Request.Query.GetTable(TableName);
88  if (table == null)
89  throw new ObjectNotFoundException(TableName);
90 
91  var tableInfo = table.TableInfo;
92  var newTableInfo = new TableInfo(tableInfo.TableName);
93 
94  var checker = ColumnChecker.Default(context.Request, TableName);
95 
96  bool tableAltered = false;
97  bool markDropped = false;
98 
99  for (int n = 0; n < tableInfo.ColumnCount; ++n) {
100  var column = tableInfo[n];
101 
102  string columnName = column.ColumnName;
103  var columnType = column.ColumnType;
104  var defaultExpression = column.DefaultExpression;
105 
106  if (Action.ActionType == AlterTableActionType.SetDefault &&
107  CheckColumnNamesMatch(context.Request, ((SetDefaultAction)Action).ColumnName, columnName)) {
108  var exp = ((SetDefaultAction)Action).DefaultExpression;
109  exp = checker.CheckExpression(exp);
110  defaultExpression = exp;
111  tableAltered = true;
112  } else if (Action.ActionType == AlterTableActionType.DropDefault &&
113  CheckColumnNamesMatch(context.Request, ((DropDefaultAction)Action).ColumnName, columnName)) {
114  defaultExpression = null;
115  tableAltered = true;
116  } else if (Action.ActionType == AlterTableActionType.DropColumn &&
117  CheckColumnNamesMatch(context.Request, ((DropColumnAction)Action).ColumnName, columnName)) {
118  // Check there are no referential links to this column
119  var refs = context.Request.Query.GetTableImportedForeignKeys(TableName);
120  foreach (var reference in refs) {
121  CheckColumnConstraint(columnName, reference.ForeignColumnNames, reference.ForeignTable, reference.ConstraintName);
122  }
123 
124  // Or from it
125  refs = context.Request.Query.GetTableForeignKeys(TableName);
126  foreach (var reference in refs) {
127  CheckColumnConstraint(columnName, reference.ColumnNames, reference.TableName, reference.ConstraintName);
128  }
129 
130  // Or that it's part of a primary key
131  var primaryKey = context.Request.Query.GetTablePrimaryKey(TableName);
132  if (primaryKey != null)
133  CheckColumnConstraint(columnName, primaryKey.ColumnNames, TableName, primaryKey.ConstraintName);
134 
135  // Or that it's part of a unique set
136  var uniques = context.Request.Query.GetTableUniqueKeys(TableName);
137  foreach (var unique in uniques) {
138  CheckColumnConstraint(columnName, unique.ColumnNames, TableName, unique.ConstraintName);
139  }
140 
141  markDropped = true;
142  tableAltered = true;
143  }
144 
145  var newColumn = new ColumnInfo(columnName, columnType);
146  if (defaultExpression != null)
147  newColumn.DefaultExpression = defaultExpression;
148 
149  newColumn.IndexType = column.IndexType;
150  newColumn.IsNotNull = column.IsNotNull;
151 
152  // If not dropped then add to the new table definition.
153  if (!markDropped) {
154  newTableInfo.AddColumn(newColumn);
155  }
156  }
157 
158  if (Action.ActionType == AlterTableActionType.AddColumn) {
159  var col = ((AddColumnAction)Action).Column;
160 
161  checker.CheckExpression(col.DefaultExpression);
162  var columnName = col.ColumnName;
163  var columnType = col.ColumnType;
164 
165  // If column name starts with [table_name]. then strip it off
166  columnName = checker.StripTableName(TableName.Name, columnName);
167  if (tableInfo.IndexOfColumn(col.ColumnName) != -1)
168  throw new InvalidOperationException("The column '" + col.ColumnName + "' is already in the table '" + tableInfo.TableName + "'.");
169 
170  var newColumn = new ColumnInfo(columnName, columnType) {
171  IsNotNull = col.IsNotNull,
172  DefaultExpression = col.DefaultExpression
173  };
174 
175  newTableInfo.AddColumn(newColumn);
176  tableAltered = true;
177  }
178 
179  if (Action.ActionType == AlterTableActionType.DropConstraint) {
180  string constraintName = ((DropConstraintAction)Action).ConstraintName;
181  int dropCount = context.Request.Query.DropConstraint(TableName, constraintName);
182  if (dropCount == 0)
183  throw new InvalidOperationException("Named constraint to drop on table " + TableName + " was not found: " + constraintName);
184  } else if (Action.ActionType == AlterTableActionType.DropPrimaryKey) {
185  string constraintName = ((DropConstraintAction)Action).ConstraintName;
186  if (!context.Request.Query.DropPrimaryKey(TableName, constraintName))
187  throw new InvalidOperationException("No primary key to delete on table " + TableName);
188  }
189 
190  if (Action.ActionType == AlterTableActionType.AddConstraint) {
191  var constraint = ((AddConstraintAction)Action).Constraint;
192  bool foreignConstraint = (constraint.ConstraintType == ConstraintType.ForeignKey);
193 
194  ObjectName refTname = null;
195  if (foreignConstraint) {
196  refTname = context.Request.Query.ResolveTableName(constraint.ReferenceTable);
197  }
198 
199  var columnNames = checker.StripColumnList(TableName.FullName, constraint.Columns);
200  columnNames = checker.StripColumnList(constraint.ReferenceTable, columnNames);
201  var expression = checker.CheckExpression(constraint.CheckExpression);
202  columnNames = checker.CheckColumns(columnNames);
203 
204  IEnumerable<string> refCols = null;
205  if (foreignConstraint && constraint.ReferenceColumns != null) {
206  var referencedChecker = ColumnChecker.Default(context.Request, refTname);
207  refCols = referencedChecker.CheckColumns(constraint.ReferenceColumns);
208  }
209 
210  var newConstraint = new ConstraintInfo(constraint.ConstraintType, TableName, columnNames.ToArray());
211  if (foreignConstraint) {
212  newConstraint.ForeignTable = refTname;
213  newConstraint.ForeignColumnNames = refCols.ToArray();
214  }
215 
216  if (constraint.ConstraintType == ConstraintType.Check)
217  newConstraint.CheckExpression = expression;
218 
219  context.Request.Query.AddConstraint(TableName, newConstraint);
220  }
221 
222  // Alter the existing table to the new format...
223  if (tableAltered) {
224  if (newTableInfo.ColumnCount == 0)
225  throw new InvalidOperationException("Can not ALTER table to have 0 columns.");
226 
227  context.Request.Query.AlterTable(newTableInfo);
228  } else {
229  // If the table wasn't physically altered, check the constraints.
230  // Calling this method will also make the transaction check all
231  // deferred constraints during the next commit.
232  context.Request.Query.CheckConstraints(TableName);
233  }
234  }
235 
236  #region PreparedSerializer
237 
238  //internal class PreparedSerializer : ObjectBinarySerializer<AlterTableStatement> {
239  // public override void Serialize(AlterTableStatement obj, BinaryWriter writer) {
240  // ObjectName.Serialize(obj.TableName, writer);
241  // SerializeAction(obj.Action, writer);
242  // }
243 
244  // private static void SerializeAction(IAlterTableAction action, BinaryWriter writer) {
245  // writer.Write((byte) action.ActionType);
246 
247  // if (action is AddColumnAction) {
248  // var addColumn = (AddColumnAction) action;
249  // SqlTableColumn.Serialize(addColumn.Column, writer);
250  // } else if (action is AddConstraintAction) {
251  // var addConstraint = (AddConstraintAction) action;
252  // SqlTableConstraint.Serialize(addConstraint.Constraint, writer);
253  // } else if (action is DropColumnAction) {
254  // var dropColumn = (DropColumnAction) action;
255  // writer.Write(dropColumn.ColumnName);
256  // } else if (action is DropConstraintAction) {
257  // var dropConstraint = (DropConstraintAction) action;
258  // writer.Write(dropConstraint.ConstraintName);
259  // } else if (action is DropDefaultAction) {
260  // var dropDefault = (DropDefaultAction) action;
261  // writer.Write(dropDefault.ColumnName);
262  // } else if (action is DropPrimaryKeyAction) {
263  // // Nothing to write here
264  // } else if (action is SetDefaultAction) {
265  // var setDefault = (SetDefaultAction) action;
266  // writer.Write(setDefault.ColumnName);
267  // SqlExpression.Serialize(setDefault.DefaultExpression, writer);
268  // } else {
269  // throw new NotSupportedException();
270  // }
271  // }
272 
273  // public override AlterTableStatement Deserialize(BinaryReader reader) {
274  // var tableName = ObjectName.Deserialize(reader);
275  // var action = DeserializeAction(reader);
276  // return new AlterTableStatement(tableName, action);
277  // }
278 
279  // private IAlterTableAction DeserializeAction(BinaryReader reader) {
280  // var actionType = (AlterTableActionType) reader.ReadByte();
281  // if (actionType == AlterTableActionType.AddColumn) {
282  // var sqlColumn = SqlTableColumn.Deserialize(reader);
283  // return new AddColumnAction(sqlColumn);
284  // }
285 
286  // if (actionType == AlterTableActionType.AddConstraint) {
287  // var sqlConstraint = SqlTableConstraint.Deserialize(reader);
288  // return new AddConstraintAction(sqlConstraint);
289  // }
290 
291  // if (actionType == AlterTableActionType.DropColumn) {
292  // var dropColumn = reader.ReadString();
293  // return new DropColumnAction(dropColumn);
294  // }
295 
296  // if (actionType == AlterTableActionType.DropConstraint) {
297  // var dropConstraint = reader.ReadString();
298  // return new DropConstraintAction(dropConstraint);
299  // }
300 
301  // if (actionType == AlterTableActionType.DropDefault) {
302  // var columnName = reader.ReadString();
303  // return new DropDefaultAction(columnName);
304  // }
305 
306  // if (actionType == AlterTableActionType.DropPrimaryKey) {
307  // return new DropPrimaryKeyAction();
308  // }
309 
310  // if (actionType == AlterTableActionType.SetDefault) {
311  // var columnName = reader.ReadString();
312  // var expression = SqlExpression.Deserialize(reader);
313  // return new SetDefaultAction(columnName, expression);
314  // }
315 
316  // throw new NotSupportedException();
317  // }
318  //}
319 
320  #endregion
321  }
322 }
Defines the metadata properties of a column within a table of a database.
Definition: ColumnInfo.cs:36
Enumerates a known set of codes in a SQL Model
A database exception that represents a constraint violation.
static void CheckColumnConstraint(string columnName, string[] columns, ObjectName table, string constraintName)
IEnumerable< string > CheckColumns(IEnumerable< string > columnNames)
bool IsNotNull
Gets or sets a boolean value indicating if the column values are constrained to be ony NOT NULL...
Definition: ColumnInfo.cs:144
void SetValue(string key, Type type, object value)
Describes the name of an object within a database.
Definition: ObjectName.cs:44
override void ExecuteStatement(ExecutionContext context)
Represents the foundation class of SQL statements to be executed.
Definition: SqlStatement.cs:32
ConstraintType
An enumeration of all the supported kinds of constraints within a table or a schema.
AlterTableStatement(ObjectName tableName, IAlterTableAction action)
An interface used to prepare a SqlExpression object.
override void GetData(SerializeData data)
const int DropColumnViolation
Column can't be dropped before of an reference to it.
AlterTableActionType IAlterTableAction. ActionType
static ColumnChecker Default(IRequest context, ObjectName tableName)
AlterTableActionType
The possible types of actions in a AlterTableAction expression.
object Prepare(IExpressionPreparer preparer)
Converts the underlying value of this instance into an object that can be evaluated by an expression...
SqlExpression CheckExpression(SqlExpression expression)
Defines the metadata properties of a table existing within a database.
Definition: TableInfo.cs:41
AlterTableActionType IAlterTableAction. ActionType
bool CheckColumnNamesMatch(IRequest context, String col1, String col2)
A contract for objects that participate to a SqlExpression.Prepare phase of an expression evaluation...
Definition: IPreparable.cs:30