04. Nullable and Array
1. Nullable
By default, all types in PostgreSQL are nullable. Therefore, our
default handling is to map parameters to the nullable type (T?
)
in .NET, which is similar to the Maybe
type in Haskell.
If a PostgreSQL function has been defined as STRICT
or RETURN NULL ON NULL INPUT
, it will not be executed if any of its arguments
are null. In these situations, we represent the type as the
non-nullable T
type in .NET. If you want to use nullable types
for these types, you can set the AlwaysNullable
variable to true
in the dotnet_src/Engine.cs file
.
Please note that after making changes to the dotnet_src/Engine.cs file
, you will need to rebuild the Debian packages and reinstall
pldotnet.
The following sections contain examples of how the generated C# and F# function should look for nullable and non-nullable types.
1.1 C#
The PostgreSQL function below was previously defined in the Basic usage page, which explains how to create functions using pldotnet in the database.
CREATE OR REPLACE FUNCTION are_integers_equal(a integer, b integer) RETURNS boolean AS $$
return a == b;
$$ LANGUAGE plcsharp;
The C# functions below illustrate the generated code for nullable and non-nullable types, respectively.
public static bool? are_integers_equal(int? a, int? b)
{
return a == b;
}
public static bool? are_integers_equal(int a, int b)
{
return a == b;
}
zsh:1: command not found: fm
CREATE OR REPLACE FUNCTION are_integers_equal(a integer, b integer) RETURNS boolean AS $$
a.Equals(b)
$$ LANGUAGE plfsharp;
The following F# code examples demonstrate the generated code for nullable and non-nullable types, respectively.
static member are_integers_equal (a: Nullable<int>) (b: Nullable<int>) : Nullable<bool> =
a.Equals(b)
static member are_integers_equal (a: int) (b: int) : Nullable<bool> =
a.Equals(b)
It is important to note that the Nullable<T>
type is only used
for F# structures, not classes. As a result, there is no change for
class objects when using the nullable type on F#.
2. Array
All supported data types also support arrays of that type, whether
they are single-dimensional or multi-dimensional. Currently, this
is achieved using the Npgsql convention of mapping them to
Array<T>
,
but this type mapping is cumbersome compared to the slightly different
T[]
representation.
The following are two PostgreSQL functions written using plcsharp and plfsharp, respectively, that modify an one-dimensional array of integers. The function takes in an array of integers and a integer as input, and returns the modified array.
CREATE OR REPLACE FUNCTION modify_1d_array(a integer[], b int) RETURNS integer[] AS $$
a.SetValue(b, 0);
return a;
$$ LANGUAGE plcsharp;
CREATE OR REPLACE FUNCTION modify_1d_array(a integer[], b int) RETURNS integer[] AS $$
a.SetValue(b.Value, 0);
a
$$ LANGUAGE plfsharp;
Here is the generated code for both the C# and F# versions of the function:
public static Array? modify_1d_array(Array? a, int? b)
{
a.SetValue(b, 0);
return a;
}
static member modify_1d_array (a: Array) (b: Nullable<int>) : Array =
a.SetValue(b.Value, 0);
a
When using the Array
class, we do not need to worry about using
multidimensional arrays, as demonstrated in the following code.
However, this method has lower performance than using T[]
.
Therefore, we plan to use T[]
for array mapping in the future,
which will require the user to specify the dimensions of the array
when creating the function.
CREATE OR REPLACE FUNCTION modify_array(a integer[], b int) RETURNS integer[] AS $$
let dim = a.Rank
match dim with
| 1 ->
a.SetValue(b, 0) |> ignore
a
| 2 ->
a.SetValue(b, 0, 0) |> ignore
a
| 3 ->
a.SetValue(b, 0, 0, 0) |> ignore
a
| _ ->
Elog.Info("Returning the input array...") |> ignore
a
$$ LANGUAGE plfsharp;
Now, with the following command:
select modify_array(array[1,2,3,4,5], 10);
We got the following output:
modify_array
--------------
{10,2,3,4,5}
A second example:
select modify_array(array[[1,2,null],[3,4,5]], 10);
The output:
modify_array
-----------------------
{{10,2,NULL},{3,4,5}}