Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit c9b3bac

Browse files
committed
Add support for using Dictionary fields in Hstore data type
1 parent aa5d47e commit c9b3bac

File tree

5 files changed

+128
-1
lines changed

5 files changed

+128
-1
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using Npgsql;
5+
using NpgsqlTypes;
6+
using ServiceStack.OrmLite.Converters;
7+
8+
namespace ServiceStack.OrmLite.PostgreSQL.Converters
9+
{
10+
public class PostgreSqlHstoreConverter : ReferenceTypeConverter
11+
{
12+
public override string ColumnDefinition => "hstore";
13+
14+
public override DbType DbType => DbType.Object;
15+
16+
public override object FromDbValue(Type fieldType, object value)
17+
{
18+
return (IDictionary<string, string>)value;
19+
}
20+
21+
public override object ToDbValue(Type fieldType, object value)
22+
{
23+
return (IDictionary<string, string>)value;
24+
}
25+
26+
public override void InitDbParam(IDbDataParameter p, Type fieldType)
27+
{
28+
var sqlParam = (NpgsqlParameter)p;
29+
sqlParam.NpgsqlDbType = NpgsqlDbType.Hstore;
30+
base.InitDbParam(p, fieldType);
31+
}
32+
33+
public override string GetColumnDefinition(int? stringLength) => ColumnDefinition;
34+
}
35+
}

src/ServiceStack.OrmLite.PostgreSQL/PostgreSQLDialectProvider.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public PostgreSqlDialectProvider()
5757

5858
RegisterConverter<byte[]>(new PostrgreSqlByteArrayConverter());
5959

60-
//TODO provide support for pgsql native datastructures:
60+
//TODO provide support for pgsql native data structures:
6161
RegisterConverter<string[]>(new PostgreSqlStringArrayConverter());
6262
RegisterConverter<int[]>(new PostgreSqlIntArrayConverter());
6363
RegisterConverter<long[]>(new PostgreSqlLongArrayConverter());
@@ -72,6 +72,23 @@ public PostgreSqlDialectProvider()
7272
};
7373
}
7474

75+
public bool UseHstore
76+
{
77+
set
78+
{
79+
if (value)
80+
{
81+
RegisterConverter<IDictionary<string, string>>(new PostgreSqlHstoreConverter());
82+
RegisterConverter<Dictionary<string, string>>(new PostgreSqlHstoreConverter());
83+
}
84+
else
85+
{
86+
RemoveConverter<IDictionary<string, string>>();
87+
RemoveConverter<Dictionary<string, string>>();
88+
}
89+
}
90+
}
91+
7592
private bool normalize;
7693
public bool Normalize
7794
{

src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ public ValueTypeConverter ValueTypeConverter
229229
}
230230
}
231231

232+
public void RemoveConverter<T>()
233+
{
234+
if (Converters.TryRemove(typeof(T), out var converter))
235+
converter.DialectProvider = null;
236+
}
237+
232238
public void RegisterConverter<T>(IOrmLiteConverter converter)
233239
{
234240
if (converter == null)

src/ServiceStack.OrmLite/OrmLiteUtils.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,24 @@ public static string StripQuotes(this string quotedExpr)
829829

830830
public static void UnPrintSql() => OrmLiteConfig.BeforeExecFilter = null;
831831

832+
public static StringBuilder CaptureSql()
833+
{
834+
var sb = StringBuilderCache.Allocate();
835+
CaptureSql(sb);
836+
return sb;
837+
}
838+
839+
public static void CaptureSql(StringBuilder sb) =>
840+
OrmLiteConfig.BeforeExecFilter = cmd => sb.AppendLine(cmd.GetDebugString());
841+
842+
public static void UnCaptureSql() => OrmLiteConfig.BeforeExecFilter = null;
843+
844+
public static string UnCaptureSqlAndFree(StringBuilder sb)
845+
{
846+
OrmLiteConfig.BeforeExecFilter = null;
847+
return StringBuilderCache.ReturnAndFree(sb);
848+
}
849+
832850
public static ModelDefinition GetModelDefinition(Type modelType)
833851
{
834852
return modelType.GetModelDefinition();
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Collections.Generic;
2+
using NUnit.Framework;
3+
using ServiceStack.OrmLite.PostgreSQL;
4+
5+
namespace ServiceStack.OrmLite.Tests
6+
{
7+
public class PostgreSqlTypes
8+
{
9+
public int Id { get; set; }
10+
11+
//hstore
12+
public Dictionary<string,string> Dictionary { get; set; }
13+
public IDictionary<string,string> IDictionary { get; set; }
14+
}
15+
16+
public class PostgreSqlDataTypesTests : OrmLiteTestBase
17+
{
18+
public PostgreSqlDataTypesTests() : base(Dialect.PostgreSql) {}
19+
20+
[OneTimeSetUp] public void OneTimeSetup() => PostgreSqlDialectProvider.Instance.UseHstore = true;
21+
22+
[OneTimeTearDown] public void OneTimeTearDown() => PostgreSqlDialectProvider.Instance.UseHstore = false;
23+
24+
[Test]
25+
public void Does_save_string_dictionary_in_hstore_columns()
26+
{
27+
using (var db = OpenDbConnection())
28+
{
29+
var sb = OrmLiteUtils.CaptureSql();
30+
31+
db.DropAndCreateTable<PostgreSqlTypes>();
32+
33+
Assert.That(OrmLiteUtils.UnCaptureSqlAndFree(sb), Does.Contain("hstore"));
34+
35+
db.Insert(new PostgreSqlTypes
36+
{
37+
Id = 1,
38+
Dictionary = new Dictionary<string, string> { {"A", "1"} },
39+
IDictionary = new Dictionary<string, string> { {"B", "2"} },
40+
});
41+
42+
Assert.That(db.Single(db.From<PostgreSqlTypes>().Where("dictionary -> 'A' = '1'")).Id,
43+
Is.EqualTo(1));
44+
45+
var q = db.From<PostgreSqlTypes>();
46+
Assert.That(db.Single(q.Where($"{q.Column<PostgreSqlTypes>(x => x.IDictionary)} -> 'B' = '2'")).Id,
47+
Is.EqualTo(1));
48+
}
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)