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

Commit 08e9176

Browse files
committed
Add support for remaining pgsql array types
1 parent 1843a02 commit 08e9176

File tree

4 files changed

+206
-57
lines changed

4 files changed

+206
-57
lines changed

src/ServiceStack.OrmLite.PostgreSQL.Tests/PostgreSqlJsonTests.cs

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,69 @@ public class PgsqlData
3535
[PrimaryKey]
3636
public Guid Id { get; set; }
3737

38-
[PgSqlIntArray]
3938
public int[] Ints { get; set; }
39+
public long[] Longs { get; set; }
40+
public float[] Floats { get; set; }
41+
public double[] Doubles { get; set; }
42+
public decimal[] Decimals { get; set; }
43+
public string[] Strings { get; set; }
44+
public DateTime[] DateTimes { get; set; }
45+
public DateTimeOffset[] DateTimeOffsets { get; set; }
46+
47+
public List<int> ListInts { get; set; }
48+
public List<long> ListLongs { get; set; }
49+
public List<float> ListFloats { get; set; }
50+
public List<double> ListDoubles { get; set; }
51+
public List<decimal> ListDecimals { get; set; }
52+
public List<string> ListStrings { get; set; }
53+
public List<DateTime> ListDateTimes { get; set; }
54+
public List<DateTimeOffset> ListDateTimeOffsets { get; set; }
55+
}
4056

57+
public class PgsqlDataAnnotated
58+
{
59+
[PrimaryKey]
60+
public Guid Id { get; set; }
61+
62+
[PgSqlIntArray]
63+
public int[] Ints { get; set; }
64+
[PgSqlBigIntArray]
65+
public long[] Longs { get; set; }
66+
[PgSqlFloatArray]
67+
public float[] Floats { get; set; }
68+
[PgSqlDoubleArray]
69+
public double[] Doubles { get; set; }
70+
[PgSqlDecimalArray]
71+
public decimal[] Decimals { get; set; }
4172
[PgSqlTextArray]
4273
public string[] Strings { get; set; }
4374

75+
// [PgSqlTimestamp]
76+
[CustomField("timestamp[]")]
77+
public DateTime[] DateTimes { get; set; }
78+
79+
// [PgSqlTimestampTz]
80+
[CustomField("timestamp with time zone[]")]
81+
public DateTimeOffset[] DateTimeOffsets { get; set; }
82+
4483
[PgSqlIntArray]
4584
public List<int> ListInts { get; set; }
46-
85+
[PgSqlBigIntArray]
86+
public List<long> ListLongs { get; set; }
87+
[PgSqlFloatArray]
88+
public List<float> ListFloats { get; set; }
89+
[PgSqlDoubleArray]
90+
public List<double> ListDoubles { get; set; }
91+
[PgSqlDecimalArray]
92+
public List<decimal> ListDecimals { get; set; }
4793
[PgSqlTextArray]
4894
public List<string> ListStrings { get; set; }
95+
// [PgSqlTimestamp]
96+
[CustomField("timestamp[]")]
97+
public List<DateTime> ListDateTimes { get; set; }
98+
// [PgSqlTimestampTz]
99+
[CustomField("timestamp with time zone[]")]
100+
public List<DateTimeOffset> ListDateTimeOffsets { get; set; }
49101
}
50102

51103
[TestFixture]
@@ -98,27 +150,114 @@ public void Can_save_complex_types_as_JSON()
98150
[Test]
99151
public void Does_save_PgSqlData()
100152
{
153+
OrmLiteUtils.PrintSql();
154+
101155
using (var db = OpenDbConnection())
102156
{
103157
db.DropAndCreateTable<PgsqlData>();
158+
long UnixEpoch = 621355968000000000L;
159+
var dateTimes = new DateTime[] {
160+
new DateTime(UnixEpoch, DateTimeKind.Utc),
161+
new DateTime(2001, 01, 01, 1, 1, 1, 1, DateTimeKind.Utc),
162+
};
163+
var dateTimeOffsets = dateTimes.Select(x => new DateTimeOffset(x, TimeSpan.Zero)).ToArray();
104164

105165
var data = new PgsqlData
106166
{
107167
Id = Guid.NewGuid(),
108168
Ints = new[] { 2, 4, 1 },
109-
ListInts = new[] { 2, 4, 1 }.ToList(),
169+
Longs = new long[] { 2, 4, 1 },
170+
Floats = new float[] { 2, 4, 1 },
171+
Doubles = new double[] { 2, 4, 1 },
110172
Strings = new[] { "test string 1", "test string 2" },
173+
Decimals = new decimal[] { 2, 4, 1 },
174+
DateTimes = dateTimes,
175+
DateTimeOffsets = dateTimeOffsets,
176+
177+
ListInts = new[] { 2, 4, 1 }.ToList(),
178+
ListLongs = new long[] { 2, 4, 1 }.ToList(),
179+
ListFloats = new float[] { 2, 4, 1 }.ToList(),
180+
ListDoubles = new double[] { 2, 4, 1 }.ToList(),
111181
ListStrings = new[] { "test string 1", "test string 2" }.ToList(),
182+
ListDecimals = new decimal[] { 2, 4, 1 }.ToList(),
183+
ListDateTimes = dateTimes.ToList(),
184+
ListDateTimeOffsets = dateTimeOffsets.ToList(),
112185
};
113186

114187
db.Save(data);
115188

116189
var row = db.Select<PgsqlData>()[0];
117190
Assert.That(row.Ints.EquivalentTo(data.Ints));
191+
Assert.That(row.Longs.EquivalentTo(data.Longs));
192+
Assert.That(row.Floats.EquivalentTo(data.Floats));
193+
Assert.That(row.Doubles.EquivalentTo(data.Doubles));
194+
Assert.That(row.Decimals.EquivalentTo(data.Decimals));
195+
Assert.That(row.ListInts.EquivalentTo(data.ListInts));
196+
Assert.That(row.ListLongs.EquivalentTo(data.ListLongs));
197+
Assert.That(row.ListFloats.EquivalentTo(data.ListFloats));
198+
Assert.That(row.ListDoubles.EquivalentTo(data.ListDoubles));
199+
Assert.That(row.ListDecimals.EquivalentTo(data.ListDecimals));
200+
Assert.That(row.Strings.EquivalentTo(data.Strings));
201+
Assert.That(row.ListStrings.EquivalentTo(data.ListStrings));
202+
}
203+
204+
OrmLiteUtils.UnPrintSql();
205+
}
206+
[Test]
207+
public void Does_save_PgSqlDataAnnotated()
208+
{
209+
OrmLiteUtils.PrintSql();
210+
211+
using (var db = OpenDbConnection())
212+
{
213+
db.DropAndCreateTable<PgsqlDataAnnotated>();
214+
long UnixEpoch = 621355968000000000L;
215+
var dateTimes = new DateTime[] {
216+
new DateTime(UnixEpoch, DateTimeKind.Utc),
217+
new DateTime(2001, 01, 01, 1, 1, 1, 1, DateTimeKind.Utc),
218+
};
219+
var dateTimeOffsets = dateTimes.Select(x => new DateTimeOffset(x, TimeSpan.Zero)).ToArray();
220+
221+
var data = new PgsqlDataAnnotated
222+
{
223+
Id = Guid.NewGuid(),
224+
Ints = new[] { 2, 4, 1 },
225+
Longs = new long[] { 2, 4, 1 },
226+
Floats = new float[] { 2, 4, 1 },
227+
Doubles = new double[] { 2, 4, 1 },
228+
Strings = new[] { "test string 1", "test string 2" },
229+
Decimals = new decimal[] { 2, 4, 1 },
230+
DateTimes = dateTimes,
231+
DateTimeOffsets = dateTimeOffsets,
232+
233+
ListInts = new[] { 2, 4, 1 }.ToList(),
234+
ListLongs = new long[] { 2, 4, 1 }.ToList(),
235+
ListFloats = new float[] { 2, 4, 1 }.ToList(),
236+
ListDoubles = new double[] { 2, 4, 1 }.ToList(),
237+
ListStrings = new[] { "test string 1", "test string 2" }.ToList(),
238+
ListDecimals = new decimal[] { 2, 4, 1 }.ToList(),
239+
ListDateTimes = dateTimes.ToList(),
240+
ListDateTimeOffsets = dateTimeOffsets.ToList(),
241+
};
242+
243+
db.Save(data);
244+
245+
var row = db.Select<PgsqlDataAnnotated>()[0];
246+
Assert.That(row.Ints.EquivalentTo(data.Ints));
247+
Assert.That(row.Longs.EquivalentTo(data.Longs));
248+
Assert.That(row.Floats.EquivalentTo(data.Floats));
249+
Assert.That(row.Doubles.EquivalentTo(data.Doubles));
250+
Assert.That(row.Decimals.EquivalentTo(data.Decimals));
118251
Assert.That(row.ListInts.EquivalentTo(data.ListInts));
252+
Assert.That(row.ListLongs.EquivalentTo(data.ListLongs));
253+
Assert.That(row.ListFloats.EquivalentTo(data.ListFloats));
254+
Assert.That(row.ListDoubles.EquivalentTo(data.ListDoubles));
255+
Assert.That(row.ListDecimals.EquivalentTo(data.ListDecimals));
119256
Assert.That(row.Strings.EquivalentTo(data.Strings));
120257
Assert.That(row.ListStrings.EquivalentTo(data.ListStrings));
121258
}
259+
260+
OrmLiteUtils.UnPrintSql();
122261
}
123262
}
124263
}

src/ServiceStack.OrmLite.PostgreSQL/Converters/PostgreSqlArrayConverters.cs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,37 +43,56 @@ public override object ToDbValue(Type fieldType, object value)
4343

4444
public override object FromDbValue(Type fieldType, object value)
4545
{
46-
var strVal = value as string;
47-
return strVal != null
46+
return value is string strVal
4847
? strVal.FromJson<string[]>()
4948
: value;
5049
}
5150
}
5251

53-
public class PostgreSqlIntArrayConverter : NativeValueOrmLiteConverter
52+
public abstract class PostgreSqlArrayConverterBase<T> : NativeValueOrmLiteConverter
5453
{
55-
public override string ColumnDefinition => "integer[]";
56-
5754
public override DbType DbType => DbType.Object;
5855

5956
public override string ToQuotedString(Type fieldType, object value)
6057
{
61-
var integerArray = (int[])value;
58+
var integerArray = (T[])value;
6259
return this.ToArray(integerArray);
6360
}
6461
}
6562

66-
public class PostgreSqlLongArrayConverter : NativeValueOrmLiteConverter
63+
public class PostgreSqlIntArrayConverter : PostgreSqlArrayConverterBase<int>
64+
{
65+
public override string ColumnDefinition => "integer[]";
66+
}
67+
68+
public class PostgreSqlLongArrayConverter : PostgreSqlArrayConverterBase<long>
6769
{
6870
public override string ColumnDefinition => "bigint[]";
71+
}
6972

70-
public override DbType DbType => DbType.Object;
73+
public class PostgreSqlFloatArrayConverter : PostgreSqlArrayConverterBase<float>
74+
{
75+
public override string ColumnDefinition => "real[]";
76+
}
7177

72-
public override string ToQuotedString(Type fieldType, object value)
73-
{
74-
var longArray = (long[])value;
75-
return this.ToArray(longArray);
76-
}
78+
public class PostgreSqlDoubleArrayConverter : PostgreSqlArrayConverterBase<double>
79+
{
80+
public override string ColumnDefinition => "double precision[]";
81+
}
82+
83+
public class PostgreSqlDecimalArrayConverter : PostgreSqlArrayConverterBase<decimal>
84+
{
85+
public override string ColumnDefinition => "numeric[]";
86+
}
87+
88+
public class PostgreSqlDateTimeTimeStampArrayConverter : PostgreSqlArrayConverterBase<DateTime>
89+
{
90+
public override string ColumnDefinition => "timestamp[]";
91+
}
92+
93+
public class PostgreSqlDateTimeOffsetTimeStampTzArrayConverter : PostgreSqlArrayConverterBase<DateTimeOffset>
94+
{
95+
public override string ColumnDefinition => "timestamp with time zone[]";
7796
}
7897

7998

src/ServiceStack.OrmLite.PostgreSQL/Converters/PostgreSqlDateTimeConverter.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ namespace ServiceStack.OrmLite.PostgreSQL.Converters
44
{
55
public class PostgreSqlDateTimeConverter : DateTimeConverter
66
{
7-
public override string ColumnDefinition
8-
{
9-
get { return "timestamp"; }
10-
}
7+
public override string ColumnDefinition => "timestamp";
118
}
129
}

src/ServiceStack.OrmLite.PostgreSQL/PostgreSQLDialectProvider.cs

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Data;
45
using System.Linq;
@@ -61,6 +62,11 @@ public PostgreSqlDialectProvider()
6162
RegisterConverter<string[]>(new PostgreSqlStringArrayConverter());
6263
RegisterConverter<int[]>(new PostgreSqlIntArrayConverter());
6364
RegisterConverter<long[]>(new PostgreSqlLongArrayConverter());
65+
RegisterConverter<float[]>(new PostgreSqlFloatArrayConverter());
66+
RegisterConverter<double[]>(new PostgreSqlDoubleArrayConverter());
67+
RegisterConverter<decimal[]>(new PostgreSqlDecimalArrayConverter());
68+
RegisterConverter<DateTime[]>(new PostgreSqlDateTimeTimeStampArrayConverter());
69+
RegisterConverter<DateTimeOffset[]>(new PostgreSqlDateTimeOffsetTimeStampTzArrayConverter());
6470

6571
this.Variables = new Dictionary<string, string>
6672
{
@@ -511,56 +517,44 @@ public override string GetLastInsertIdSqlSuffix<T>()
511517
return "; " + SelectIdentitySql;
512518
}
513519

520+
public static Dictionary<string, NpgsqlDbType> NativeTypes = new Dictionary<string, NpgsqlDbType> {
521+
{ "json", NpgsqlDbType.Json },
522+
{ "jsonb", NpgsqlDbType.Jsonb },
523+
{ "hstore", NpgsqlDbType.Hstore },
524+
{ "text[]", NpgsqlDbType.Array | NpgsqlDbType.Text },
525+
{ "integer[]", NpgsqlDbType.Array | NpgsqlDbType.Integer },
526+
{ "bigint[]", NpgsqlDbType.Array | NpgsqlDbType.Bigint },
527+
{ "real[]", NpgsqlDbType.Array | NpgsqlDbType.Real },
528+
{ "double precision[]", NpgsqlDbType.Array | NpgsqlDbType.Double },
529+
{ "numeric[]", NpgsqlDbType.Array | NpgsqlDbType.Numeric },
530+
{ "timestamp[]", NpgsqlDbType.Array | NpgsqlDbType.Timestamp },
531+
{ "timestamp with time zone[]", NpgsqlDbType.Array | NpgsqlDbType.TimestampTz },
532+
};
533+
514534
public override void SetParameter(FieldDefinition fieldDef, IDbDataParameter p)
515535
{
516-
if (fieldDef.CustomFieldDefinition == "json")
536+
if (fieldDef.CustomFieldDefinition != null &&
537+
NativeTypes.TryGetValue(fieldDef.CustomFieldDefinition, out var npgsqlDbType))
517538
{
518539
p.ParameterName = this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName));
519-
((NpgsqlParameter) p).NpgsqlDbType = NpgsqlDbType.Json;
520-
return;
540+
((NpgsqlParameter) p).NpgsqlDbType = npgsqlDbType;
521541
}
522-
if (fieldDef.CustomFieldDefinition == "jsonb")
523-
{
524-
p.ParameterName = this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName));
525-
((NpgsqlParameter)p).NpgsqlDbType = NpgsqlDbType.Jsonb;
526-
return;
527-
}
528-
if (fieldDef.CustomFieldDefinition == "hstore")
529-
{
530-
p.ParameterName = this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName));
531-
((NpgsqlParameter)p).NpgsqlDbType = NpgsqlDbType.Hstore;
532-
return;
533-
}
534-
if (fieldDef.CustomFieldDefinition == "text[]")
535-
{
536-
p.ParameterName = this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName));
537-
((NpgsqlParameter)p).NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Text;
538-
return;
539-
}
540-
if (fieldDef.CustomFieldDefinition == "integer[]")
541-
{
542-
p.ParameterName = this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName));
543-
((NpgsqlParameter) p).NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Integer;
544-
return;
545-
}
546-
if (fieldDef.CustomFieldDefinition == "bigint[]")
542+
else
547543
{
548-
p.ParameterName = this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName));
549-
((NpgsqlParameter) p).NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Bigint;
550-
return;
544+
base.SetParameter(fieldDef, p);
551545
}
552-
base.SetParameter(fieldDef, p);
553546
}
554547

548+
public virtual bool UseRawValue(string columnDef) => columnDef?.EndsWith("[]") == true;
549+
555550
protected override object GetValue<T>(FieldDefinition fieldDef, object obj)
556551
{
557-
switch (fieldDef.CustomFieldDefinition)
552+
if (fieldDef.CustomFieldDefinition != null && NativeTypes.ContainsKey(fieldDef.CustomFieldDefinition)
553+
&& UseRawValue(fieldDef.CustomFieldDefinition))
558554
{
559-
case "text[]":
560-
case "integer[]":
561-
case "bigint[]":
562-
return fieldDef.GetValue(obj);
555+
return fieldDef.GetValue(obj);
563556
}
557+
564558
return base.GetValue<T>(fieldDef, obj);
565559
}
566560

0 commit comments

Comments
 (0)