Skip to content

Commit 2056a87

Browse files
authored
Merge pull request #135 from peetw/feautre/mysql-8
Add spatial dialect for MySQL 8
2 parents 77cfb9e + a1b7163 commit 2056a87

39 files changed

+1101
-90
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ jobs:
4242
DB_INIT: docker run -d -e MYSQL_ROOT_PASSWORD=nhsp_test -p 13306:3306 -v ./Tests.NHibernate.Spatial.MySQL57/initdb:/docker-entrypoint-initdb.d mysql:5.7-debian
4343
TEST_PROJECT: Tests.NHibernate.Spatial.MySQL57
4444

45+
- DB: MySQL80 (MySQL 8.0)
46+
DB_INIT: docker run -d -e MYSQL_ROOT_PASSWORD=nhsp_test -p 13307:3306 -v ./Tests.NHibernate.Spatial.MySQL80/initdb:/docker-entrypoint-initdb.d mysql:8.0
47+
TEST_PROJECT: Tests.NHibernate.Spatial.MySQL80
48+
49+
- DB: MySQL80 (MySQL 8.3)
50+
DB_INIT: docker run -d -e MYSQL_ROOT_PASSWORD=nhsp_test -p 13307:3306 -v ./Tests.NHibernate.Spatial.MySQL80/initdb:/docker-entrypoint-initdb.d mysql:8.3
51+
TEST_PROJECT: Tests.NHibernate.Spatial.MySQL80
52+
4553
- DB: PostGis20 (PostgreSQL 12 PostGIS 2.5)
4654
DB_INIT: docker run -d -e POSTGRES_PASSWORD=nhsp_test -p 15432:5432 -v ./Tests.NHibernate.Spatial.PostGis20/initdb:/docker-entrypoint-initdb.d postgis/postgis:12-2.5
4755
TEST_PROJECT: Tests.NHibernate.Spatial.PostGis20

NHibernate.Spatial.MySQL/Dialect/MySQL57SpatialDialect.cs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace NHibernate.Spatial.Dialect
3333
public class MySQL57SpatialDialect : MySQL5Dialect, ISpatialDialect
3434
{
3535
protected const string DialectPrefix = SpatialDialect.IsoPrefix;
36-
protected static readonly IType geometryType = new CustomType(typeof(MySQL57GeometryType), null);
36+
private static readonly IType geometryType = new CustomType(typeof(MySQLGeometryType), null);
3737

3838
/// <summary>
3939
/// Initializes a new instance of the <see cref="MySQLDialect"/> class.
@@ -125,7 +125,7 @@ protected override void RegisterFunctions()
125125
RegisterSpatialFunction("GeometryType", NHibernateUtil.String);
126126

127127
RegisterSpatialFunction("Area", NHibernateUtil.Double);
128-
RegisterSpatialFunction("Length", "GLength", NHibernateUtil.Double);
128+
RegisterSpatialFunction("Length", NHibernateUtil.Double);
129129
RegisterSpatialFunction("X", NHibernateUtil.Double);
130130
RegisterSpatialFunction("Y", NHibernateUtil.Double);
131131

@@ -150,12 +150,12 @@ protected void RegisterSpatialFunction(string standardName, string dialectName,
150150

151151
protected void RegisterSpatialFunction(string name, IType returnedType, int allowedArgsCount)
152152
{
153-
RegisterSpatialFunction(name, name, returnedType, allowedArgsCount);
153+
RegisterSpatialFunction(name, SpatialDialect.IsoPrefix + name, returnedType, allowedArgsCount);
154154
}
155155

156156
protected void RegisterSpatialFunction(string name, IType returnedType)
157157
{
158-
RegisterSpatialFunction(name, name, returnedType);
158+
RegisterSpatialFunction(name, SpatialDialect.IsoPrefix + name, returnedType);
159159
}
160160

161161
protected void RegisterSpatialFunction(string name, int allowedArgsCount)
@@ -193,7 +193,7 @@ protected void RegisterSpatialFunction(SpatialAnalysis analysis)
193193
/// <returns></returns>
194194
public virtual IGeometryUserType CreateGeometryUserType()
195195
{
196-
return new MySQL57GeometryType();
196+
return new MySQLGeometryType();
197197
}
198198

199199
/// <summary>
@@ -208,15 +208,9 @@ public virtual IGeometryUserType CreateGeometryUserType()
208208
/// <param name="geometry">The geometry.</param>
209209
/// <param name="srid">The srid.</param>
210210
/// <returns></returns>
211-
public SqlString GetSpatialTransformString(object geometry, int srid)
211+
public virtual SqlString GetSpatialTransformString(object geometry, int srid)
212212
{
213-
return new SqlStringBuilder()
214-
.Add("Transform(")
215-
.AddObject(geometry)
216-
.Add(",")
217-
.Add(srid.ToString())
218-
.Add(")")
219-
.ToSqlString();
213+
throw new NotSupportedException("MySQL 5.7 does not support spatial transform");
220214
}
221215

222216
/// <summary>
@@ -491,7 +485,7 @@ public string GetSpatialCreateString(string schema)
491485
/// </summary>
492486
/// <param name="schema">The schema.</param>
493487
/// <returns></returns>
494-
private string QuoteSchema(string schema)
488+
protected string QuoteSchema(string schema)
495489
{
496490
if (string.IsNullOrEmpty(schema))
497491
{
@@ -511,7 +505,7 @@ private string QuoteSchema(string schema)
511505
/// <param name="dimension">The dimension.</param>
512506
/// <param name="isNullable">Whether or not the column is nullable</param>
513507
/// <returns></returns>
514-
public string GetSpatialCreateString(string schema, string table, string column, int srid, string subtype, int dimension, bool isNullable)
508+
public virtual string GetSpatialCreateString(string schema, string table, string column, int srid, string subtype, int dimension, bool isNullable)
515509
{
516510
var builder = new StringBuilder();
517511

@@ -568,7 +562,7 @@ public string GetSpatialDropString(string schema, string table, string column)
568562
/// <value>
569563
/// <c>true</c> if it supports spatial metadata; otherwise, <c>false</c>.
570564
/// </value>
571-
public bool SupportsSpatialMetadata(MetadataClass metadataClass)
565+
public virtual bool SupportsSpatialMetadata(MetadataClass metadataClass)
572566
{
573567
return false;
574568
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
using NHibernate.Spatial.Metadata;
2+
using NHibernate.SqlCommand;
3+
using System;
4+
using System.Data;
5+
using System.Text;
6+
7+
namespace NHibernate.Spatial.Dialect
8+
{
9+
public class MySQL80SpatialDialect : MySQL57SpatialDialect
10+
{
11+
public MySQL80SpatialDialect()
12+
{
13+
// See: https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Dialect/MySQL8Dialect.cs#L7C48-L7C73
14+
RegisterColumnType(DbType.Boolean, "BOOLEAN");
15+
}
16+
17+
/// <summary>
18+
/// Gets the spatial transform string.
19+
/// </summary>
20+
/// <param name="geometry">The geometry.</param>
21+
/// <param name="srid">The srid.</param>
22+
/// <returns></returns>
23+
public override SqlString GetSpatialTransformString(object geometry, int srid)
24+
{
25+
return new SqlStringBuilder()
26+
.Add(DialectPrefix)
27+
.Add("Transform(")
28+
.AddObject(geometry)
29+
.Add(",")
30+
.Add(srid.ToString())
31+
.Add(")")
32+
.ToSqlString();
33+
}
34+
35+
/// <summary>
36+
/// Gets the spatial validation string.
37+
/// </summary>
38+
/// <param name="geometry">The geometry.</param>
39+
/// <param name="validation">The validation.</param>
40+
/// <param name="criterion">if set to <c>true</c> [criterion].</param>
41+
/// <returns></returns>
42+
public override SqlString GetSpatialValidationString(object geometry, SpatialValidation validation, bool criterion)
43+
{
44+
return new SqlStringBuilder()
45+
.Add(DialectPrefix)
46+
.Add(validation.ToString())
47+
.Add("(")
48+
.AddObject(geometry)
49+
.Add(")")
50+
.ToSqlString();
51+
}
52+
53+
/// <summary>
54+
/// Gets the spatial aggregate string.
55+
/// </summary>
56+
/// <param name="geometry">The geometry.</param>
57+
/// <param name="aggregate">The aggregate.</param>
58+
/// <returns></returns>
59+
public override SqlString GetSpatialAggregateString(object geometry, SpatialAggregate aggregate)
60+
{
61+
switch (aggregate)
62+
{
63+
case SpatialAggregate.Collect:
64+
return new SqlStringBuilder()
65+
.Add(DialectPrefix)
66+
.Add(aggregate.ToString())
67+
.Add("(")
68+
.AddObject(geometry)
69+
.Add(")")
70+
.ToSqlString();
71+
72+
case SpatialAggregate.ConvexHull:
73+
case SpatialAggregate.Envelope:
74+
// MySQL only directly supports the ST_Collect spatial aggregate function, therefore
75+
// we mimic these spatial agg functions by grouping the geometries from each row into
76+
// a geometry collection and then performing the function on the geometry collection
77+
// See: https://forums.mysql.com/read.php?23,249284,249284#msg-249284
78+
var collectAggregate = GetSpatialAggregateString(geometry, SpatialAggregate.Collect);
79+
return new SqlStringBuilder()
80+
.Add(DialectPrefix)
81+
.Add(aggregate.ToString())
82+
.Add("(")
83+
.Add(collectAggregate)
84+
.Add(")")
85+
.ToSqlString();
86+
87+
case SpatialAggregate.Intersection:
88+
case SpatialAggregate.Union:
89+
throw new NotSupportedException($"MySQL does not support {aggregate} spatial aggregate function");
90+
91+
default:
92+
throw new ArgumentException("Invalid spatial aggregate argument");
93+
}
94+
}
95+
96+
/// <summary>
97+
/// Gets the spatial create string.
98+
/// </summary>
99+
/// <param name="schema">The schema.</param>
100+
/// <param name="table">The table.</param>
101+
/// <param name="column">The column.</param>
102+
/// <param name="srid">The srid.</param>
103+
/// <param name="subtype">The subtype.</param>
104+
/// <param name="dimension">The dimension.</param>
105+
/// <param name="isNullable">Whether or not the column is nullable</param>
106+
/// <returns></returns>
107+
public override string GetSpatialCreateString(string schema, string table, string column, int srid, string subtype, int dimension, bool isNullable)
108+
{
109+
var builder = new StringBuilder();
110+
111+
string quotedSchema = QuoteSchema(schema);
112+
string quoteForTableName = QuoteForTableName(table);
113+
string quoteForColumnName = QuoteForColumnName(column);
114+
115+
builder.AppendFormat("ALTER TABLE {0}{1} DROP COLUMN {2}"
116+
, quotedSchema
117+
, quoteForTableName
118+
, quoteForColumnName
119+
);
120+
121+
builder.Append(MultipleQueriesSeparator);
122+
123+
builder.AppendFormat("ALTER TABLE {0}{1} ADD {2} {3} {4} SRID {5}"
124+
, quotedSchema
125+
, quoteForTableName
126+
, quoteForColumnName
127+
, subtype
128+
, isNullable ? "NULL" : "NOT NULL"
129+
, srid
130+
);
131+
132+
builder.Append(MultipleQueriesSeparator);
133+
134+
return builder.ToString();
135+
}
136+
137+
/// <summary>
138+
/// Gets a value indicating whether it supports spatial metadata.
139+
/// </summary>
140+
/// <value>
141+
/// <c>true</c> if it supports spatial metadata; otherwise, <c>false</c>.
142+
/// </value>
143+
public override bool SupportsSpatialMetadata(MetadataClass metadataClass)
144+
{
145+
return true;
146+
}
147+
}
148+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
4+
5+
<class name="NHibernate.Spatial.Metadata.GeometryColumn, NHibernate.Spatial"
6+
schema="INFORMATION_SCHEMA"
7+
table="ST_GEOMETRY_COLUMNS"
8+
lazy="false"
9+
mutable="false">
10+
<composite-id>
11+
<key-property name="TableCatalog" column="TABLE_CATALOG" />
12+
<key-property name="TableSchema" column="TABLE_SCHEMA" />
13+
<key-property name="TableName" column="TABLE_NAME" />
14+
<key-property name="Name" column="COLUMN_NAME" />
15+
</composite-id>
16+
<property name="SRID" column="SRS_ID" />
17+
<property name="Subtype" column="GEOMETRY_TYPE_NAME" />
18+
</class>
19+
</hibernate-mapping>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
4+
5+
<class name="NHibernate.Spatial.Metadata.SpatialReferenceSystem, NHibernate.Spatial"
6+
schema="INFORMATION_SCHEMA"
7+
table="ST_SPATIAL_REFERENCE_SYSTEMS"
8+
lazy="false"
9+
mutable="false">
10+
<id name="SRID" column="SRS_ID" type="Int32">
11+
<generator class="assigned" />
12+
</id>
13+
<property name="AuthorityName" column="ORGANIZATION" type="String" />
14+
<property name="AuthoritySRID" column="ORGANIZATION_COORDSYS_ID" type="Int32" />
15+
<property name="WellKnownText" column="DEFINITION" type="String" />
16+
</class>
17+
</hibernate-mapping>

NHibernate.Spatial.MySQL/NHibernate.Spatial.MySQL.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1010
</PropertyGroup>
1111

12+
<ItemGroup>
13+
<EmbeddedResource Include="Metadata\GeometryColumn.MySQL80SpatialDialect.hbm.xml" />
14+
<EmbeddedResource Include="Metadata\SpatialReferenceSystem.MySQL80SpatialDialect.hbm.xml" />
15+
</ItemGroup>
16+
1217
<ItemGroup>
1318
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
1419
</ItemGroup>

NHibernate.Spatial.MySQL/Type/MySQL57GeometryAdapterType.cs renamed to NHibernate.Spatial.MySQL/Type/MySQLGeometryAdapterType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ namespace NHibernate.Spatial.Type
3030
/// This class maps MySQLDbType.Geometry to and from Geometry
3131
/// </summary>
3232
[Serializable]
33-
public class MySQL57GeometryAdapterType : ImmutableType
33+
public class MySQLGeometryAdapterType : ImmutableType
3434
{
35-
public MySQL57GeometryAdapterType()
35+
public MySQLGeometryAdapterType()
3636
: base(SqlTypeFactory.Byte) // Any arbitrary type can be passed as parameter
3737
{ }
3838

NHibernate.Spatial.MySQL/Type/MySQL57GeometryType.cs renamed to NHibernate.Spatial.MySQL/Type/MySQLGeometryType.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ namespace NHibernate.Spatial.Type
2626
/// <summary>
2727
/// MySQL geometry type (to be used in models) that supports the changes introduced in MySQL 5.7
2828
/// </summary>
29-
public class MySQL57GeometryType : GeometryTypeBase<MySqlGeometry?>
29+
public class MySQLGeometryType : GeometryTypeBase<MySqlGeometry?>
3030
{
31-
private static readonly NullableType MySQL57GeometryAdapterType = new MySQL57GeometryAdapterType();
31+
private static readonly NullableType MySQLGeometryAdapterType = new MySQLGeometryAdapterType();
3232

33-
public MySQL57GeometryType()
34-
: base(MySQL57GeometryAdapterType)
33+
public MySQLGeometryType()
34+
: base(MySQLGeometryAdapterType)
3535
{ }
3636

3737
protected override void SetDefaultSRID(Geometry geometry)

NHibernate.Spatial.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
2626
NHibernate.Spatial.props = NHibernate.Spatial.props
2727
EndProjectSection
2828
EndProject
29+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.NHibernate.Spatial.MySQL80", "Tests.NHibernate.Spatial.MySQL80\Tests.NHibernate.Spatial.MySQL80.csproj", "{D6643E3E-D004-41A7-B1AB-96D52018FE17}"
30+
EndProject
2931
Global
3032
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3133
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
6870
{12E29E47-4760-427E-8EF9-5D2F39B0E980}.Debug|Any CPU.Build.0 = Debug|Any CPU
6971
{12E29E47-4760-427E-8EF9-5D2F39B0E980}.Release|Any CPU.ActiveCfg = Release|Any CPU
7072
{12E29E47-4760-427E-8EF9-5D2F39B0E980}.Release|Any CPU.Build.0 = Release|Any CPU
73+
{D6643E3E-D004-41A7-B1AB-96D52018FE17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74+
{D6643E3E-D004-41A7-B1AB-96D52018FE17}.Debug|Any CPU.Build.0 = Debug|Any CPU
75+
{D6643E3E-D004-41A7-B1AB-96D52018FE17}.Release|Any CPU.ActiveCfg = Release|Any CPU
76+
{D6643E3E-D004-41A7-B1AB-96D52018FE17}.Release|Any CPU.Build.0 = Release|Any CPU
7177
EndGlobalSection
7278
GlobalSection(SolutionProperties) = preSolution
7379
HideSolutionNode = FALSE

README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@ NHibernate binaries.
1414

1515
## Supported Databases
1616

17-
| Package | Minimum Version | CI Tests |
18-
|----------------------------|-------------------------------------------|---------------------------------------------------|
19-
| NHibernate.Spatial.MsSql | SQL Server 2012 | SQL Server 2017, SQL Server 2019, SQL Server 2022 |
20-
| NHibernate.Spatial.MySQL | MySQL 5.7 | MySQL 5.7 |
21-
| NHibernate.Spatial.PostGis | PostgreSQL 12 w/ PostGIS 2.5 <sup>1</sup> | PostgreSQL 12 w/ PostGIS 2.5 |
22-
23-
<sup>1</sup> PostgreSQL 9.1 w/ PostGIS 2.0 or later will likely work, but are not explicitly
24-
supported here as they are EOL (see [here](https://trac.osgeo.org/postgis/wiki/UsersWikiPostgreSQLPostGIS#PostGISSupportMatrix)).
17+
| Package | Dialects | CI Tests |
18+
|----------------------------|--------------------------|---------------------------------------------------|
19+
| NHibernate.Spatial.MsSql | SQL Server 2012 | SQL Server 2017, SQL Server 2019, SQL Server 2022 |
20+
| NHibernate.Spatial.MySQL | MySQL 5.7, MySQL 8.0 | MySQL 5.7, MySQL 8.0, MySQL 8.3 |
21+
| NHibernate.Spatial.PostGis | PostGIS 2.0 | PostGIS 2.5 (PostgreSQL 12) |
2522

2623
## Getting Started
2724

0 commit comments

Comments
 (0)