terrajobst / nquery

NQuery is a relational query engine written in C#. It allows you to execute a SELECT query against .NET objects.
MIT License
72 stars 20 forks source link

Provide a way to add functions that can be called with NULL arguments #30

Open terrajobst opened 9 years ago

terrajobst commented 9 years ago

Ported from CodePlex

Provide a way to add functions that can be called with NULL arguments.

NULL AND TRUE --> NULL
NULL OR FALSE --> NULL
NULL AND FALSE --> FALSE
NULL OR TRUE --> TRUE
NULL * 2 --> NULL
COS(NULL) --> NULL
SUBSTRING(NULL, 1, 2) --> ?

The problem is that some methods cannot accept null values e.g. Cos(double value) but some can e.g. Substring(string str, int start, int length). I think the best design is to allow the the function to specify whether it wish to handle null values itself or not (the default). If not, the invocation node should not call the function but immediatley return null if any argument is null.

If the function wants to handle null values the QE has to check whether the given parameter allows null values. The only datatypes legal for null values are reference types and sql data types. The compiler should check this and insert COALESCE nodes to handle sql data types.

Example:

MyFunction(object a, string b, int c, SqlInt32 d)

MyFunction(a, b, c, d) should result in

MyFunction(a, b, c, COALESCE(c, SqlInt32.NullValue))

The InvocationExpression should handle this in GetValue() like this:

Type[] parameterTypes = _function.GetParameterTypes();
object[] argumentValues = new object[_arguments.Length];
for (int i = 0; i < argumentValues.Length; i++)
{
 argumentValues[i] = _arguments[i].GetValue(computer);
 // IS NULL means: either NULL or implements INullable or is 
 // DBNullValue
 if (argumentValues[i] is null) 
 {
  if (_function.HandlesNullValues && parameterTypes[i].IsReferenceType)
   parameterTypes[i] = null;
  else
   return null;
 }
}
return _function.Invoke(argumentValues);

Needless to say that the value array and the parameter type array should be cached so that not every call results in two array creations.