Add project files.

This commit is contained in:
Andy Howard
2021-05-05 15:36:28 +01:00
parent 949836d24c
commit a4cf706eb3
353 changed files with 60029 additions and 0 deletions
+751
View File
@@ -0,0 +1,751 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
// Definitions.
public enum DbJoinType
{
Inner,
LeftOuter,
RightOuter,
FullOuter,
Cross,
}
public enum DbOrderByNullMode
{
Default,
DemoteNulls,
PromoteNulls,
DateDemoteNulls,
DatePromoteNulls
}
public enum DbDateDiffInterval
{
Year,
Quarter,
Month,
DayOfYear,
Day,
Weekday,
Week,
Hour,
Minute,
Second,
}
//=======================================================================
// Global DBMS.
// When dealing with databases using different DBMSs, the methods below that
// depend on the DBMS to correctly format query clauses need to ensure that
// they are referencing the correct DBMS type.
// This global value is designed to be populated whenever a Query is created
// with a specific DBMS specified so that it can be made available to other
// methods that depend on the value (especially static ones).
public class DbGlobalDbms
{
private DbGlobalDbms () { }
private static DbmsType dbms = DbmsType.General;
public static DbmsType Type
{
get { return dbms; }
set { if (value != DbmsType.General) dbms = value; }
}
}
//=======================================================================
public class DbQueryJoin
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public DbQueryJoin (DbJoinType type = DbJoinType.Inner)
{
this.Type = type;
}
//-------------------------------------------------------------------
// Properties.
public DbJoinType Type { get; private set; } = DbJoinType.Inner;
public String JoinType { get { return GetJoinType(Type); } }
public StringPair Table { get; set; } = null; /* Second value is the optional alias. */
public List<String> Filters { get; } = new List<String>();
public List<DbQueryJoin> SubJoins { get; } = new List<DbQueryJoin>();
//-------------------------------------------------------------------
public static String GetJoinType (DbJoinType type)
{
switch (type)
{
case DbJoinType.LeftOuter: return (DbGlobalDbms.Type == DbmsType.Sage ? "LEFT JOIN" : "LEFT OUTER JOIN");
case DbJoinType.RightOuter: return (DbGlobalDbms.Type == DbmsType.Sage ? "RIGHT JOIN" : "RIGHT OUTER JOIN");
case DbJoinType.FullOuter: return (DbGlobalDbms.Type == DbmsType.Sage ? "FULL JOIN" : "FULL OUTER JOIN");
case DbJoinType.Cross: return "CROSS JOIN";
case DbJoinType.Inner:
default: return "INNER JOIN";
}
}
//-------------------------------------------------------------------
public String BuildJoin (String enclosedJoin)
{
//---------------------------------------------------------------
// Select syntax.
// SELECT [ DISTINCT | ALL ]
// column_expression1, column_expression2, ....
// [ FROM from_clause ]
// [ WHERE where_expression ]
// [ GROUP BY expression1, expression2, .... ]
// [ HAVING having_expression ]
// [ ORDER BY order_column_expr1, order_column_expr2, .... ]
// column_expression ::= expression [ AS ] [ column_alias ]
// from_clause ::= select_table1, select_table2, ...
// from_clause ::= select_table1 LEFT [OUTER] JOIN select_table2 ON expr ...
// from_clause ::= select_table1 RIGHT [OUTER] JOIN select_table2 ON expr ...
// from_clause ::= select_table1 [INNER] JOIN select_table2 ...
// select_table ::= table_name [ AS ] [ table_alias ]
// select_table ::= ( sub_select_statement ) [ AS ] [ table_alias ]
// order_column_expr ::= expression [ ASC | DESC ]
// Issues.
// MS Access - complex join expressions must be bracketed: ON (expr ...)
//---------------------------------------------------------------
String joinData = String.Empty;
//---------------------------------------------------------------
// Integrate multiple joins (nested).
// <join1-Type>
// (<join1-Table>
// <join2-Type>
// (<join2-Table> <join3-Type> <join3-Table> ON <join3-Filter>)
// ON <join2-Filter>)
// ON <join1-Filter>
//---------------------------------------------------------------
// Sage doesn't use 'AS' to alias tables.
String tableAliasOperator = (DbGlobalDbms.Type == DbmsType.Sage ? " " : " AS ");
String joinType = GetJoinType(Type);
String joinTable = Table.Concat (tableAliasOperator, false);
String subJoinData = String.Empty;
foreach (var subJoin in SubJoins)
{
// Add another bracket in front of the table:
joinTable = "(" + joinTable;
// Add the sub join.
subJoinData += subJoin.BuildJoin("") + ")";
}
// Build this join's list of filter conditions.
// Take care not to add too many embedded brackets - confuses Access etc.
// [Original version: lFilter.Concat (" AND ", "(", ")", true, true)].
String filter = String.Empty;
foreach (var rawFilterClause in Filters)
{
// Retrieve the next filter clause.
String filterClause = rawFilterClause.Trim();
if (filterClause.IsEmpty()) continue;
// Embed the clause in brackets if it contains at least one 'OR' operator.
if (filterClause.IndexOf(" OR ", StringComparison.OrdinalIgnoreCase) >= 0)
filterClause = "(" + filterClause + ")";
filter += (!filter.IsEmpty() ? " AND " : "") + filterClause;
}
// Surround the entire filter in brackets (if not already done).
if (filter.FindMatchingBrace(0) != filter.Length)
filter = "(" + filter + ")";
// Build the join.
if (enclosedJoin.IsEmpty() && subJoinData.IsEmpty())
{
//-----------------------------------------------------------
// Simple join syntax.
// <Join-Type> <Join-Table> ON <Join-Filter>
joinData = joinType + " " + joinTable + " ON " + filter;
//-----------------------------------------------------------
}
else
{
//-----------------------------------------------------------
// More complex join syntax - nested joins.
// <Join-Type> (<Join-Table> <NestedJoin>) ON <Join-Filter>
//-----------------------------------------------------------
// Bracket the table, enclosing any enclosed or subordinate joins.
joinData = joinType + " (" + joinTable;
if (!subJoinData.IsEmpty())
joinData += " " + subJoinData;
if (!enclosedJoin.IsEmpty())
joinData += " " + enclosedJoin;
joinData += ") ON " + filter;
//-----------------------------------------------------------
}
return joinData;
}
//-------------------------------------------------------------------
// Debugging.
public void Dump ()
{
SaLogger.Debug(GetType().Name + ".Dump");
SaLogger.Debug("\tType = " + GetJoinType(Type));
SaLogger.Debug("\tTable = " + Table.Concat(" AS "));
SaLogger.Debug("\tFilters = " + Filters.Concat("", "\t\t", "\n"));
if (SubJoins.Count > 0)
{
}
SaLogger.Debug("\tJoin = " + BuildJoin(""));
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class DbQueryJoinList
{
//-------------------------------------------------------------------
// List extensions.
public static String BuildJoins (this List<DbQueryJoin> joins, String primaryTable)
{
String joinData = primaryTable;
if (joins.Count > 0)
{
//-----------------------------------------------------------
// Integrate multiple joins (nested).
// <join1-Type>
// (<join1-Table>
// <join2-Type>
// (<join2-Table> <join3-Type> <join3-Table> ON <join3-Filter>)
// ON <join2-Filter>)
// ON <join1-Filter>
//-----------------------------------------------------------
bool bBracketFromFirstToLast = true;
if (bBracketFromFirstToLast)
{
// Bracketing from the first through to the last causes the
// Main table name to be included in the brackets.
// This appears to work for complex joins (multiple tables)
// but may not be 100% compliant with the SQL specification.
int joinCount = 0;
foreach (var join in joins)
{
// Add the next join in the list.
if (++joinCount > 1 && !joinData.IsEmpty()) joinData = "(" + joinData + ")";
joinData = joinData + " " + join.BuildJoin("");
joinData.Trim();
}
}
else
{
// Jul-04 Attempt to organise joins with innermost bracketed.
// Start from the end of the list and work backwards.
// NB Fails with multiple joins - check SQL specification!
// Start with the last join and roll up towards the first.
joinData = String.Empty;
foreach (var join in joins)
{
// Add the next join in the list.
joinData = join.BuildJoin(joinData);
joinData.Trim();
}
joinData = primaryTable + " " + joinData;
}
}
return joinData;
}
//-------------------------------------------------------------------
}
//=======================================================================
public class DbQuery
{
//-------------------------------------------------------------------
// Results paging.
public class Paging
{
public uint PageNo { get; set; } = 0;
public ushort PageLength { get; set; } = 0;
public virtual bool Paged { get { return PageNo > 0 && PageLength > 0; } }
}
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public DbQuery (DbmsType dbms)
{
DbGlobalDbms.Type = dbms;
}
//-------------------------------------------------------------------
// Properties.
public bool DistinctRows { get; set; } = false;
public uint RowLimit { get; set; } = 0;
public List<StringPair> Tables { get; } = new List<StringPair>();
public List<StringPair> Columns { get; } = new List<StringPair>(); /* s1 = qualified column name, s2 = optional alias. */
public List<String> Filters { get; } = new List<string>();
public List<String> GroupBy { get; } = new List<string>();
public List<String> OrderBy { get; } = new List<string>();
public List<DbQueryJoin> Joins { get; } = new List<DbQueryJoin>();
/// <summary>Date value used as alternative to Null on database (ref. C++ ODBC defaults).</summary>
public static DateTime NullDate { get; } = new DateTime(1970, 1, 1);
//-------------------------------------------------------------------
// S t r i n g V a l u e F o r m a t t i n g
//-------------------------------------------------------------------
/// <summary>
/// Format the value so that it can be used in a conditional clause.
/// <para>- single quotes are duplicated.</para>
/// </summary>
public static String FormatForCondition (String value)
{
return StringUtil.EmptyOnNull(value).Replace("'", "''");
}
/// <summary>
/// Format the specified string value so that it can be used in a 'LIKE' clause.
/// <para>- single quotes are duplicated</para>
/// <para>- open square bracket ('[') is enclosed in square brackets ('[[]')</para>
/// <para>- percent ('%') is enclosed in square brackets ('[%]') unless wildcards allowed.</para>
/// <para>- asterisk ('*') converted to percent ('%') if wildcards allowed.</para>
/// </summary>
public static String FormatForLike (String value, bool allowWildcards = false)
{
value = StringUtil.EmptyOnNull(value).Replace("'", "''").Replace("[", "[[]");
if (allowWildcards)
{
value = value.Replace("*", "%");
}
else
{
value = value.Replace("%", "[%]");
}
return value;
}
/// <summary>
/// Format the list of values so that they it can be used in an 'In' clause.
/// <para>- Ignore blanks (if instructed), remove duplicates &amp; pass string values through 'FormatForCondition'.</para>
/// <para>- Format as: ('Value1','Value2','Value3') if string data, else (Value1,Value2,Value3) etc.</para>
/// <para>- Returns empty string if list is empty.</para>
/// </summary>
public static String FormatForIn (List<String> rawValues, bool stringData = true, bool ignoreBlanks = true)
{
String result = String.Empty;
List<String> values = new List<String>();
foreach (var rawValue in rawValues)
{
String value = rawValue;
if (!rawValue.IsEmpty() || !ignoreBlanks)
{
if (stringData)
value = "'" + FormatForCondition(value) + "'";
else value.Trim(); // Non string data can be cleansed.
values.Add(value);
}
}
values.RemoveDuplicatesCI();
if (values.Count > 0)
result = "(" + values.Concat(",") + ")";
return result;
}
//-------------------------------------------------------------------
// D a t e V a l u e F o r m a t t i n g
//-------------------------------------------------------------------
public static String GetDateDelim ()
{
String delim = "#"; // MS Access only?
if (DbGlobalDbms.Type == DbmsType.MsSql)
delim = "'"; // MS SQL Server.
return delim;
}
public static String FormatDateForCondition (DateTime date, bool delimit = true, bool includeMidnight = false)
{
String value = DateUtil.Format(date, "dd-MMM-yyyy");
if (includeMidnight)
value += " 00:00:00";
if (delimit)
{
String delim = GetDateDelim();
value = delim + value + delim;
}
return value;
}
public static String FormatDateTimeForCondition (DateTime dateTime, bool delimit = true, bool precise = false)
{
String value = DateUtil.Format(dateTime, "yyyy-MM-dd HH:mm:ss"+(precise?".fff":""));
if (delimit)
{
String delim = GetDateDelim();
value = delim + value + delim;
}
return value;
}
public static String FormatTimeForCondition (DateTime time, bool delimit = true, bool zeroSeconds = false)
{
String value = String.Empty;
if (zeroSeconds)
value = DateUtil.Format(time, "HH:mm:00");
else
value = DateUtil.Format(time, "HH:mm:ss");
if (delimit)
{
String delim = GetDateDelim();
value = delim + value + delim;
}
return value;
}
//-------------------------------------------------------------------
// F o r m a t F o r F i l t e r
//-------------------------------------------------------------------
public static String FormatNullValueFilter (String expression, bool excludeNulls)
{
String clause = String.Empty;
expression = StringUtil.EmptyOnNull(expression).Trim();
if (!expression.IsEmpty())
{
// Surround the expression in brackets if not present.
if (expression.Left(1) != "(")
expression = "(" + expression + ")";
// Logic to include or exclude null values.
if (excludeNulls)
{
// Explicitly exclude Null values.
//if (DbGlobalDbms.Type == DbmsType.MsSql)
// sClause = "(CASE WHEN "+sExpr+" IS NULL THEN 0 ELSE 1 END) = 1";
//else
clause = expression + " IS NOT NULL";
}
else
{
// Explicitly include only Null values.
//if (DbGlobalDbms.Type == DbmsType.MsSql)
// sClause = "(CASE WHEN "+sExpr+" IS NULL THEN 0 ELSE 1 END) = 0";
//else
clause = expression + " IS NULL";
}
}
return clause;
}
// Construct 'Order By' clauses, allowing direction (ASC/DESC) and whether
// to demote NULL values (push to end of sequence) into account.
public static List<String> FormatForOrderBy (String expression, bool ascending, DbOrderByNullMode nullMode = DbOrderByNullMode.Default, bool treatZeroAsNull = false)
{
List<String> orderBy = new List<string>();
expression = StringUtil.EmptyOnNull(expression).Trim();
if (!expression.IsEmpty())
{
// Surround the expression in brackets if not present.
if (expression.Left(1) != "(")
expression = "(" + expression + ")";
// Logic to promote null values to start of sequence or demote null values to the end of the sequence.
String nullsClause = String.Empty;
if (nullMode == DbOrderByNullMode.PromoteNulls || nullMode == DbOrderByNullMode.DatePromoteNulls)
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// Treat NULLs before other values.
List<String> nullsFilters = new List<String>();
nullsFilters.Add(expression + " IS NULL");
if (treatZeroAsNull)
nullsFilters.Add(expression + "=0");
if (nullMode == DbOrderByNullMode.DatePromoteNulls)
nullsFilters.Add(expression + "<=" + FormatDateForCondition(NullDate));
nullsClause = "(CASE WHEN " + nullsFilters.Concat(" OR ", "(", ")") + " THEN 0 ELSE 1 END)";
}
else
nullsClause = expression + " IS NULL";
}
else if (nullMode == DbOrderByNullMode.DemoteNulls || nullMode == DbOrderByNullMode.DateDemoteNulls)
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// Treat NULLs after other values.
List<String> nullsFilters = new List<string>();
nullsFilters.Add(expression + " IS NULL");
if (treatZeroAsNull)
nullsFilters.Add(expression + "=0");
if (nullMode == DbOrderByNullMode.DateDemoteNulls)
nullsFilters.Add(expression + "<=" + FormatDateForCondition(NullDate));
nullsClause = "(CASE WHEN " + nullsFilters.Concat(" OR ", "(", ")") + " THEN 1 ELSE 0 END)";
}
else
nullsClause = expression + " IS NOT NULL";
}
if (!nullsClause.IsEmpty()) orderBy.Add(nullsClause);
// Main clause for this expression.
orderBy.Add(expression + " " + (ascending ? "ASC" : "DESC"));
}
return orderBy;
}
//-------------------------------------------------------------------
// S Q L F u n c t i o n F o r m a t t i n g
//-------------------------------------------------------------------
// NB Many functions depend on the DBMS in use.
// Default versions are for Access/SQL.
public static String FormatFn_Len (String column)
{
if (String.IsNullOrEmpty(column)) return "''";
// MS Access.
return "Len(" + column + ")";
}
public static String FormatFn_Left (String column, int length)
{
if (String.IsNullOrEmpty(column) || length < 1) return "''";
// MS Access.
return "Left(" + column + "," + length.ToString() + ")";
}
public static String FormatFn_Right (String column, int length)
{
if (String.IsNullOrEmpty(column) || length < 1) return "''";
// MS Access.
return "Right(" + column + "," + length.ToString() + ")";
}
public static String FormatFn_TimeValue (String expression)
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// Convert with Date/Time values.
// Param 3 - Date/Time format.
// 1 = mm/dd/yy
// 2 = yy.mm.dd
// 3 = dd/mm/yy
// 4 = dd.mm.yy
// 5 = dd-mm-yy
// 6 = dd mmm yy
// 7 = mmm dd, yy
// 8 = hh:mm:ss
// 9 = mmm dd yyyy hh:mm:ss:000AP
return "Convert(VarChar(10)," + expression + ",8)"; // CONVERT(varchar(10), DateClosed, 8)
}
return "TimeValue(" + expression + ")";
}
//-------------------------------------------------------------------
// N u m e r i c F u n c t i o n s
//-------------------------------------------------------------------
public static String FormatFn_Abs (String sExpr)
{
if (sExpr.IsEmpty()) return "''";
return "Abs(" + sExpr + ")";
}
public static String FormatFn_Round (String expression, int decimals = 0)
{
if (String.IsNullOrEmpty(expression) || decimals < 0) return "''";
if (DbGlobalDbms.Type == DbmsType.Access)
{
// MS Access.
// NB Uses the 'round-to-even' logic - 0.5 values go to the even whole value rather than always up.
// Solution to get it behave consistently - add a fraction to make it 0.5001.
return "Round(" + expression + ((long)(10 ^ -(decimals + 2))).ToString() + "," + decimals.ToString() + ")";
}
return "Round(" + expression + "," + decimals.ToString() + ")";
}
public static String FormatFn_Truncate (String expression, int decimals = 0)
{
if (String.IsNullOrEmpty(expression) || decimals < 0) return "''";
if (DbGlobalDbms.Type == DbmsType.MsSql)
return "Round(" + expression + "," + decimals.ToString() + ",1)";
if (DbGlobalDbms.Type == DbmsType.Access)
// MS Access.
if (decimals > 0)
{
String factor = ((long)(10^decimals)).ToString();
return "Int(" + expression + "*" + factor + "," + decimals.ToString() + ")/" + factor;
}
return "Int(" + expression + "," + decimals.ToString() + ")";
}
public static String FormatFn_FormatNumber (String expression, int decimals)
{
if (String.IsNullOrEmpty(expression) || decimals < 0) return "''";
// MS Access.
return "FormatNumber(" + expression + "," + decimals.ToString() + ")";
}
//-------------------------------------------------------------------
// D a t e D i f f e r e n c e
//-------------------------------------------------------------------
// Date difference: eg. DateDiff('n',Col1,Col2).
public static String FormatFn_DateDiff (DbDateDiffInterval dateInterval, String column1, String column2)
{
if (column1.IsEmpty() || column2.IsEmpty()) return "''";
String interval = String.Empty;
if (DbGlobalDbms.Type == DbmsType.MsSql)
{
// SQL Server.
switch (dateInterval)
{
// NB No delimiter.
case DbDateDiffInterval.Year: interval = "yyyy"; break;
case DbDateDiffInterval.Quarter: interval = "q"; break;
case DbDateDiffInterval.Month: interval = "m"; break;
case DbDateDiffInterval.DayOfYear: interval = "y"; break;
case DbDateDiffInterval.Day: interval = "d"; break;
//case Weekday: interval = "w"; break; // Not supported by SQL Server.
case DbDateDiffInterval.Week: interval = "ww"; break;
case DbDateDiffInterval.Hour: interval = "h"; break;
case DbDateDiffInterval.Minute: interval = "n"; break;
case DbDateDiffInterval.Second: interval = "s"; break;
//case MilliSecond: interval = "ms"; break; // Not yet defined.
}
}
else
{
// MS Access.
switch (dateInterval)
{
case DbDateDiffInterval.Year: interval = "'yyyy'"; break;
case DbDateDiffInterval.Quarter: interval = "'q'"; break;
case DbDateDiffInterval.Month: interval = "'m'"; break;
case DbDateDiffInterval.DayOfYear: interval = "'y'"; break;
case DbDateDiffInterval.Day: interval = "'d'"; break;
case DbDateDiffInterval.Weekday: interval = "'w'"; break;
case DbDateDiffInterval.Week: interval = "'ww'"; break;
case DbDateDiffInterval.Hour: interval = "'h'"; break;
case DbDateDiffInterval.Minute: interval = "'n'"; break;
case DbDateDiffInterval.Second: interval = "'s'"; break;
}
if (interval.IsEmpty()) return "''";
}
return "DateDiff(" + interval + "," + column1 + "," + column2 + ")";
}
//-------------------------------------------------------------------
// B i t M a p p e d V a l u e s
//-------------------------------------------------------------------
public static String BuildBitMappedValue (String column, ushort bit, bool match)
{
// Filter clause to test for specific bit set in field.
String clause = String.Empty;
if (!String.IsNullOrEmpty(column))
{
if (bit > 0 && bit <= 32)
{
// The operation depands on whether the value should be matched or not.
String condition = (match? "<>" : "=");
// Test is achieved by shifting the value so as to drop the target bit, shift back one bit
// and identify those value that then do not match the original value shifted by one place less.
// Example match: abcdefgh, Test bit 3: abcde0 <> abcdef
clause = "(" + FormatFn_Truncate(column + "/" + Math.Pow(2, bit).ToString()) + "*2)" + condition + FormatFn_Truncate(column + "/" + Math.Pow(2, bit - 1).ToString());
if (DbGlobalDbms.Type == DbmsType.MsSql)
clause = "(Case When " + clause + " Then 1 Else 0 End)";
}
#if DEBUG
else
{
SaLogger.Debug("BuildBitMappedFilter - Invalid bit (" + bit + ").");
}
#endif
}
#if DEBUG
else
{
SaLogger.Debug("BuildBitMappedFilter - NULL column name.");
}
#endif
return clause;
}
public static String BuildBitMappedFilter (String column, ushort bit, bool match)
{
String clause = BuildBitMappedValue(column, bit, match);
if (!clause.IsEmpty())
{
if (DbGlobalDbms.Type == DbmsType.MsSql)
clause = "(" + clause + " <> 0)";
}
return clause;
}
//-------------------------------------------------------------------
// A t t r i b u t e s
//-------------------------------------------------------------------
public void Clear ()
{
Tables.Clear();
Columns.Clear();
Filters.Clear();
GroupBy.Clear();
OrderBy.Clear();
Joins.Clear();
}
public bool IsValid ()
{
return (Tables.Count > 0);
}
//-------------------------------------------------------------------
// B u i l d Q u e r y
//-------------------------------------------------------------------
public String BuildQuery (Paging paging = null)
{
if (Tables.Count < 1) return "";
//---------------------------------------------------------------------
// Start with the column names.
String query = "SELECT ";
if (DistinctRows) query += "DISTINCT ";
if (RowLimit > 0) query += "TOP " + RowLimit.ToString() + " ";
if (Columns.Count < 1) query += "* ";
else query += Columns.Concat(" AS ", false).Concat(",");
//---------------------------------------------------------------------
// Add the tables and joins.
String tableAliasOperator = DbGlobalDbms.Type == DbmsType.Sage? " " : " AS ";
if (Tables.Count > 1 || Joins.Count < 1)
{
query += " FROM " + Tables.Concat(tableAliasOperator, false).Concat(",") + " " + Joins.BuildJoins("");
}
else if (Tables.Count == 1)
{
// Table name is bracketed with multiple joins...
query += " FROM " + Joins.BuildJoins(Tables.Concat(tableAliasOperator, false)[0]);
}
query.TrimEnd(' ');
//---------------------------------------------------------------------
// Filters.
String filter = Filters.Concat (" AND ", "(", ")", true, true);
// Jan2019 Not required: if (Filters.Count > 1) filter = "(" + filter + ")";
if (!filter.IsEmpty()) query += " WHERE " + filter;
// Groupings.
String groupByData = GroupBy.Concat(",", "", "", true, true);
if (!groupByData.IsEmpty()) query += " GROUP BY " + groupByData;
// Sort sequence.
String orderByData = OrderBy.Concat(",", "", "", true, true);
if (!orderByData.IsEmpty()) query += " ORDER BY " + orderByData;
// Paging.
if (paging != null && paging.Paged)
{
// Must have an 'Order By' clause to use 'Offset ... Fetch Next ...'
if (orderByData.IsEmpty())
throw new NotSupportedException("Query paging requires sort ofrder to be specified");
if (DbGlobalDbms.Type != DbmsType.MsSql)
throw new NotSupportedException("Query paging only supported for MS SQL");
query += " OFFSET " + ((paging.PageNo - 1) * paging.PageLength) + " ROWS FETCH NEXT " + paging.PageLength + " ROWS ONLY";
}
//---------------------------------------------------------------------
#if DEBUG
bool bDisplayDebugMsg = false;
if (bDisplayDebugMsg)
SaLogger.Debug("Query: " + query);
#endif
//---------------------------------------------------------------------
return query;
}
//-------------------------------------------------------------------
}
//=======================================================================
}