DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
TableQueryExtensions.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 using System.Text;
21 
22 using Deveel.Data;
23 using Deveel.Data.Index;
25 using Deveel.Data.Sql.Objects;
26 using Deveel.Data.Text;
27 using Deveel.Data.Types;
28 
29 namespace Deveel.Data.Sql.Tables {
34  public static class TableQueryExtensions {
35  #region Get Value
36 
37  public static DataObject GetValue(this ITable table, int rowIndex, ObjectName columnName) {
38  return table.GetValue(rowIndex, table.IndexOfColumn(columnName));
39  }
40 
41  public static DataObject GetValue(this ITable table, int rowIndex, string columnName) {
42  return table.GetValue(rowIndex, table.ResolveColumnName(columnName));
43  }
44 
45  public static DataObject GetLastValue(this ITable table, int columnOffset) {
46  var rows = table.SelectLast(columnOffset).ToList();
47  return rows.Count > 0 ? table.GetValue(rows[0], columnOffset) : null;
48  }
49 
50  public static DataObject GetLastValue(this ITable table, string columnName) {
51  return table.GetLastValue(table.IndexOfColumn(columnName));
52  }
53 
54  public static DataObject[] GetLastValues(this ITable table, int[] columnOffsets) {
55  if (columnOffsets.Length > 1)
56  throw new ArgumentException("Multi-column gets not supported.");
57 
58  return new[] {table.GetLastValue(columnOffsets[0])};
59  }
60 
61  public static DataObject GetFirstValue(this ITable table, int columnOffset) {
62  var rows = table.SelectFirst(columnOffset).ToList();
63  return rows.Count > 0 ? table.GetValue(rows[0], columnOffset) : null;
64  }
65 
66  public static DataObject GetFirstValue(this ITable table, string columnName) {
67  return table.GetFirstValue(table.IndexOfColumn(columnName));
68  }
69 
70  public static DataObject[] GetFirstValues(this ITable table, int[] columnOffsets) {
71  if (columnOffsets.Length > 1)
72  throw new ArgumentException("Multi-column gets not supported.");
73 
74  return new[] {table.GetFirstValue(columnOffsets[0])};
75  }
76 
77  public static DataObject GetSingleValue(this ITable table, int columnOffset) {
78  IList<int> rows = table.SelectFirst(columnOffset).ToList();
79  int sz = rows.Count;
80  return sz == table.RowCount && sz > 0 ? table.GetValue(rows[0], columnOffset) : null;
81  }
82 
83  public static DataObject GetSingleValue(this ITable table, string columnName) {
84  return table.GetSingleValue(table.IndexOfColumn(columnName));
85  }
86 
87  public static DataObject[] GetSingleValues(this ITable table, int[] columnOffsets) {
88  if (columnOffsets.Length > 1)
89  throw new ArgumentException("Multi-column gets not supported.");
90 
91  return new[] {table.GetSingleValue(columnOffsets[0])};
92  }
93 
94  #endregion
95 
96  #region Get Row
97 
98  public static Row GetRow(this ITable table, int rowNumber) {
99  return new Row(table, new RowId(table.TableInfo.Id, rowNumber));
100  }
101 
102  #endregion
103 
104  public static IEnumerable<int> FindKeys(this ITable table, int[] columnOffsets, DataObject[] keyValue) {
105  int keySize = keyValue.Length;
106 
107  // Now command table 2 to determine if the key values are present.
108  // Use index scan on first key.
109  var columnIndex = table.GetIndex(columnOffsets[0]);
110  var list = columnIndex.SelectEqual(keyValue[0]).ToList();
111 
112  if (keySize <= 1)
113  return list;
114 
115  // Full scan for the rest of the columns
116  int sz = list.Count;
117 
118  // For each element of the list
119  for (int i = sz - 1; i >= 0; --i) {
120  int rIndex = list[i];
121  // For each key in the column list
122  for (int c = 1; c < keySize; ++c) {
123  int columnOffset = columnOffsets[c];
124  var columnValue = keyValue[c];
125  if (columnValue.CompareTo(table.GetValue(rIndex, columnOffset)) != 0) {
126  // If any values in the key are not equal set this flag to false
127  // and remove the index from the list.
128  list.RemoveAt(i);
129  // Break the for loop
130  break;
131  }
132  }
133  }
134 
135  return list;
136  }
137 
138  private static DataObject MakeObject(this ITable table, int columnOffset, ISqlObject value) {
139  if (columnOffset < 0 || columnOffset >= table.TableInfo.ColumnCount)
140  throw new ArgumentOutOfRangeException("columnOffset");
141 
142  var columnType = table.TableInfo[columnOffset].ColumnType;
143  return new DataObject(columnType, value);
144  }
145 
146  public static int IndexOfColumn(this ITable table, string columnName) {
147  if (table is IQueryTable)
148  return ((IQueryTable) table).FindColumn(ObjectName.Parse(columnName));
149 
150  return table.TableInfo.IndexOfColumn(columnName);
151  }
152 
153  public static int IndexOfColumn(this ITable table, ObjectName columnName) {
154  if (table is IQueryTable)
155  return ((IQueryTable) table).FindColumn(columnName);
156 
157  if (columnName.Parent != null &&
158  !columnName.Parent.Equals(table.TableInfo.TableName))
159  return -1;
160 
161  return table.TableInfo.IndexOfColumn(columnName);
162  }
163 
164  #region Select Rows
165 
166  public static IEnumerable<int> SelectRowsEqual(this ITable table, int columnIndex, DataObject value) {
167  return table.GetIndex(columnIndex).SelectEqual(value);
168  }
169 
170  public static IEnumerable<int> SelectRowsEqual(this ITable table, string columnName, DataObject value) {
171  return table.SelectRowsEqual(table.IndexOfColumn(columnName), value);
172  }
173 
174  public static IEnumerable<int> SelectNotEqual(this ITable table, int columnOffset, DataObject value) {
175  return table.GetIndex(columnOffset).SelectNotEqual(value);
176  }
177 
178  public static IEnumerable<int> SelectNotEqual(this ITable table, int columnOffset, ISqlObject value) {
179  return table.SelectNotEqual(columnOffset, table.MakeObject(columnOffset, value));
180  }
181 
182  public static IEnumerable<int> SelectRowsEqual(this ITable table, int columnIndex1, DataObject value1, int columnIndex2, DataObject value2) {
183  var result = new List<int>();
184 
185  var index1 = table.GetIndex(columnIndex1).SelectEqual(value1);
186  foreach (var rowIndex in index1) {
187  var tableValue = table.GetValue(rowIndex, columnIndex2);
188  if (tableValue.IsEqualTo(value2))
189  result.Add(rowIndex);
190  }
191 
192  return result;
193  }
194 
195  public static IEnumerable<int> SelectRowsRange(this ITable table, int column, IndexRange[] ranges) {
196  return table.GetIndex(column).SelectRange(ranges);
197  }
198 
199  public static IEnumerable<int> SelectRowsGreater(this ITable table, int columnOffset, DataObject value) {
200  return table.GetIndex(columnOffset).SelectGreater(value);
201  }
202 
203  public static IEnumerable<int> SelectRowsGreater(this ITable table, int columnOffset, ISqlObject value) {
204  return table.SelectRowsGreater(columnOffset, table.MakeObject(columnOffset, value));
205  }
206 
207  public static IEnumerable<int> SelectRowsGreaterOrEqual(this ITable table, int columnOffset, DataObject value) {
208  return table.GetIndex(columnOffset).SelectGreaterOrEqual(value);
209  }
210 
211  public static IEnumerable<int> SelectRowsGreaterOrEqual(this ITable table, int columnOffset, ISqlObject value) {
212  return table.SelectRowsGreaterOrEqual(columnOffset, table.MakeObject(columnOffset, value));
213  }
214 
215  public static IEnumerable<int> SelecRowsLess(this ITable table, int columnOffset, DataObject value) {
216  return table.GetIndex(columnOffset).SelectLess(value);
217  }
218 
219  public static IEnumerable<int> SelecRowsLess(this ITable table, int columnOffset, ISqlObject value) {
220  return table.SelecRowsLess(columnOffset, table.MakeObject(columnOffset, value));
221  }
222 
223  public static IEnumerable<int> SelectRowsLessOrEqual(this ITable table, int columnOffset, DataObject value) {
224  return table.GetIndex(columnOffset).SelectLessOrEqual(value);
225  }
226 
227  public static IEnumerable<int> SelectRowsLessOrEqual(this ITable table, int columnOffset, ISqlObject value) {
228  return table.SelectRowsLessOrEqual(columnOffset, table.MakeObject(columnOffset, value));
229  }
230 
231  public static IEnumerable<int> SelectAllRows(this ITable table, int columnOffset) {
232  return table.GetIndex(columnOffset).SelectAll();
233  }
234 
235  public static IEnumerable<int> SelectAllRows(this ITable table) {
236  return table.Select(x => x.RowId.RowNumber);
237  }
238 
239  public static IEnumerable<int> SelectLast(this ITable table, int columnOffset) {
240  return table.GetIndex(columnOffset).SelectLast();
241  }
242 
243  public static IEnumerable<int> SelectFirst(this ITable table, int columnOffset) {
244  return table.GetIndex(columnOffset).SelectFirst();
245  }
246 
247  public static IEnumerable<int> SelectRows(this ITable table,
248  IVariableResolver resolver,
249  IRequest context,
250  SqlBinaryExpression expression) {
251 
252  var objRef = expression.Left as SqlReferenceExpression;
253  if (objRef == null)
254  throw new NotSupportedException();
255 
256  var columnName = objRef.ReferenceName;
257 
258  var column = table.FindColumn(columnName);
259  if (column < 0)
260  throw new InvalidOperationException();
261 
262  var reduced = expression.Right.Evaluate(context, resolver);
263  if (reduced.ExpressionType != SqlExpressionType.Constant)
264  throw new InvalidOperationException();
265 
266  var value = ((SqlConstantExpression) reduced).Value;
267  var binOperator = expression.ExpressionType;
268 
269  return table.SelectRows(column, binOperator, value);
270  }
271 
272  public static IEnumerable<int> SelectRows(this ITable table, int[] columnOffsets, SqlExpressionType op,
273  DataObject[] values) {
274  if (columnOffsets.Length > 1)
275  throw new NotSupportedException("Multi-column selects not supported yet.");
276 
277  return SelectRows(table, columnOffsets[0], op, values[0]);
278  }
279 
280  public static IEnumerable<int> SelectRowsBetween(this ITable table, int column, DataObject minCell, DataObject maxCell) {
281  // Check all the tables are comparable
282  var colType = table.TableInfo[column].ColumnType;
283  if (!minCell.Type.IsComparable(colType) ||
284  !maxCell.Type.IsComparable(colType)) {
285  // Types not comparable, so return 0
286  return new List<int>(0);
287  }
288 
289  return table.GetIndex(column).SelectBetween(minCell, maxCell);
290  }
291 
292  public static IEnumerable<int> SelectRows(this ITable table, int column, SqlExpressionType op, DataObject value) {
293  // If the cell is of an incompatible type, return no results,
294  var colType = table.TableInfo[column].ColumnType;
295  if (!value.Type.IsComparable(colType)) {
296  // Types not comparable, so return 0
297  return new List<int>(0);
298  }
299 
300  // Get the selectable scheme for this column
301  var index = table.GetIndex(column);
302 
303  // If the operator is a standard operator, use the interned SelectableScheme
304  // methods.
305  if (op == SqlExpressionType.Equal)
306  return index.SelectEqual(value);
307  if (op == SqlExpressionType.NotEqual)
308  return index.SelectNotEqual(value);
309  if (op == SqlExpressionType.GreaterThan)
310  return index.SelectGreater(value);
311  if (op == SqlExpressionType.SmallerThan)
312  return index.SelectLess(value);
313  if (op == SqlExpressionType.GreaterOrEqualThan)
314  return index.SelectGreaterOrEqual(value);
315  if (op == SqlExpressionType.SmallerOrEqualThan)
316  return index.SelectLessOrEqual(value);
317 
318  // If it's not a standard operator (such as IS, NOT IS, etc) we generate the
319  // range set especially.
320  var rangeSet = new IndexRangeSet();
321  rangeSet = rangeSet.Intersect(op, value);
322  return index.SelectRange(rangeSet.ToArray());
323  }
324 
325  public static IEnumerable<int> Search(this ITable table, int column, string pattern) {
326  return table.Search(column, pattern, '\\');
327  }
328 
329  public static IEnumerable<int> Search(this ITable table, int column, string pattern, char escapeChar) {
330  var colType = table.TableInfo[column].ColumnType;
331 
332  // If the column type is not a string type then report an error.
333  if (!(colType is StringType))
334  throw new InvalidOperationException("Unable to perform a pattern search on a non-String type column.");
335 
336  // First handle the case that the column has an index that supports text search
337  var index = table.GetIndex(column);
338  if (index != null && index.HandlesTextSearch)
339  return index.SelectLike(DataObject.String(pattern));
340 
341  var colStringType = (StringType)colType;
342 
343  // ---------- Pre Search ----------
344 
345  // First perform a 'pre-search' on the head of the pattern. Note that
346  // there may be no head in which case the entire column is searched which
347  // has more potential to be expensive than if there is a head.
348 
349  StringBuilder prePattern = new StringBuilder();
350  int i = 0;
351  bool finished = i >= pattern.Length;
352  bool lastIsEscape = false;
353 
354  while (!finished) {
355  char c = pattern[i];
356  if (lastIsEscape) {
357  lastIsEscape = true;
358  prePattern.Append(c);
359  } else if (c == escapeChar) {
360  lastIsEscape = true;
361  } else if (!PatternSearch.IsWildCard(c)) {
362  prePattern.Append(c);
363 
364  ++i;
365  if (i >= pattern.Length) {
366  finished = true;
367  }
368 
369  } else {
370  finished = true;
371  }
372  }
373 
374  // This is set with the remaining search.
375  string postPattern;
376 
377  // This is our initial search row set. In the second stage, rows are
378  // eliminated from this vector.
379  IEnumerable<int> searchCase;
380 
381  if (i >= pattern.Length) {
382  // If the pattern has no 'wildcards' then just perform an EQUALS
383  // operation on the column and return the results.
384 
385  var cell = new DataObject(colType, new SqlString(pattern));
386  return SelectRows(table, column, SqlExpressionType.Equal, cell);
387  }
388 
389  if (prePattern.Length == 0 ||
390  colStringType.Locale != null) {
391 
392  // No pre-pattern easy search :-(. This is either because there is no
393  // pre pattern (it starts with a wild-card) or the locale of the string
394  // is non-lexicographical. In either case, we need to select all from
395  // the column and brute force the search space.
396 
397  searchCase = table.SelectAllRows(column);
398  postPattern = pattern;
399  } else {
400 
401  // Criteria met: There is a pre_pattern, and the column locale is
402  // lexicographical.
403 
404  // Great, we can do an upper and lower bound search on our pre-search
405  // set. eg. search between 'Geoff' and 'Geofg' or 'Geoff ' and
406  // 'Geoff\33'
407 
408  var lowerBounds = prePattern.ToString();
409  int nextChar = prePattern[i - 1] + 1;
410  prePattern[i - 1] = (char)nextChar;
411  var upperBounds = prePattern.ToString();
412 
413  postPattern = pattern.Substring(i);
414 
415  var cellLower = new DataObject(colType, new SqlString(lowerBounds));
416  var cellUpper = new DataObject(colType, new SqlString(upperBounds));
417 
418  // Select rows between these two points.
419 
420  searchCase = table.SelectRowsBetween(column, cellLower, cellUpper);
421  }
422 
423  // ---------- Post search ----------
424 
425  int preIndex = i;
426 
427  // Now eliminate from our 'search_case' any cells that don't match our
428  // search pattern.
429  // Note that by this point 'post_pattern' will start with a wild card.
430  // This follows the specification for the 'PatternMatch' method.
431  // EFFICIENCY: This is a brute force iterative search. Perhaps there is
432  // a faster way of handling this?
433 
434  var iList = new BlockIndex<int>(searchCase);
435  var enumerator = iList.GetEnumerator(0, iList.Count - 1);
436 
437  while (enumerator.MoveNext()) {
438  // Get the expression (the contents of the cell at the given column, row)
439 
440  bool patternMatches = false;
441  var cell = table.GetValue(enumerator.Current, column);
442  // Null values doesn't match with anything
443  if (!cell.IsNull) {
444  string expression = cell.AsVarChar().Value.ToString();
445  // We must remove the head of the string, which has already been
446  // found from the pre-search section.
447  expression = expression.Substring(preIndex);
448  patternMatches = PatternSearch.PatternMatch(postPattern, expression, escapeChar);
449  }
450  if (!patternMatches) {
451  // If pattern does not match then remove this row from the search.
452  enumerator.Remove();
453  }
454  }
455 
456  return iList.ToList();
457  }
458 
459  #endregion
460 
461  #region Select
462 
463  public static ITable SelectEqual(this ITable table, int columnIndex, DataObject value) {
464  return table.AsVirtual(() => table.SelectRowsEqual(columnIndex, value));
465  }
466 
467 
468  public static ITable SelectEqual(this ITable table, string columnName, DataObject value) {
469  return table.AsVirtual(() => table.SelectRowsEqual(columnName, value));
470  }
471 
472  public static ITable SelectAll(this ITable table, int columnOffset) {
473  return table.AsVirtual(() => table.SelectAllRows(columnOffset));
474  }
475 
476  public static ITable Select(this ITable table, IRequest context, SqlExpression expression) {
477  if (expression is SqlBinaryExpression) {
478  var binary = (SqlBinaryExpression)expression;
479 
480  // Perform the pattern search expression on the table.
481  // Split the expression,
482  var leftRef = binary.Left.AsReferenceName();
483  if (leftRef != null)
484  // LHS is a simple variable so do a simple select
485  return table.SimpleSelect(context, leftRef, binary.ExpressionType, binary.Right);
486  }
487 
488  // LHS must be a constant so we can just evaluate the expression
489  // and see if we get true, false, null, etc.
490  var v = expression.EvaluateToConstant(context, null);
491 
492  // If it evaluates to NULL or FALSE then return an empty set
493  if (v.IsNull || v == false)
494  return table.EmptySelect();
495 
496  return table;
497  }
498 
499  public static ITable SimpleSelect(this ITable table, IRequest context, ObjectName columnName, SqlExpressionType op, SqlExpression exp) {
500  // Find the row with the name given in the condition.
501  int column = table.FindColumn(columnName);
502 
503  if (column == -1)
504  throw new ArgumentException(String.Format("Unable to find the column {0} in the condition.", columnName.Name));
505 
506  // If we are doing a sub-query search
507  if (op.IsSubQuery()) {
508  // We can only handle constant expressions in the RHS expression, and
509  // we must assume that the RHS is a Expression[] array.
510  if (exp.ExpressionType != SqlExpressionType.Constant &&
511  exp.ExpressionType != SqlExpressionType.Tuple)
512  throw new ArgumentException();
513 
514  IEnumerable<SqlExpression> list;
515 
516  if (exp.ExpressionType == SqlExpressionType.Constant) {
517  var tob = ((SqlConstantExpression) exp).Value;
518  if (tob.Type is ArrayType) {
519  var array = (SqlArray) tob.Value;
520  list = array;
521  } else {
522  throw new Exception("Error with format or RHS expression.");
523  }
524  } else {
525  list = ((SqlTupleExpression) exp).Expressions;
526  }
527 
528  // Construct a temporary table with a single column that we are
529  // comparing to.
530  var col = table.TableInfo[column];
531  var ttable = TemporaryTable.SingleColumnTable(table.Context, col.ColumnName, col.ColumnType);
532 
533  foreach (var expression in list) {
534  var rowNum = ttable.NewRow();
535 
536  var evalExp = (SqlConstantExpression)expression.Evaluate(context, null, null);
537  ttable.SetValue(rowNum, 0, evalExp.Value);
538  }
539 
540  ttable.BuildIndexes();
541 
542  // Perform the any/all sub-query on the constant table.
543 
544  return table.SelectAnyAllNonCorrelated(new[] { columnName }, op, ttable);
545  }
546 
547  {
548  if (!exp.IsConstant())
549  throw new ArgumentException("The search expression is not constant.");
550 
551  var evalExp = exp.Evaluate(context, null);
552  if (evalExp.ExpressionType != SqlExpressionType.Constant)
553  throw new InvalidOperationException();
554 
555  var value = ((SqlConstantExpression) evalExp).Value;
556 
557  IEnumerable<int> rows;
558 
559  if (op == SqlExpressionType.Like ||
560  op == SqlExpressionType.NotLike
561  /* TODO: ||
562  op.IsOfType(BinaryOperatorType.Regex)*/) {
563 
564  /*
565  TODO:
566  if (op.IsOfType(BinaryOperatorType.Regex)) {
567  rows = SelectFromRegex(column, op, value);
568  } else {
569  */
570  rows = table.SelectFromPattern(column, op, value);
571  } else {
572 
573  // Is the column we are searching on indexable?
574  var colInfo = table.TableInfo[column];
575  if (!colInfo.IsIndexable)
576  throw new InvalidOperationException(String.Format("Column {0} os type {1} cannot be searched.", colInfo.ColumnName,
577  colInfo.ColumnType));
578 
579  rows = table.SelectRows(column, op, value);
580  }
581 
582  return new VirtualTable(table, rows.ToArray()) {SortColumn = column};
583  }
584  }
585 
586  public static ITable ExhaustiveSelect(this ITable table, IRequest context, SqlExpression expression) {
587  var result = table;
588 
589  // Exit early if there's nothing in the table to select from
590  int rowCount = table.RowCount;
591  if (rowCount > 0) {
592  var tableResolver = table.GetVariableResolver();
593  List<int> selectedSet = new List<int>(rowCount);
594 
595  foreach (var row in table) {
596  int rowIndex = row.RowId.RowNumber;
597 
598  var rowResolver = tableResolver.ForRow(rowIndex);
599 
600  // Resolve expression into a constant.
601  var exp = expression.Evaluate(context, rowResolver);
602  if (exp.ExpressionType != SqlExpressionType.Constant)
603  throw new NotSupportedException();
604 
605  var value = ((SqlConstantExpression) exp).Value;
606  // If resolved to true then include in the selected set.
607  if (!value.IsNull && value.Type is BooleanType &&
608  value == true) {
609  selectedSet.Add(rowIndex);
610  }
611  }
612 
613  result = new VirtualTable(table, selectedSet); ;
614  }
615 
616  return result;
617  }
618 
619 
620  public static IEnumerable<int> SelectFromPattern(this ITable table, int column, SqlExpressionType op, DataObject ob) {
621  if (ob.IsNull)
622  return new List<int>();
623 
624  if (op == SqlExpressionType.NotLike) {
625  // How this works:
626  // Find the set or rows that are like the pattern.
627  // Find the complete set of rows in the column.
628  // Sort the 'like' rows
629  // For each row that is in the original set and not in the like set,
630  // add to the result list.
631  // Result is the set of not like rows ordered by the column.
632 
633  var likeSet = (List<int>)table.Search(column, ob.ToString());
634  // Don't include NULL values
635  var nullCell = DataObject.Null(ob.Type);
636  IList<int> originalSet = table.SelectRows(column, SqlExpressionType.IsNot, nullCell).ToList();
637  int listSize = System.Math.Max(4, (originalSet.Count - likeSet.Count) + 4);
638  List<int> resultSet = new List<int>(listSize);
639  likeSet.Sort();
640  int size = originalSet.Count;
641  for (int i = 0; i < size; ++i) {
642  int val = originalSet[i];
643  // If val not in like set, add to result
644  if (likeSet.BinarySearch(val) == 0) {
645  resultSet.Add(val);
646  }
647  }
648  return resultSet;
649  }
650 
651  // if (op.is("like")) {
652  return table.Search(column, ob.ToString());
653  }
654 
655  public static ITable EmptySelect(this ITable table) {
656  if (table.RowCount == 0)
657  return table;
658 
659  return new VirtualTable(table, new int[0]);
660  }
661 
662  public static ITable DistinctBy(this ITable table, int[] columns) {
663  List<int> resultList = new List<int>();
664  var rowList = table.OrderRowsByColumns(columns).ToList();
665 
666  int rowCount = rowList.Count;
667  int previousRow = -1;
668  for (int i = 0; i < rowCount; ++i) {
669  int rowIndex = rowList[i];
670 
671  if (previousRow != -1) {
672 
673  bool equal = true;
674  // Compare cell in column in this row with previous row.
675  for (int n = 0; n < columns.Length && equal; ++n) {
676  var c1 = table.GetValue(columns[n], rowIndex);
677  var c2 = table.GetValue(columns[n], previousRow);
678  equal = (c1.CompareTo(c2) == 0);
679  }
680 
681  if (!equal) {
682  resultList.Add(rowIndex);
683  }
684  } else {
685  resultList.Add(rowIndex);
686  }
687 
688  previousRow = rowIndex;
689  }
690 
691  // Return the new table with distinct rows only.
692  return new VirtualTable(table, resultList);
693  }
694 
695  public static ITable DistinctBy(this ITable table, ObjectName[] columnNames) {
696  var mapSize = columnNames.Length;
697  var map = new int[mapSize];
698  for (int i = 0; i < mapSize; i++) {
699  map[i] = table.IndexOfColumn(columnNames[i]);
700  }
701 
702  return table.DistinctBy(map);
703  }
704 
705  public static ITable SelectRange(this ITable thisTable, ObjectName columnName, IndexRange[] ranges) {
706  // If this table is empty then there is no range to select so
707  // trivially return this object.
708  if (thisTable.RowCount == 0)
709  return thisTable;
710 
711  // Are we selecting a black or null range?
712  if (ranges == null || ranges.Length == 0)
713  // Yes, so return an empty table
714  return thisTable.EmptySelect();
715 
716  // Are we selecting the entire range?
717  if (ranges.Length == 1 &&
718  ranges[0].Equals(IndexRange.FullRange))
719  // Yes, so return this table.
720  return thisTable;
721 
722  // Must be a non-trivial range selection.
723 
724  // Find the column index of the column selected
725  int column = thisTable.IndexOfColumn(columnName);
726 
727  if (column == -1) {
728  throw new Exception(
729  "Unable to find the column given to select the range of: " +
730  columnName.Name);
731  }
732 
733  // Select the range
734  var rows = thisTable.SelectRowsRange(column, ranges);
735 
736  // Make a new table with the range selected
737  var result = new VirtualTable(thisTable, rows.ToArray());
738 
739  // We know the new set is ordered by the column.
740  result.SortColumn = column;
741 
742  return result;
743  }
744 
745  #region Sub-Query
746 
747  public static bool AllRowsMatchColumnValue(this ITable table, int columnOffset, SqlExpressionType op, DataObject value) {
748  var rows = table.SelectRows(columnOffset, op, value);
749  return rows.Count() == table.RowCount;
750  }
751 
752  public static ITable SelectAnyAllNonCorrelated(this ITable table, ObjectName[] leftColumns, SqlExpressionType op, ITable rightTable) {
753  if (rightTable.TableInfo.ColumnCount != leftColumns.Length) {
754  throw new ArgumentException(String.Format("The right table has {0} columns that is different from the specified column names ({1})",
755  rightTable.TableInfo.ColumnCount, leftColumns.Length));
756  }
757 
758  // Handle trivial case of no entries to select from
759  if (table.RowCount == 0)
760  return table;
761 
762  // Resolve the vars in the left table and check the references are
763  // compatible.
764  var sz = leftColumns.Length;
765  var leftColMap = new int[sz];
766  var rightColMap = new int[sz];
767  for (int i = 0; i < sz; ++i) {
768  leftColMap[i] = table.FindColumn(leftColumns[i]);
769  rightColMap[i] = i;
770 
771  if (leftColMap[i] == -1)
772  throw new Exception("Invalid reference: " + leftColumns[i]);
773 
774  var leftType = table.TableInfo[leftColMap[i]].ColumnType;
775  var rightType = rightTable.TableInfo[i].ColumnType;
776  if (!leftType.IsComparable(rightType)) {
777  throw new ArgumentException(String.Format("The type of the sub-query expression {0}({1}) " +
778  "is not compatible with the sub-query type {2}.",
779  leftColumns[i], leftType, rightType));
780  }
781  }
782 
783  IEnumerable<int> rows;
784 
785  if (!op.IsSubQuery())
786  throw new ArgumentException(String.Format("The operator {0} is not a sub-query form.", op));
787 
788  if (op.IsAll()) {
789  // ----- ALL operation -----
790  // We work out as follows:
791  // For >, >= type ALL we find the highest value in 'table' and
792  // select from 'source' all the rows that are >, >= than the
793  // highest value.
794  // For <, <= type ALL we find the lowest value in 'table' and
795  // select from 'source' all the rows that are <, <= than the
796  // lowest value.
797  // For = type ALL we see if 'table' contains a single value. If it
798  // does we select all from 'source' that equals the value, otherwise an
799  // empty table.
800  // For <> type ALL we use the 'not in' algorithm.
801 
802  if (op == SqlExpressionType.AllGreaterThan ||
803  op == SqlExpressionType.AllGreaterOrEqualThan) {
804  // Select the last from the set (the highest value),
805  var highestCells = rightTable.GetLastValues(rightColMap);
806  // Select from the source table all rows that are > or >= to the
807  // highest cell,
808  rows = table.SelectRows(leftColMap, op, highestCells);
809  } else if (op == SqlExpressionType.AllSmallerThan ||
810  op == SqlExpressionType.AllSmallerOrEqualThan) {
811  // Select the first from the set (the lowest value),
812  var lowestCells = rightTable.GetFirstValues(rightColMap);
813  // Select from the source table all rows that are < or <= to the
814  // lowest cell,
815  rows = table.SelectRows(leftColMap, op, lowestCells);
816  } else if (op == SqlExpressionType.AllEqual) {
817  // Select the single value from the set (if there is one).
818  var singleCell = rightTable.GetSingleValues(rightColMap);
819  if (singleCell != null) {
820  // Select all from source_table all values that = this cell
821  rows = table.SelectRows(leftColMap, op, singleCell);
822  } else {
823  // No single value so return empty set (no value in LHS will equal
824  // a value in RHS).
825  return table.EmptySelect();
826  }
827  } else if (op == SqlExpressionType.AllNotEqual) {
828  // Equiv. to NOT IN
829  rows = table.SelectRowsNotIn(rightTable, leftColMap, rightColMap);
830  } else {
831  throw new ArgumentException(String.Format("Operator of type {0} is not valid in ALL functions.", op.SubQueryPlainType()));
832  }
833  } else {
834  // ----- ANY operation -----
835  // We work out as follows:
836  // For >, >= type ANY we find the lowest value in 'table' and
837  // select from 'source' all the rows that are >, >= than the
838  // lowest value.
839  // For <, <= type ANY we find the highest value in 'table' and
840  // select from 'source' all the rows that are <, <= than the
841  // highest value.
842  // For = type ANY we use same method from INHelper.
843  // For <> type ANY we iterate through 'source' only including those
844  // rows that a <> query on 'table' returns size() != 0.
845 
846  if (op == SqlExpressionType.AnyGreaterThan ||
847  op == SqlExpressionType.AnyGreaterOrEqualThan) {
848  // Select the first from the set (the lowest value),
849  var lowestCells = rightTable.GetFirstValues(rightColMap);
850  // Select from the source table all rows that are > or >= to the
851  // lowest cell,
852  rows = table.SelectRows(leftColMap, op, lowestCells);
853  } else if (op == SqlExpressionType.AnySmallerThan ||
854  op == SqlExpressionType.AnySmallerOrEqualThan) {
855  // Select the last from the set (the highest value),
856  var highestCells = rightTable.GetLastValues(rightColMap);
857  // Select from the source table all rows that are < or <= to the
858  // highest cell,
859  rows = table.SelectRows(leftColMap, op, highestCells);
860  } else if (op == SqlExpressionType.AnyEqual) {
861  // Equiv. to IN
862  rows = table.SelectRowsIn(rightTable, leftColMap, rightColMap);
863  } else if (op == SqlExpressionType.AnyNotEqual) {
864  // Select the value that is the same of the entire column
865  var cells = rightTable.GetSingleValues(rightColMap);
866  if (cells != null) {
867  // All values from 'source_table' that are <> than the given cell.
868  rows = table.SelectRows(leftColMap, op, cells);
869  } else {
870  // No, this means there are different values in the given set so the
871  // query evaluates to the entire table.
872  return table;
873  }
874  } else {
875  throw new ArgumentException(String.Format("Operator of type {0} is not valid in ANY functions.", op.SubQueryPlainType()));
876  }
877  }
878 
879  return new VirtualTable(table, rows.ToArray());
880  }
881 
882  public static ITable Union(this ITable thisTable, ITable otherTable) {
883  // Optimizations - handle trivial case of row count in one of the tables
884  // being 0.
885  // NOTE: This optimization assumes this table and the unioned table are
886  // of the same type.
887  if ((thisTable.RowCount == 0 && otherTable.RowCount == 0) ||
888  otherTable.RowCount == 0) {
889  return thisTable;
890  }
891 
892  if (thisTable.RowCount == 0)
893  return otherTable;
894 
895  // First we merge this table with the input table.
896 
897  var raw1 = thisTable.GetRawTableInfo();
898  var raw2 = otherTable.GetRawTableInfo();
899 
900  // This will throw an exception if the table types do not match up.
901 
902  var union = raw1.Union(raw2);
903 
904  // Now 'union' contains a list of uniquely merged rows (ie. the union).
905  // Now make it into a new table and return the information.
906 
907  var tableList = union.GetTables().AsEnumerable();
908  return new VirtualTable(tableList, union.GetRows());
909  }
910 
921  public static IEnumerable<int> SelectRowsIn(this ITable table, ITable other, int column1, int column2) {
922  // First pick the the smallest and largest table. We only want to iterate
923  // through the smallest table.
924  // NOTE: This optimisation can't be performed for the 'not_in' command.
925 
926  ITable smallTable;
927  ITable largeTable;
928  int smallColumn;
929  int largeColumn;
930 
931  if (table.RowCount < other.RowCount) {
932  smallTable = table;
933  largeTable = other;
934 
935  smallColumn = column1;
936  largeColumn = column2;
937 
938  } else {
939  smallTable = other;
940  largeTable = table;
941 
942  smallColumn = column2;
943  largeColumn = column1;
944  }
945 
946  // Iterate through the small table's column. If we can find identical
947  // cells in the large table's column, then we should include the row in our
948  // final result.
949 
950  var resultRows = new BlockIndex<int>();
951  var op = SqlExpressionType.Equal;
952 
953  foreach (var row in smallTable) {
954  var cell = row.GetValue(smallColumn);
955 
956  var selectedSet = largeTable.SelectRows(largeColumn, op, cell).ToList();
957 
958  // We've found cells that are IN both columns,
959 
960  if (selectedSet.Count > 0) {
961  // If the large table is what our result table will be based on, append
962  // the rows selected to our result set. Otherwise add the index of
963  // our small table. This only works because we are performing an
964  // EQUALS operation.
965 
966  if (largeTable == table) {
967  // Only allow unique rows into the table set.
968  int sz = selectedSet.Count;
969  bool rs = true;
970  for (int i = 0; rs && i < sz; ++i) {
971  rs = resultRows.UniqueInsertSort(selectedSet[i]);
972  }
973  } else {
974  // Don't bother adding in sorted order because it's not important.
975  resultRows.Add(row.RowId.RowNumber);
976  }
977  }
978  }
979 
980  return resultRows.ToList();
981  }
982 
991  public static IEnumerable<int> SelectRowsIn(this ITable table, ITable other, int[] t1Cols, int[] t2Cols) {
992  if (t1Cols.Length > 1)
993  throw new NotSupportedException("Multi-column 'in' not supported yet.");
994 
995  return table.SelectRowsIn(other, t1Cols[0], t2Cols[0]);
996  }
997 
1010  public static IEnumerable<int> SelectRowsNotIn(this ITable table, ITable other, int col1, int col2) {
1011  // Handle trivial cases
1012  int t2RowCount = other.RowCount;
1013  if (t2RowCount == 0)
1014  // No rows so include all rows.
1015  return table.SelectAllRows(col1);
1016 
1017  if (t2RowCount == 1) {
1018  // 1 row so select all from table1 that doesn't equal the value.
1019  var en = other.GetEnumerator();
1020  if (!en.MoveNext())
1021  throw new InvalidOperationException("Cannot iterate through table rows.");
1022 
1023  var cell = other.GetValue(en.Current.RowId.RowNumber, col2);
1024  return table.SelectRows(col1, SqlExpressionType.NotEqual, cell);
1025  }
1026 
1027  // Iterate through table1's column. If we can find identical cell in the
1028  // tables's column, then we should not include the row in our final
1029  // result.
1030  List<int> resultRows = new List<int>();
1031 
1032  foreach (var row in table) {
1033  int rowIndex = row.RowId.RowNumber;
1034  var cell = row.GetValue(col1);
1035 
1036  var selectedSet = other.SelectRows(col2, SqlExpressionType.Equal, cell);
1037 
1038  // We've found a row in table1 that doesn't have an identical cell in
1039  // other, so we should include it in the result.
1040 
1041  if (!selectedSet.Any())
1042  resultRows.Add(rowIndex);
1043  }
1044 
1045  return resultRows;
1046  }
1047 
1056  public static IEnumerable<int> SelectRowsNotIn(this ITable table, ITable other, int[] t1Cols, int[] t2Cols) {
1057  if (t1Cols.Length > 1)
1058  throw new NotSupportedException("Multi-column 'not in' not supported yet.");
1059 
1060  return table.SelectRowsNotIn(other, t1Cols[0], t2Cols[0]);
1061  }
1062 
1063  public static ITable NotIn(this ITable table, ITable otherTable, int[] tableColumns, int[] otherColumns) {
1064  return table.AsVirtual(() => SelectRowsNotIn(table, otherTable, tableColumns, otherColumns));
1065  }
1066 
1067  public static ITable Composite(this ITable table, ITable other, CompositeFunction function, bool all) {
1068  return new CompositeTable(table, new[] { table, other }, function, all);
1069  }
1070 
1071  public static ITable Execept(this ITable table, ITable other, bool all) {
1072  return table.Composite(other, CompositeFunction.Except, all);
1073  }
1074 
1075  public static ITable Intersect(this ITable table, ITable other, bool all) {
1076  return table.Composite(other, CompositeFunction.Intersect, all);
1077  }
1078 
1079  #endregion
1080 
1081  #region Join
1082 
1083  public static ITable Join(this ITable table, ITable otherTable, bool quick) {
1084  ITable outTable;
1085 
1086  if (quick) {
1087  // This implementation doesn't materialize the join
1088  outTable = new NaturallyJoinedTable(table, otherTable);
1089  } else {
1090  var tabs = new [] { table, otherTable};
1091  var rowSets = new IList<int>[2];
1092 
1093  // Optimized trivial case, if either table has zero rows then result of
1094  // join will contain zero rows also.
1095  if (table.RowCount == 0 || otherTable.RowCount == 0) {
1096  rowSets[0] = new List<int>(0);
1097  rowSets[1] = new List<int>(0);
1098  } else {
1099  // The natural join algorithm.
1100  List<int> thisRowSet = new List<int>();
1101  List<int> tableRowSet = new List<int>();
1102 
1103  // Get the set of all rows in the given table.
1104  var tableSelectedSet = otherTable.Select(x => x.RowId.RowNumber).ToList();
1105 
1106  int tableSelectedSetSize = tableSelectedSet.Count;
1107 
1108  // Join with the set of rows in this table.
1109  var e = table.GetEnumerator();
1110  while (e.MoveNext()) {
1111  int rowIndex = e.Current.RowId.RowNumber;
1112  for (int i = 0; i < tableSelectedSetSize; ++i) {
1113  thisRowSet.Add(rowIndex);
1114  }
1115 
1116  tableRowSet.AddRange(tableSelectedSet);
1117  }
1118 
1119  // The row sets we are joining from each table.
1120  rowSets[0] = thisRowSet;
1121  rowSets[1] = tableRowSet;
1122  }
1123 
1124  // Create the new VirtualTable with the joined tables.
1125  outTable = new VirtualTable(tabs, rowSets);
1126  }
1127 
1128  return outTable;
1129  }
1130 
1131  public static ITable NaturalJoin(this ITable table, ITable otherTable) {
1132  return table.Join(otherTable, true);
1133  }
1134 
1135  public static ITable Join(this ITable table, IRequest context, ITable other, ObjectName columnName, SqlExpressionType operatorType,
1136  SqlExpression expression) {
1137  var rightExpression = expression;
1138  // If the rightExpression is a simple variable then we have the option
1139  // of optimizing this join by putting the smallest table on the LHS.
1140  var rhsVar = rightExpression.AsReferenceName();
1141  var lhsVar = columnName;
1142  var op = operatorType;
1143 
1144  if (rhsVar != null) {
1145  // We should arrange the expression so the right table is the smallest
1146  // of the sides.
1147  // If the left result is less than the right result
1148 
1149  if (table.RowCount < other.RowCount) {
1150  // Reverse the join
1151  rightExpression = SqlExpression.Reference(lhsVar);
1152  lhsVar = rhsVar;
1153  op = op.Reverse();
1154 
1155  // Reverse the tables.
1156  var t = other;
1157  other = table;
1158  table = t;
1159  }
1160  }
1161 
1162  var joinExp = SqlExpression.Binary(SqlExpression.Reference(lhsVar), op, rightExpression);
1163 
1164  // The join operation.
1165  return table.SimpleJoin(context, other, joinExp);
1166  }
1167 
1168  public static ITable SimpleJoin(this ITable thisTable, IRequest context, ITable other, SqlBinaryExpression binary) {
1169  var objRef = binary.Left as SqlReferenceExpression;
1170  if (objRef == null)
1171  throw new ArgumentException();
1172 
1173  // Find the row with the name given in the condition.
1174  int lhsColumn = thisTable.FindColumn(objRef.ReferenceName);
1175 
1176  if (lhsColumn == -1)
1177  throw new Exception("Unable to find the LHS column specified in the condition: " + objRef.ReferenceName);
1178 
1179  // Create a variable resolver that can resolve columns in the destination
1180  // table.
1181  var resolver = other.GetVariableResolver();
1182 
1183  // The join algorithm. It steps through the RHS expression, selecting the
1184  // cells that match the relation from the LHS table (this table).
1185 
1186  var thisRowSet = new List<int>();
1187  var tableRowSet = new List<int>();
1188 
1189  var e = other.GetEnumerator();
1190 
1191  while (e.MoveNext()) {
1192  int rowIndex = e.Current.RowId.RowNumber;
1193 
1194  var rowResolver = resolver.ForRow(rowIndex);
1195 
1196  // Select all the rows in this table that match the joining condition.
1197  var selectedSet = thisTable.SelectRows(rowResolver, context, binary);
1198 
1199  var selectList = selectedSet.ToList();
1200 
1201  var size = selectList.Count;
1202  // Include in the set.
1203  for (int i = 0; i < size; i++) {
1204  tableRowSet.Add(rowIndex);
1205  }
1206 
1207  thisRowSet.AddRange(selectList);
1208  }
1209 
1210  // Create the new VirtualTable with the joined tables.
1211 
1212  var tabs = new[] {thisTable, other};
1213  var rowSets = new IList<int>[] {thisRowSet, tableRowSet};
1214 
1215  return new VirtualTable(tabs, rowSets);
1216  }
1217 
1218  public static ITable OuterJoin(this ITable table, ITable rightTable) {
1219  // Form the row list for right hand table,
1220  var rowList = rightTable.Select(x => x.RowId.RowNumber).ToList();
1221 
1222  int colIndex = rightTable.IndexOfColumn(table.GetResolvedColumnName(0));
1223  rowList = rightTable.ResolveRows(colIndex, rowList, table).ToList();
1224 
1225  // This row set
1226  var thisTableSet = table.Select(x => x.RowId.RowNumber).ToList();
1227 
1228  thisTableSet.Sort();
1229  rowList.Sort();
1230 
1231  // Find all rows that are in 'this table' and not in 'right'
1232  List<int> resultList = new List<int>(96);
1233  int size = thisTableSet.Count;
1234  int rowListIndex = 0;
1235  int rowListSize = rowList.Count;
1236  for (int i = 0; i < size; ++i) {
1237  int thisVal = thisTableSet[i];
1238  if (rowListIndex < rowListSize) {
1239  int inVal = rowList[rowListIndex];
1240  if (thisVal < inVal) {
1241  resultList.Add(thisVal);
1242  } else if (thisVal == inVal) {
1243  while (rowListIndex < rowListSize &&
1244  rowList[rowListIndex] == inVal) {
1245  ++rowListIndex;
1246  }
1247  } else {
1248  throw new InvalidOperationException("'this_val' > 'in_val'");
1249  }
1250  } else {
1251  resultList.Add(thisVal);
1252  }
1253  }
1254 
1255  // Return the new VirtualTable
1256  return new VirtualTable(table, resultList);
1257  }
1258 
1259  public static ITable EquiJoin(this ITable table, IRequest context, ITable other, ObjectName[] leftColumns, ObjectName[] rightColumns) {
1260  // TODO: This needs to migrate to a better implementation that
1261  // exploits multi-column indexes if one is defined that can be used.
1262 
1263  var firstLeft = SqlExpression.Reference(leftColumns[0]);
1264  var firstRight = SqlExpression.Reference(rightColumns[0]);
1265  var onExpression = SqlExpression.Equal(firstLeft, firstRight);
1266 
1267  var result = table.SimpleJoin(context, other, onExpression);
1268 
1269  int sz = leftColumns.Length;
1270 
1271  // If there are columns left to equi-join, we resolve the rest with a
1272  // single exhaustive select of the form,
1273  // ( table1.col2 = table2.col2 AND table1.col3 = table2.col3 AND ... )
1274  if (sz > 1) {
1275  // Form the expression
1276  SqlExpression restExpression = null;
1277  for (int i = 1; i < sz; ++i) {
1278  var left = SqlExpression.Reference(leftColumns[i]);
1279  var right = SqlExpression.Reference(rightColumns[i]);
1280  var equalExp = SqlExpression.And(left, right);
1281 
1282  if (restExpression == null) {
1283  restExpression = equalExp;
1284  } else {
1285  restExpression = SqlExpression.And(restExpression, equalExp);
1286  }
1287  }
1288 
1289  result = result.ExhaustiveSelect(context, restExpression);
1290  }
1291 
1292  return result;
1293  }
1294 
1295  #endregion
1296 
1297  #region Order By
1298 
1299  public static IEnumerable<int> OrderRowsByColumns(this ITable table, int[] columns) {
1300  var work = table.OrderBy(columns);
1301  // 'work' is now sorted by the columns,
1302  // Get the rows in this tables domain,
1303  var rowList = work.Select(row => row.RowId.RowNumber);
1304 
1305  return work.ResolveRows(0, rowList, table);
1306  }
1307 
1308 
1309  public static ITable OrderBy(this ITable table, int[] columns) {
1310  // Sort by the column list.
1311  ITable resultTable = table;
1312  for (int i = columns.Length - 1; i >= 0; --i) {
1313  resultTable = resultTable.OrderBy(columns[i], true);
1314  }
1315 
1316  // A nice post condition to check on.
1317  if (resultTable.RowCount != table.RowCount)
1318  throw new InvalidOperationException("The final row count mismatches.");
1319 
1320  return table;
1321  }
1322 
1323  public static ITable OrderBy(this ITable table, int columnIndex, bool ascending) {
1324  if (table == null)
1325  return null;
1326 
1327  var rows = table.SelectAllRows(columnIndex);
1328 
1329  // Reverse the list if we are not ascending
1330  if (@ascending == false)
1331  rows = rows.Reverse();
1332 
1333  return new VirtualTable(table, rows.ToArray());
1334  }
1335 
1336  public static ITable OrderBy(this ITable table, ObjectName columnName, bool ascending) {
1337  var columnOffset = table.IndexOfColumn(columnName);
1338  if (columnOffset == -1)
1339  throw new ArgumentException(String.Format("Column '{0}' was not found in table.", columnName));
1340 
1341  return table.OrderBy(columnOffset, @ascending);
1342  }
1343 
1344  public static ITable OrderBy(this ITable table, ObjectName[] columnNames, bool[] ascending) {
1345  var result = table;
1346  // Sort the results by the columns in reverse-safe order.
1347  int sz = ascending.Length;
1348  for (int n = sz - 1; n >= 0; --n) {
1349  result = result.OrderBy(columnNames[n], ascending[n]);
1350  }
1351  return result;
1352  }
1353 
1354  public static ITable OrderBy(this ITable table, string columnName, bool ascending) {
1355  return table.OrderBy(table.ResolveColumnName(columnName), ascending);
1356  }
1357 
1358  #endregion
1359 
1360  public static ITable Subset(this ITable table, ObjectName[] columnNames, ObjectName[] aliases) {
1361  var columnMap = new int[columnNames.Length];
1362 
1363  for (int i = 0; i < columnMap.Length; i++) {
1364  columnMap[i] = table.IndexOfColumn(columnNames[i]);
1365  }
1366 
1367  return new SubsetColumnTable(table, columnMap, aliases);
1368  }
1369 
1370  #endregion
1371 
1372  public static bool Exists(this ITable table, int columnOffset, DataObject value) {
1373  return table.SelectRowsEqual(columnOffset, value).Any();
1374  }
1375 
1376  public static bool Exists(this ITable table, int columnOffset1, DataObject value1, int columnOffset2, DataObject value2) {
1377  return table.SelectRowsEqual(columnOffset1, value1, columnOffset2, value2).Any();
1378  }
1379 
1380  private static ITable AsVirtual(this ITable table, Func<IEnumerable<int>> selector) {
1381  return new VirtualTable(table, selector().ToArray());
1382  }
1383 
1384  public static ITable ColumnMerge(this ITable table, ITable other) {
1385  if (table.RowCount != other.RowCount)
1386  throw new InvalidOperationException("Tables have different row counts.");
1387 
1388  // Create the new VirtualTable with the joined tables.
1389 
1390  List<int> allRowSet = new List<int>();
1391  int rcount = table.RowCount;
1392  for (int i = 0; i < rcount; ++i) {
1393  allRowSet.Add(i);
1394  }
1395 
1396  var tabs = new[] { table, other };
1397  var rowSets = new IList<int>[] { allRowSet, allRowSet };
1398 
1399  return new VirtualTable(tabs, rowSets);
1400  }
1401 
1402  public static Dictionary<string, ISqlObject> ToDictionary(this ITable table) {
1403  if (table.TableInfo.ColumnCount != 2)
1404  throw new NotSupportedException("Table must have two columns.");
1405 
1406  var map = new Dictionary<string, ISqlObject>();
1407  foreach (var row in table) {
1408  var key = row.GetValue(0);
1409  var value = row.GetValue(1);
1410  map[key.AsVarChar().Value.ToString()] = value.Value;
1411  }
1412 
1413  return map;
1414  }
1415  }
1416 }
IEnumerable< int > SelectEqual(DataObject ob)
Definition: ColumnIndex.cs:180
virtual IEnumerable< int > SelectFirst()
Definition: ColumnIndex.cs:142
Provides a set of extension methods to ITable and IMutableTable objects.
bool IsNull
Gets a value that indicates if this object is materialized as null.
Definition: DataObject.cs:91
Defines the contract to access the data contained into a table of a database.
Definition: ITable.cs:40
SqlType Type
Gets the SqlType that defines the object properties
Definition: DataObject.cs:78
static ObjectName Parse(string s)
Parses the given string into a ObjectName object.
Definition: ObjectName.cs:139
static IEnumerable< int > SelectRowsGreater(this ITable table, int columnOffset, ISqlObject value)
static DataObject MakeObject(this ITable table, int columnOffset, ISqlObject value)
An expression that references an object within a context.
A long string in the system.
IEnumerable< int > SelectGreaterOrEqual(DataObject ob)
Definition: ColumnIndex.cs:222
static SqlBinaryExpression And(SqlExpression left, SqlExpression right)
static ITable SimpleSelect(this ITable table, IRequest context, ObjectName columnName, SqlExpressionType op, SqlExpression exp)
static Row GetRow(this ITable table, int rowNumber)
static DataObject GetSingleValue(this ITable table, string columnName)
static ITable NotIn(this ITable table, ITable otherTable, int[] tableColumns, int[] otherColumns)
static bool AllRowsMatchColumnValue(this ITable table, int columnOffset, SqlExpressionType op, DataObject value)
static IEnumerable< int > Search(this ITable table, int column, string pattern, char escapeChar)
static ITable OuterJoin(this ITable table, ITable rightTable)
static ITable Execept(this ITable table, ITable other, bool all)
IEnumerable< int > SelectLast()
Definition: ColumnIndex.cs:158
static ITable Union(this ITable thisTable, ITable otherTable)
Describes the range of values to select from an index.
Definition: IndexRange.cs:38
static readonly IndexRange FullRange
The entire range of values in an index (including NULL)
Definition: IndexRange.cs:74
int Id
Gets a unique identifier of the table in a database system.
Definition: TableInfo.cs:107
static IEnumerable< int > SelectRowsEqual(this ITable table, string columnName, DataObject value)
virtual bool IsComparable(SqlType type)
Verifies if a given SqlType is comparable to this data-type.
Definition: SqlType.cs:137
static IEnumerable< int > Search(this ITable table, int column, string pattern)
static IEnumerable< int > SelectFirst(this ITable table, int columnOffset)
static DataObject Null(SqlType type)
Definition: DataObject.cs:630
static ITable SelectAll(this ITable table, int columnOffset)
static IEnumerable< int > SelecRowsLess(this ITable table, int columnOffset, DataObject value)
static IEnumerable< int > SelectRowsGreaterOrEqual(this ITable table, int columnOffset, ISqlObject value)
static SqlBinaryExpression Equal(SqlExpression left, SqlExpression right)
static IEnumerable< int > SelectFromPattern(this ITable table, int column, SqlExpressionType op, DataObject ob)
static bool Exists(this ITable table, int columnOffset, DataObject value)
Describes the name of an object within a database.
Definition: ObjectName.cs:44
ISqlObject Value
Gets the underlined value that is handled.
Definition: DataObject.cs:84
A single row in a table of a database.
Definition: Row.cs:44
static DataObject String(string s)
Definition: DataObject.cs:592
override bool Equals(object obj)
Definition: ObjectName.cs:241
static ITable ExhaustiveSelect(this ITable table, IRequest context, SqlExpression expression)
static DataObject[] GetFirstValues(this ITable table, int[] columnOffsets)
DataObject GetValue(long rowNumber, int columnOffset)
Gets a single cell within the table that is located at the given column offset and row...
SqlExpressionType
All the possible type of SqlExpression supported
static ITable SimpleJoin(this ITable thisTable, IRequest context, ITable other, SqlBinaryExpression binary)
static bool PatternMatch(string pattern, string expression, char escapeChar)
This is the pattern match recurrsive method.
ObjectName TableName
Gets the fully qualified name of the table that is ensured to be unique within the system...
Definition: TableInfo.cs:97
static DataObject GetValue(this ITable table, int rowIndex, ObjectName columnName)
static TemporaryTable SingleColumnTable(IContext database, string columnName, SqlType columnType)
Defines the contract for a valid SQL Object
Definition: ISqlObject.cs:23
static ITable OrderBy(this ITable table, ObjectName[] columnNames, bool[] ascending)
virtual SqlExpression Evaluate(EvaluateContext context)
When overridden by a derived class, this method evaluates the expression within the provided context...
DataObject AsVarChar()
Definition: DataObject.cs:528
static IEnumerable< int > SelectRowsBetween(this ITable table, int column, DataObject minCell, DataObject maxCell)
static IEnumerable< int > SelectRowsNotIn(this ITable table, ITable other, int[] t1Cols, int[] t2Cols)
A multi-column version of NOT IN.
static ITable Join(this ITable table, ITable otherTable, bool quick)
static ITable Intersect(this ITable table, ITable other, bool all)
static ITable SelectAnyAllNonCorrelated(this ITable table, ObjectName[] leftColumns, SqlExpressionType op, ITable rightTable)
static ITable Composite(this ITable table, ITable other, CompositeFunction function, bool all)
static IEnumerable< int > SelectNotEqual(this ITable table, int columnOffset, ISqlObject value)
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
static IEnumerable< int > SelectRowsIn(this ITable table, ITable other, int[] t1Cols, int[] t2Cols)
A multi-column version of IN.
IEnumerable< int > SelectRange(IndexRange range)
Definition: ColumnIndex.cs:130
static IEnumerable< int > SelectRowsEqual(this ITable table, int columnIndex1, DataObject value1, int columnIndex2, DataObject value2)
static ITable Join(this ITable table, IRequest context, ITable other, ObjectName columnName, SqlExpressionType operatorType, SqlExpression expression)
static DataObject GetLastValue(this ITable table, string columnName)
static ITable DistinctBy(this ITable table, ObjectName[] columnNames)
static ITable OrderBy(this ITable table, int[] columns)
static IEnumerable< int > FindKeys(this ITable table, int[] columnOffsets, DataObject[] keyValue)
TableInfo TableInfo
Gets the metadata information of the table, used to resolve the column sources.
Definition: ITable.cs:47
static int IndexOfColumn(this ITable table, ObjectName columnName)
int RowCount
Gets the total number of rows in the table.
Definition: ITable.cs:52
static DataObject GetLastValue(this ITable table, int columnOffset)
static bool IsWildCard(char ch)
Returns true if the given character is a wild card (unknown).
static Dictionary< string, ISqlObject > ToDictionary(this ITable table)
IEnumerable< int > SelectGreater(DataObject ob)
Definition: ColumnIndex.cs:204
IEnumerable< int > SelectBetween(DataObject ob1, DataObject ob2)
Definition: ColumnIndex.cs:240
static ITable SelectEqual(this ITable table, string columnName, DataObject value)
static DataObject[] GetLastValues(this ITable table, int[] columnOffsets)
int IndexOfColumn(string columnName)
Gets the offset of the column with the given name.
Definition: TableInfo.cs:310
static IEnumerable< int > SelectRowsNotIn(this ITable table, ITable other, int col1, int col2)
This implements the not in command.
An interface to resolve a variable name to a constant object.
static IEnumerable< int > SelectRowsLessOrEqual(this ITable table, int columnOffset, DataObject value)
static ITable Select(this ITable table, IRequest context, SqlExpression expression)
Defines the value of a ROWID object, that is a unique reference within a database system to a single ...
Definition: RowId.cs:24
ObjectName Parent
Gets the parent reference of the current one, if any or null if none.
Definition: ObjectName.cs:99
static IEnumerable< int > SelectRowsGreater(this ITable table, int columnOffset, DataObject value)
static ITable AsVirtual(this ITable table, Func< IEnumerable< int >> selector)
override bool Equals(object obj)
Definition: IndexRange.cs:106
IEnumerable< int > SelectLess(DataObject ob)
Definition: ColumnIndex.cs:213
An expression that holds a constant value.
static IEnumerable< int > SelectNotEqual(this ITable table, int columnOffset, DataObject value)
static DataObject GetValue(this ITable table, int rowIndex, string columnName)
string Name
Gets the name of the object being referenced.
Definition: ObjectName.cs:108
static ITable OrderBy(this ITable table, int columnIndex, bool ascending)
static ITable Subset(this ITable table, ObjectName[] columnNames, ObjectName[] aliases)
static SqlReferenceExpression Reference(ObjectName objectName)
static ITable SelectEqual(this ITable table, int columnIndex, DataObject value)
static ITable DistinctBy(this ITable table, int[] columns)
static ITable EquiJoin(this ITable table, IRequest context, ITable other, ObjectName[] leftColumns, ObjectName[] rightColumns)
static int IndexOfColumn(this ITable table, string columnName)
static IEnumerable< int > SelectLast(this ITable table, int columnOffset)
int ColumnCount
Gets a count of the columns defined by this object.
Definition: TableInfo.cs:159
static DataObject GetFirstValue(this ITable table, int columnOffset)
static IEnumerable< int > SelectRows(this ITable table, int[] columnOffsets, SqlExpressionType op, DataObject[] values)
IEnumerable< int > SelectNotEqual(DataObject ob)
Definition: ColumnIndex.cs:189
static IEnumerable< int > SelectRowsRange(this ITable table, int column, IndexRange[] ranges)
static DataObject GetFirstValue(this ITable table, string columnName)
CompositeFunction
The kind of composite function in a CompositeTable.
static SqlBinaryExpression Binary(SqlExpression left, SqlExpressionType expressionType, SqlExpression right)
static IEnumerable< int > SelectRowsGreaterOrEqual(this ITable table, int columnOffset, DataObject value)
Defines the base class for instances that represent SQL expression tree nodes.
An object that provides methods for accessing a finite collection of SQL expressions.
Definition: SqlArray.cs:28
static IEnumerable< int > SelectRows(this ITable table, IVariableResolver resolver, IRequest context, SqlBinaryExpression expression)
IEnumerable< int > SelectLessOrEqual(DataObject ob)
Definition: ColumnIndex.cs:231
Object used to represent a column in the ORDER BY clauses of a select statement.
Definition: SortColumn.cs:26
static ITable SelectRange(this ITable thisTable, ObjectName columnName, IndexRange[] ranges)
abstract SqlExpressionType ExpressionType
Gets the type code of this SQL expression.
static IEnumerable< int > SelectRowsIn(this ITable table, ITable other, int column1, int column2)
This implements the in command.
static IEnumerable< int > SelecRowsLess(this ITable table, int columnOffset, ISqlObject value)
static DataObject[] GetSingleValues(this ITable table, int[] columnOffsets)
static IEnumerable< int > SelectAllRows(this ITable table, int columnOffset)
IEnumerable< int > SelectLike(DataObject value)
Definition: ColumnIndex.cs:249
DataObject Add(DataObject other)
Adds the given value to this object value.
Definition: DataObject.cs:383
virtual IEnumerable< int > SelectAll()
Definition: ColumnIndex.cs:136
static IEnumerable< int > OrderRowsByColumns(this ITable table, int[] columns)
static ITable NaturalJoin(this ITable table, ITable otherTable)
static ITable ColumnMerge(this ITable table, ITable other)
This is a static class that performs the operations to do a pattern search on a given column of a tab...
Deveel.Data.Sql.Objects.SqlString SqlString
Definition: DataObject.cs:27
static ITable OrderBy(this ITable table, string columnName, bool ascending)
static bool Exists(this ITable table, int columnOffset1, DataObject value1, int columnOffset2, DataObject value2)
static DataObject GetSingleValue(this ITable table, int columnOffset)
static IEnumerable< int > SelectRows(this ITable table, int column, SqlExpressionType op, DataObject value)
ColumnIndex GetIndex(int columnOffset)
Gets an index for given column that can be used to select values from this table. ...
static IEnumerable< int > SelectRowsLessOrEqual(this ITable table, int columnOffset, ISqlObject value)
static ITable EmptySelect(this ITable table)
static ITable OrderBy(this ITable table, ObjectName columnName, bool ascending)
static IEnumerable< int > SelectRowsEqual(this ITable table, int columnIndex, DataObject value)
static IEnumerable< int > SelectAllRows(this ITable table)