Skip to content

Commit

Permalink
fix: Npsql invalid case error
Browse files Browse the repository at this point in the history
Added built-in support for the `ag_catalog.agtype` PostgreSQL type to Npgsql using a custom converter. This fixes #3 .
  • Loading branch information
Allison-E committed May 23, 2024
1 parent a6757e7 commit b90d2c7
Show file tree
Hide file tree
Showing 31 changed files with 1,184 additions and 616 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Here's a simple example to get you started:

```csharp
using ApacheAGE;
using ApacheAGE.Types;

var connectionString = "Host=server;Port=5432;Username=user;Password=pass;Database=sample1";

Expand All @@ -21,8 +22,8 @@ await using var client = clientBuilder.Build();
await client.OpenConnectionAsync();

// Create a graph and add vertices.
await client.CreateGraphAsync("graph1")
await client.ExecuteCypherAsync("graph1", "CREATE (:Person {age: 23}), (:Person {age:78})");
await client.CreateGraphAsync("graph1");
await client.ExecuteCypherAsync("graph1", "CREATE (:Person {age: 23}), (:Person {age: 78})");
await using var reader = await client.ExecuteQueryAsync(
@"SELECT * FROM cypher('graph1', $$
MATCH (n:Person)
Expand All @@ -32,9 +33,8 @@ $$) AS (persons agtype);");
// Read the result row by row.
while(await reader.ReadAsync())
{
var agtypeResult = reader.GetValue(0);
var agtypeResult = reader.GetValue<Agtype>(0);
Vertex person = agtypeResult.GetVertex();

// Do something with vertex.
Console.WriteLine(person);
}
```
9 changes: 5 additions & 4 deletions src/ApacheAGE/AGEClientEventId.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace ApacheAGE
{
public static class AgeClientEventId
internal static class AgeClientEventId
{
#region Connection
public const int CONNECTION_OPENED = 1000;
Expand All @@ -26,7 +26,9 @@ public static class AgeClientEventId

#region Commands
public const int GRAPH_CREATED = 3001;
public const int GRAPH_DROPPED = 3003;
public const int GRAPH_DROPPED = 3002;
public const int GRAPH_EXISTS = 3003;
public const int GRAPH_DOES_NOT_EXIST = 3004;

public const int CYPHER_EXECUTED = 3101;
public const int QUERY_EXECUTED = 3102;
Expand All @@ -37,7 +39,6 @@ public static class AgeClientEventId
public const int QUERY_EXECUTION_ERROR = 3903;
#endregion

#region Command
#endregion
public const int UNKNOWN_ERROR = 9900;
}
}
53 changes: 51 additions & 2 deletions src/ApacheAGE/AgeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ public class AgeClient: IAgeClient, IDisposable, IAsyncDisposable

internal AgeClient(string connectionString, AgeConfiguration configuration)
{
_dataSource = new NpgsqlDataSourceBuilder(connectionString)
.Build();
var builder = new NpgsqlDataSourceBuilder(connectionString);

builder.UseAge();

_dataSource = builder.Build();
_configuration = configuration;
}

Expand Down Expand Up @@ -73,6 +76,9 @@ public async Task CreateGraphAsync(
{
CheckForExistingConnection();

if (await GraphExistsAsync(graphName, cancellationToken))
return;

await using var command = new NpgsqlCommand(
"SELECT * FROM create_graph($1);",
_connection)
Expand Down Expand Up @@ -257,6 +263,49 @@ public async Task<AgeDataReader> ExecuteQueryAsync(
}
}

public async Task<bool> GraphExistsAsync(
string graphName,
CancellationToken cancellationToken = default)
{
CheckForExistingConnection();

await using var command = new NpgsqlCommand(
"SELECT * FROM ag_catalog.ag_graph WHERE name = $1;",
_connection)
{
Parameters =
{
new() { Value = graphName },
}
};

try
{
object? result = await command.ExecuteScalarAsync(cancellationToken)
.ConfigureAwait(false);

if (result is null)
{
LogMessages.GraphExists(
_configuration!.Logger.CommandLogger,
graphName);

return false;
}

LogMessages.GraphDoesNotExist(
_configuration!.Logger.CommandLogger,
graphName);

return true;
}
catch (Exception e)
{
LogMessages.UnknownError(_configuration!.Logger.CommandLogger, e);
throw new AgeException($"An error occurred.", e);
}
}

#endregion

#region Dispose
Expand Down
34 changes: 13 additions & 21 deletions src/ApacheAGE/AgeDataReader.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Data;
using System.Threading.Tasks;
using ApacheAGE.Data;
using Npgsql;

namespace ApacheAGE
Expand All @@ -14,11 +12,6 @@ public class AgeDataReader: IAgeDataReader, IDisposable, IAsyncDisposable
private readonly NpgsqlDataReader _reader;
private bool _isDisposed = false;

public int FieldCount => _reader.FieldCount;
public bool IsOnRow => _reader.IsOnRow;
public bool HasRows => _reader.HasRows;
public bool IsClosed => _reader.IsClosed;

/// <summary>
/// Initialises a new instance of <see cref="AgeDataReader"/>.
/// </summary>
Expand All @@ -29,29 +22,28 @@ internal AgeDataReader(NpgsqlDataReader reader)
_reader = reader;
}

public int FieldCount => _reader.FieldCount;
public bool IsOnRow => _reader.IsOnRow;
public bool HasRows => _reader.HasRows;
public bool IsClosed => _reader.IsClosed;

public void Close() => _reader.Close();

public void CloseAsync() => _reader.CloseAsync();

public Task<bool> ReadAsync() => _reader.ReadAsync();
public bool Read() => _reader.Read();

public AgeRowSet GetValues()
{
object[] values = new object[FieldCount];
public Task<bool> ReadAsync() => _reader.ReadAsync();

_ = _reader.GetValues(values);
public int GetValues(object[] values) => _reader.GetValues(values);

return new(values);
}
public T? GetValue<T>(int ordinal) => _reader.GetFieldValueAsync<T?>(ordinal).GetAwaiter().GetResult();

public AgType GetValue(int ordinal)
public async Task<T?> GetValueAsync<T>(int ordinal)
{
var value = _reader.GetValue(ordinal);

if (value is DBNull)
value = null;

return new(value);
var value = await _reader.GetFieldValueAsync<T?>(ordinal)
.ConfigureAwait(false);
return value;
}

public string GetName(int ordinal) => _reader.GetName(ordinal);
Expand Down
3 changes: 2 additions & 1 deletion src/ApacheAGE/ApacheAGE.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<IncludeSymbols>False</IncludeSymbols>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>

</PropertyGroup>

Expand All @@ -33,7 +34,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Npgsql" Version="8.0.0" />
<PackageReference Include="Npgsql" Version="8.0.3" />
</ItemGroup>

</Project>
42 changes: 42 additions & 0 deletions src/ApacheAGE/Converters/AgtypeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Buffers;
using System.Text;
using ApacheAGE.Types;
using Npgsql.Internal;

namespace ApacheAGE.Converters
{
#pragma warning disable NPG9001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
internal class AgtypeConverter: PgBufferedConverter<Agtype>
{
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.None;
return format is DataFormat.Text;
}

/// <summary>
/// Read agtype from its binary representation.
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
protected override Agtype ReadCore(PgReader reader)
{
ReadOnlySequence<byte> textBytes = reader.ReadBytes(reader.CurrentRemaining);
string text = Encoding.UTF8.GetString(textBytes);

return new(text);
}

/// <summary>
/// Write agtype to its binary representation.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected override void WriteCore(PgWriter writer, Agtype value)
{
byte[] bytes = Encoding.UTF8.GetBytes(value.GetString());
writer.WriteBytes(bytes);
}
}
#pragma warning restore NPG9001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
}
Loading

0 comments on commit b90d2c7

Please sign in to comment.