18 using System.Collections.Generic;
30 namespace Deveel.Data.Transactions {
33 string constraintName) {
46 constraintName = MakeUniqueConstraintName(constraintName, uniqueId);
47 row.SetValue(0, uniqueId);
48 row.SetValue(1, constraintName);
50 row.SetValue(3, tableName.
Name);
51 row.SetValue(4, (
short) deferred);
55 for (
int i = 0; i < columns.Length; ++i) {
57 row.SetValue(0, uniqueId);
58 row.SetValue(1, columns[i]);
69 throw new Exception(
String.Format(
"Primary key constraint name '{0}' is already being used.", constraintName));
79 AddForeignKey(transaction, table, columns, refTable, refColumns, deleteRule, updateRule,
84 ObjectName refTable,
string[] refColumns, String constraintName) {
92 deferred, constraintName);
104 if (refColumns.Length == 0) {
105 var set = transaction.QueryTablePrimaryKey(refTable);
107 throw new Exception(
String.Format(
"No primary key defined for referenced table '{0}'", refTable));
109 refColumns = set.ColumnNames;
112 if (columns.Length != refColumns.Length) {
113 throw new Exception(
String.Format(
"Foreign key reference '{0}' -> '{1}' does not have an equal number of " +
114 "column terms.", table, refTable));
121 var tableInfo = transaction.GetTableInfo(table);
122 for (
int i = 0; i < columns.Length; ++i) {
123 var columnInfo = tableInfo[tableInfo.IndexOfColumn(columns[i])];
124 if (columnInfo.IsNotNull) {
125 throw new Exception(
String.Format(
"Foreign key reference '{0}' -> '{1}' update or delete triggered " +
126 "action is SET NULL for columns that are constrained as " +
127 "NOT NULL.", table, refTable));
133 var row = t.NewRow();
135 constraintName = MakeUniqueConstraintName(constraintName, uniqueId);
136 row.SetValue(0, uniqueId);
137 row.SetValue(1, constraintName);
139 row.SetValue(3, table.
Name);
141 row.SetValue(5, refTable.
Name);
142 row.SetValue(6, ((
int) updateRule));
143 row.SetValue(7, ((
int) deleteRule));
144 row.SetValue(8, ((
short) deferred));
148 for (
int i = 0; i < columns.Length; ++i) {
149 row = tcols.NewRow();
150 row.SetValue(0, uniqueId);
151 row.SetValue(1, columns[i]);
152 row.SetValue(2, refColumns[i]);
164 throw new Exception(
String.Format(
"Foreign key constraint name '{0}' is already being used.", constraintName));
171 string constraintName) {
182 var row = t.NewRow();
184 constraintName = MakeUniqueConstraintName(constraintName, uniqueId);
185 row.SetValue(0, uniqueId);
186 row.SetValue(1, constraintName);
188 row.SetValue(3, tableName.
Name);
189 row.SetValue(4, (
short) deferred);
193 for (
int i = 0; i < columns.Length; ++i) {
194 row = tcols.NewRow();
195 row.SetValue(0, uniqueId);
196 row.SetValue(1, columns[i]);
208 throw new Exception(
String.Format(
"Unique constraint name '{0}' is already being used.", constraintName));
217 var t = transaction.GetMutableTable(tn);
218 int colCount = t.TableInfo.ColumnCount;
222 using (var stream =
new MemoryStream()) {
223 using (var writer =
new BinaryWriter(stream, Encoding.Unicode)) {
227 binExp = stream.ToArray();
232 var uniqueId = transaction.NextTableId(tn);
233 constraintName = MakeUniqueConstraintName(constraintName, uniqueId);
235 rd.SetValue(0, uniqueId);
236 rd.SetValue(1, constraintName);
238 rd.SetValue(3, tableName.
Name);
239 rd.SetValue(4, expression.
ToString());
240 rd.SetValue(5, (
short) deferrability);
252 throw new InvalidOperationException(
"Check constraint name '" + constraintName +
"' is already being used.");
259 return String.IsNullOrEmpty(constraintName) ? (
"_ANONYMOUS_CONSTRAINT_" + uniqueId) : constraintName;
263 var colList = cols.ToList();
264 int size = colList.Count;
265 var list =
new String[size];
268 for (
int n = 0; n < size; ++n) {
270 for (
int i = 0; i < size; ++i) {
271 int rowIndex = colList[i];
287 IList<int> identicalRows = null;
294 if (colIndexes.Select(x => table.
GetValue(rindex, x)).Any(x => x.IsNull))
297 foreach (var colIndex
in colIndexes) {
298 var value = table.GetValue(rindex, colIndex);
305 if (identicalRows == null || identicalRows.Count > 0) {
309 var index = table.GetIndex(colIndex);
310 var list = index.SelectEqual(value).ToList();
320 if (identicalRows == null) {
321 identicalRows = list;
324 int rowIndex = identicalRows.Count - 1;
325 while (rowIndex >= 0) {
326 int val = identicalRows[rowIndex];
327 int foundIndex = list.BinarySearch(val);
330 if (foundIndex < 0 ||
331 list[foundIndex] != val) {
332 identicalRows.RemoveAt(rowIndex);
342 if (identicalRows != null) {
343 int sz = identicalRows.Count;
349 throw new InvalidOperationException(
"Assertion failed: We must be able to find the " +
350 "row we are testing uniqueness against!");
357 string[] cols1,
ObjectName table2, String[] cols2,
358 bool checkSourceTableKey) {
361 var t1 = transaction.GetTable(table1);
362 var t2 = transaction.GetTable(table2);
365 var dti1 = t1.TableInfo;
366 var dti2 = t2.TableInfo;
369 var col1Indexes = dti1.IndexOfColumns(cols1).ToArray();
370 var col2Indexes = dti2.IndexOfColumns(cols2).ToArray();
372 int keySize = col1Indexes.Length;
377 for (
int n = 0; n < keySize; ++n) {
378 keyValue[n] = t1.GetValue(rowIndex, col1Indexes[n]);
379 if (keyValue[n].IsNull) {
392 if (checkSourceTableKey) {
393 var keys = t1.FindKeys(col1Indexes, keyValue);
398 return t2.FindKeys(col2Indexes, keyValue).Count();
402 if (rowIndices == null || rowIndices.Length == 0)
417 for (
int i = 0; i < len; ++i) {
419 var columnInfo = tableInfo[i];
422 for (
int rn = 0; rn < rowIndices.Length; ++rn) {
423 var value = table.
GetValue(rowIndices[rn], i);
427 if (columnInfo.IsNotNull && value.IsNull) {
430 "Attempt to set NULL value to column '" +
431 tableInfo[i].ColumnName +
432 "' which is declared as NOT NULL");
438 columnInfo.ColumnType.TypeCode ==
SqlTypeCode.Object) {
439 throw new NotImplementedException();
448 var rows = table.Select(x => x.RowId.RowNumber).ToArray();
451 CheckAddConstraintViolations(transaction, table, rows, deferred);
457 using (var session =
new SystemSession(transaction, curSchema)) {
458 using (var queryContext = session.CreateQuery()) {
461 if (rowIndices == null || rowIndices.Length == 0)
470 var primaryKey = transaction.QueryTablePrimaryKey(tableName);
471 if (primaryKey != null &&
476 foreach (
int rowIndex
in rowIndices) {
477 if (!IsUniqueColumns(table, rowIndex, primaryKey.ColumnNames,
false)) {
480 deferred.AsDebugString() +
" primary Key constraint violation (" +
481 primaryKey.ConstraintName +
") Columns = ( " +
482 String.Join(
", ", primaryKey.ColumnNames) +
483 " ) Table = ( " + tableName +
" )");
489 var uniqueConstraints = transaction.QueryTableUniqueKeys(tableName);
490 foreach (var unique
in uniqueConstraints) {
495 foreach (
int rowIndex
in rowIndices) {
496 if (!IsUniqueColumns(table, rowIndex, unique.ColumnNames,
true)) {
499 deferred.AsDebugString() +
" unique constraint violation (" +
500 unique.ConstraintName +
") Columns = ( " +
501 String.Join(
", ", unique.ColumnNames) +
" ) Table = ( " +
511 var foreignConstraints = transaction.QueryTableForeignKeys(tableName);
513 foreach (var reference
in foreignConstraints) {
517 foreach (
int rowIndex
in rowIndices) {
523 int rowCount = RowCountOfReferenceTable(transaction,
525 reference.TableName, reference.ColumnNames,
526 reference.ForeignTable, reference.ForeignColumnNames,
528 if (rowCount == -1) {
535 deferred.AsDebugString() +
" foreign key constraint violation (" +
536 reference.ConstraintName +
") Columns = " +
537 reference.TableName +
"( " +
538 String.Join(
", ", reference.ColumnNames) +
" ) -> " +
539 reference.ForeignTable +
"( " +
540 String.Join(
", ", reference.ForeignColumnNames) +
" )");
547 var checkConstraints = transaction.QueryTableCheckExpressions(tableName);
550 for (
int i = 0; i < checkConstraints.Length; ++i) {
551 var check = checkConstraints[i];
556 var exp = tableInfo.ResolveColumns(
true, check.CheckExpression);
559 for (
int rn = 0; rn < rowIndices.Length; ++rn) {
561 var evalExp = exp.Evaluate(queryContext, resolver, null);
564 var b = ob.AsBoolean();
571 deferred.AsDebugString() +
" check constraint violation (" +
572 check.ConstraintName +
") - '" + exp +
573 "' evaluated to false for inserted/updated row.");
590 if (rowIndices == null || rowIndices.Length == 0)
599 var foreignConstraints = transaction.QueryTableImportedForeignKeys(tableName);
600 foreach (var reference
in foreignConstraints) {
604 foreach (
int rowIndex
in rowIndices) {
610 int rowCount = RowCountOfReferenceTable(transaction,
612 reference.ForeignTable, reference.ForeignColumnNames,
613 reference.TableName, reference.ColumnNames,
620 deferred.AsDebugString() +
" foreign key constraint violation " +
622 reference.ConstraintName +
") Columns = " +
623 reference.TableName +
"( " +
624 String.Join(
", ", reference.ColumnNames) +
" ) -> " +
625 reference.ForeignTable +
"( " +
626 String.Join(
", ", reference.ForeignColumnNames) +
" )");
641 var data = t.SelectRowsEqual(3, objTableName, 2, objSchema).ToList();
645 for (
int i = 0; i < data.Count; ++i) {
646 int rowIndex = data[i];
649 var
id = t.GetValue(rowIndex, 0);
653 new ObjectName(t.GetValue(rowIndex, 4).Value.ToString()),
654 t.GetValue(rowIndex, 5).Value.
ToString());
657 var cols = t2.SelectRowsEqual(0,
id).ToList();
659 var name = t.GetValue(rowIndex, 1).Value.
ToString();
665 int colsSize = cols.Count;
666 string[] keyCols =
new string[colsSize];
667 string[] refCols =
new string[colsSize];
668 for (
int n = 0; n < colsSize; ++n) {
669 for (
int p = 0; p < colsSize; ++p) {
670 int colsIndex = cols[p];
671 if (t2.GetValue(colsIndex, 3) == n) {
672 keyCols[n] = t2.GetValue(colsIndex, 1).Value.ToString();
673 refCols[n] = t2.GetValue(colsIndex, 2).Value.ToString();
681 constraint.OnUpdate = updateRule;
682 constraint.Deferred = deferred;
684 groups[i] = constraint;
698 var data = t.SelectRowsEqual(5, objRefTableName, 4, objRefSchema).ToArray();
702 for (
int i = 0; i < data.Length; ++i) {
703 int rowIndex = data[i];
706 var
id = t.GetValue(rowIndex, 0);
709 var schemaNamePart = t.GetValue(rowIndex, 2).AsVarChar().Value.ToString();
710 var tableNamePart = t.GetValue(rowIndex, 3).AsVarChar().Value.ToString();
714 var cols = t2.SelectRowsEqual(0,
id).ToArray();
716 var name = t.GetValue(rowIndex, 1).AsVarChar().Value.ToString();
722 int colsSize = cols.Length;
723 string[] keyCols =
new string[colsSize];
724 string[] refCols =
new string[colsSize];
725 for (
int n = 0; n < colsSize; ++n) {
726 for (
int p = 0; p < colsSize; ++p) {
727 int colsIndex = cols[p];
728 if (t2.GetValue(colsIndex, 3) == n) {
729 keyCols[n] = t2.GetValue(colsIndex, 1);
730 refCols[n] = t2.GetValue(colsIndex, 2);
738 constraint.OnUpdate = updateRule;
739 constraint.Deferred = deferred;
741 groups[i] = constraint;
755 var data = t.SelectRowsEqual(3, objTableName, 2, objSchemaName).ToList();
759 for (
int i = 0; i < data.Count; ++i) {
760 var
id = t.GetValue(data[i], 0);
763 var cols = t2.SelectRowsEqual(0,
id);
765 var name = t.GetValue(data[i], 1).Value.ToString();
766 var columns = ToColumns(t2, cols);
771 constraints[i] = constraint;
785 var data = t.SelectRowsEqual(3, objTableName, 2, objSchemaName).ToList();
788 throw new InvalidOperationException(
"Assertion failed: multiple primary key for: " + tableName);
793 int rowIndex = data[0];
795 var
id = t.GetValue(rowIndex, 0);
798 var list = t2.SelectRowsEqual(0,
id);
801 var name = t.GetValue(rowIndex, 1).AsVarChar().Value.ToString();
802 string[] columns = ToColumns(t2, list);
811 var list =
new List<ObjectName>();
812 var refs = QueryTableForeignKeys(transaction, tableName);
813 foreach (var fkeyRef
in refs) {
814 var tname = fkeyRef.ForeignTable;
815 if (!list.Contains(tname))
819 refs = QueryTableImportedForeignKeys(transaction, tableName);
820 foreach (var fkeyRef
in refs) {
821 var tname = fkeyRef.TableName;
822 if (!list.Contains(tname))
826 return list.ToArray();
836 var data = t.SelectRowsEqual(3, objTableName, 2, objSchemaName).ToList();
839 for (
int i = 0; i < checks.Length; ++i) {
840 int rowIndex = data[i];
842 string name = t.GetValue(rowIndex, 1).Value.ToString();
847 if (t.TableInfo.ColumnCount > 6) {
848 var sexp = (
SqlBinary) t.GetValue(rowIndex, 6).Value;
853 throw new NotImplementedException();
854 }
catch (Exception) {
863 if (expression == null) {
876 var primary = transaction.QueryTablePrimaryKey(tableName);
877 var uniques = transaction.QueryTableUniqueKeys(tableName);
878 var expressions = transaction.QueryTableCheckExpressions(tableName);
879 var refs = transaction.QueryTableForeignKeys(tableName);
882 transaction.DropTablePrimaryKey(tableName, primary.ConstraintName);
883 foreach (var unique
in uniques) {
884 transaction.DropTableUniqueKey(tableName, unique.ConstraintName);
886 foreach (var expression
in expressions) {
887 transaction.DropTableCheck(tableName, expression.ConstraintName);
889 foreach (var reference
in refs) {
890 transaction.DropTableForeignKey(tableName, reference.ConstraintName);
896 if (transaction.DropTablePrimaryKey(tableName, constraintName)) {
899 if (transaction.DropTableUniqueKey(tableName, constraintName)) {
902 if (transaction.DropTableCheck(tableName, constraintName)) {
905 if (transaction.DropTableForeignKey(tableName, constraintName)) {
914 IEnumerable<int> data;
915 if (constraintName != null) {
925 var resultList = data.ToList();
927 if (resultList.Count > 1)
928 throw new InvalidOperationException(
"Assertion failed: multiple primary key for: " + tableName);
930 if (resultList.Count == 1) {
931 int rowIndex = resultList[0];
934 var
id = t.GetValue(rowIndex, 0);
937 var columns = t2.SelectRowsEqual(0,
id);
940 t2.DeleteRows(columns);
941 t.DeleteRows(resultList);
956 var resultList = data.ToList();
957 if (resultList.Count > 1)
958 throw new InvalidOperationException(
"Assertion failed: multiple unique constraint name: " + constraintName);
960 if (resultList.Count == 1) {
961 var rowIndex = resultList[0];
964 var
id = t.GetValue(rowIndex, 0);
967 var columns = t2.SelectRowsEqual(0,
id);
970 t2.DeleteRows(columns);
971 t.DeleteRows(resultList);
984 var resultList = data.ToList();
985 if (resultList.Count > 1)
986 throw new InvalidOperationException(
"Assertion failed: multiple check constraint name: " + constraintName);
988 if (resultList.Count == 1) {
990 t.DeleteRows(resultList);
1005 var resultList = data.ToList();
1007 if (resultList.Count > 1)
1008 throw new InvalidOperationException(
"Assertion failed: multiple foreign key constraint " +
"name: " + constraintName);
1010 if (resultList.Count == 1) {
1011 int rowIndex = resultList[0];
1014 var
id = t.GetValue(rowIndex, 0);
1017 var columns = t2.SelectRowsEqual(0,
id);
1020 t2.DeleteRows(columns);
1021 t.DeleteRows(resultList);
1036 this.rowIndex = rowIndex;
1040 int colIndex = table.TableInfo.IndexOfColumn(variable.
Name);
1042 throw new InvalidOperationException(
"Can't find column: " + variable);
1050 get {
return rowIndex; }
1054 int colIndex = FindColumnName(variable);
1055 return table.GetValue(rowIndex, colIndex);
1059 int colIndex = FindColumnName(variable);
1060 return table.TableInfo[colIndex].ColumnType;
static ConstraintInfo PrimaryKey(ObjectName tableName, params string[] columnNames)
static bool DropTablePrimaryKey(this ITransaction transaction, ObjectName tableName, string constraintName)
Defines the contract to access the data contained into a table of a database.
static DataObject Binary(SqlBinary binary)
Enumerates a known set of codes in a SQL Model
static ConstraintInfo[] QueryTableForeignKeys(this ITransaction transaction, ObjectName tableName)
A database exception that represents a constraint violation.
static ConstraintInfo[] QueryTableCheckExpressions(this ITransaction transaction, ObjectName tableName)
override string ToString()
static SqlExpression Parse(string s)
Parses the given SQL string to an expression that can be evaluated.
A long string in the system.
int ErrorCode
Gets a numeric value representing the code of the error catched by this exception.
const int UniqueViolation
A Unique constraint violation error code.
static void CheckFieldConstraintViolations(this ITransaction transaction, ITable table, int[] rowIndices)
SqlType ReturnType(ObjectName variable)
Returns the SqlType of object the given variable is.
static void CheckAddConstraintViolations(this ITransaction transaction, ITable table, ConstraintDeferrability deferred)
Implements a BINARY object that handles a limited number of bytes, not exceding MaxLength.
static void CheckRemoveConstraintViolations(this ITransaction transaction, ITable table, int[] rowIndices, ConstraintDeferrability deferred)
static String[] ToColumns(ITable table, IEnumerable< int > cols)
static ConstraintInfo Check(ObjectName tableName, SqlExpression expression, params string[] columnNames)
ConstraintDeferrability Deferred
static readonly ObjectName CheckInfoTableName
Describes the name of an object within a database.
static readonly ObjectName PrimaryKeyInfoTableName
static void AddForeignKey(this ITransaction transaction, ObjectName table, string[] columns, ObjectName refTable, string[] refColumns, ForeignKeyAction deleteRule, ForeignKeyAction updateRule, String constraintName)
static readonly ObjectName UniqueKeyInfoTableName
static int DropTableConstraint(this ITransaction transaction, ObjectName tableName, string constraintName)
ISqlObject Value
Gets the underlined value that is handled.
ConstraintDeferrability
The type of deferrance of a constraint.
static DataObject String(string s)
static readonly ObjectName UniqueKeyColumnsTableName
DataObject GetValue(long rowNumber, int columnOffset)
Gets a single cell within the table that is located at the given column offset and row...
static void Serialize(SqlExpression expression, BinaryWriter writer)
DataObject Resolve(ObjectName variable)
Returns the value of a given variable.
static bool DropTableUniqueKey(this ITransaction transaction, ObjectName table, string constraintName)
static bool DropTableCheck(this ITransaction transaction, ObjectName table, string constraintName)
ObjectName TableName
Gets the fully qualified name of the table that is ensured to be unique within the system...
static ConstraintInfo[] QueryTableUniqueKeys(this ITransaction transaction, ObjectName tableName)
static readonly ObjectName ForeignKeyInfoTableName
ForeignKeyAction
Enumerates the foreign key referential trigger actions.
static bool DropTableForeignKey(this ITransaction transaction, ObjectName table, string constraintName)
static readonly ObjectName ForeignKeyColumnsTableName
static string MakeUniqueConstraintName(string constraintName, SqlNumber uniqueId)
const int ForeignKeyViolation
A Foreign Key constraint violation error code.
static ConstraintInfo QueryTablePrimaryKey(this ITransaction transaction, ObjectName tableName)
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
ForeignKeyAction OnDelete
static void AddForeignKey(this ITransaction transaction, ObjectName table, string[] columns, ObjectName refTable, string[] refColumns, ConstraintDeferrability deferred, String constraintName)
static void DropAllTableConstraints(this ITransaction transaction, ObjectName tableName)
static void AddCheck(this ITransaction transaction, ObjectName tableName, SqlExpression expression, ConstraintDeferrability deferrability, string constraintName)
Provides utilities and properties for handling the SYSTEN schema of a database.
Defines the properties of a specific SQL Type and handles the values compatible.
static void AddPrimaryKey(this ITransaction transaction, ObjectName tableName, string[] columns, string constraintName)
TableInfo TableInfo
Gets the metadata information of the table, used to resolve the column sources.
static void AddUniqueKey(this ITransaction transaction, ObjectName tableName, string[] columns, string constraintName)
static ConstraintInfo[] QueryTableImportedForeignKeys(this ITransaction transaction, ObjectName refTableName)
const int PrimaryKeyViolation
A Primary Key constraint violation error code.
const int NullableViolation
A Nullable constraint violation error code (data added to not null columns that was null)...
SqlTypeCode
Enumerates the codes of all SQL types handled by the system.
An interface to resolve a variable name to a constant object.
static void AddForeignKey(this ITransaction transaction, ObjectName table, string[] columns, ObjectName refTable, string[] refColumns, ForeignKeyAction deleteRule, ForeignKeyAction updateRule, ConstraintDeferrability deferred, String constraintName)
ObjectName Parent
Gets the parent reference of the current one, if any or null if none.
An expression that holds a constant value.
static int RowCountOfReferenceTable(this ITransaction transaction, int rowIndex, ObjectName table1, string[] cols1, ObjectName table2, String[] cols2, bool checkSourceTableKey)
static void AddUniqueKey(this ITransaction transaction, ObjectName tableName, string[] columns, ConstraintDeferrability deferred, string constraintName)
string Name
Gets the name of the object being referenced.
static readonly ObjectName PrimaryKeyColumnsTableName
int ColumnCount
Gets a count of the columns defined by this object.
int FindColumnName(ObjectName variable)
static bool IsUniqueColumns(ITable table, int rindex, string[] cols, bool nullsAllowed)
static ConstraintInfo ForeignKey(ObjectName tableName, string columnName, ObjectName refTable, string refColumn)
Defines the base class for instances that represent SQL expression tree nodes.
static ObjectName[] QueryTablesRelationallyLinkedTo(this ITransaction transaction, ObjectName tableName)
TableRowVariableResolver(ITable table, int rowIndex)
The simplest implementation of a transaction.
static void AddForeignKey(this ITransaction transaction, ObjectName table, string[] columns, ObjectName refTable, string[] refColumns, String constraintName)
static void AddPrimaryKey(this ITransaction transaction, ObjectName tableName, string[] columns, ConstraintDeferrability deferred, string constraintName)
const int CheckViolation
A Check constraint violation error code.
override string ToString()
static ConstraintInfo Unique(ObjectName tableName, params string[] columnNames)
IEnumerable< int > IndexOfColumns(IEnumerable< string > columnNames)
static void CheckAddConstraintViolations(this ITransaction transaction, ITable table, int[] rowIndices, ConstraintDeferrability deferred)