-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add spatial types #7096
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 4.5.x
Are you sure you want to change the base?
Add spatial types #7096
Conversation
| self::assertSame( | ||
| [ | ||
| 'CREATE TABLE spatial_table (id INT NOT NULL, ' | ||
| . 'location geometry(geometry) NOT NULL, ' | ||
| . 'point_location geometry(point,4326) NOT NULL, ' | ||
| . 'polygon_area geometry(polygon) NOT NULL)', | ||
| ], | ||
| $this->platform->getCreateTableSQL($table), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please focus on functional tests. The kind of test that you're building here is pretty much worthless because it does not tell us if the SQL actually works when it hits a database.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback! I already had in mind to add functional tests, since they’re definitely the right way to validate the SQL.
Because these tests require PostgreSQL with the PostGIS extension, would you suggest:
- creating a dedicated GitHub workflow (e.g.
postgresql-postgis.yml) that uses thepostgis/postgisimage, or - replacing the current PostgreSQL workflow with that image so that all PostgreSQL jobs run with PostGIS enabled?
I’d be happy to go with whichever approach best fits the project’s CI strategy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make sure DBAL still works with Postgres databases that don't have these extensions installed. So it's probably best for now if we add an additional check for Postgres that uses the latest postgis image and leave the other checks as they are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@derrabus I’ve added the functional tests as suggested and pushed them. However, I noticed that none of the functional tests actually executed in the PR pipeline — is there something I need to do to make sure they run?
If you already had a chance to take a look, I’d really appreciate some feedback on whether this is heading in the right direction. That way I can adjust before proceeding with support for a second platform (e.g. MySQL).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, your new workflows were run: https://github.com/doctrine/dbal/actions/runs/17288021054/job/49069200071?pr=7096
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I noticed an error in continuous-integration.yml only afterwards. After fixing it, the functional tests were executed correctly.
I have one question regarding the functional tests: in MySQL there is a test case Doctrine\DBAL\Tests\Functional\Schema\MySQLSchemaManagerTest::testColumnIntrospection that attempts to define any type as a column to compare it against the same definition. At the moment, I’ve introduced two types for PostgreSQL/PostGIS, geometry and geography. Since geography is not available in MySQL, the current test setup would fail.
Is this behavior intentional, or would it be acceptable to adjust the tests so that they explicitly check which types are supported on MySQL?
543f582 to
a35520d
Compare
810f526 to
86b4b15
Compare
|
Adding spatial index management would be interesting for this PR I think :). For instance: Sources: |
Thanks for the advice — I agree, index management is definitely an important aspect when working with geometry data. I haven’t investigated it in depth yet, but it looks like this might already be supported by specifying the index type. A quick search in the repository shows an |
ba2a7aa to
a33fcd8
Compare
0b81371 to
a3544b1
Compare
|
Wow, that's exactly what I'd like to see integrated into Doctrine soon. Great work! |
a3544b1 to
98c3be5
Compare
Add geometry and geography types for spatial data Introduces new spatial data types for handling geometric and geographic data in database applications. GeometryType handles planar coordinates while GeographyType handles spherical earth coordinates, both using GeoJSON format. This provides foundation for spatial data operations across database platforms that support spatial extensions.
Extends Column and ColumnEditor with geometryType and srid properties to support PostgreSQL's PostGIS spatial types and provides a clean schema API for working with GEOMETRY and GEOGRAPHY columns while maintaining backward compatibility. This builds on the core spatial types implementation to complete the PostgreSQL spatial type support at the schema level.
This commit adds functional testing for PostGIS spatial types (GEOMETRY and GEOGRAPHY) with schema introspection and CI integration. The implementation leverages PostgreSQL's native type system for introspection, making it compatible with any PostgreSQL instance without requiring PostGIS system tables to be accessible during schema operations.
Extends AbstractMySQLPlatform with GEOMETRY type support and enhances MySQLSchemaManager to introspect spatial columns with geometryType and SRID properties. MySQL supports GEOMETRY types (POINT, LINESTRING, POLYGON, etc.) with optional SRID constraints using the conditional comment syntax for MySQL 8.0.3+.
Refactor GeometryType and GeographyType to replace direct GeoJSON string handling with dedicated value objects, preventing exposure of database-specific formats to application code. This commit introduces a Geometry value object that encapsulates a supporting GeoJSON value object responsible for validating and wrapping GeoJSON representations.
Implement spatial index creation and introspection for PostgreSQL using the GIST (Generalized Search Tree) index method — the standard access method for spatial data in PostGIS. This commit introduces a new getIndexMethodSQL() hook in AbstractPlatform for platform-specific index clauses, and overrides it in PostgreSQLPlatform to emit "USING GIST" for spatial indexes. SQL generation examples: MySQL → CREATE SPATIAL INDEX idx ON table (col) PostgreSQL → CREATE INDEX idx ON table USING GIST (col)
98c3be5 to
e9e86f4
Compare
|
Hi @derrabus I’ve addressed all the feedback received so far and updated the PR accordingly. Thanks a lot for your time |
derrabus
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank your for all the work you've put into this PR. Your changes look very promising.
| - "8.3" | ||
| - "8.4" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - "8.3" | |
| - "8.4" | |
| - "8.5" |
I don't expect any new insights from testing on multiple PHP versions, do you? Let's not bloat the matrix too much. Let's test with the lastest PHP version on both extensions and on the lowest supported PHP version with one extension only.
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| uses: actions/checkout@v4 | |
| uses: actions/checkout@v6 |
|
|
||
| if ($srid !== null) { | ||
| // MySQL 8.0.3+ supports SRID attribute using conditional comment syntax | ||
| $sql .= sprintf(' /*!80003 SRID %d */', $srid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to emit those version-conditional comments? We should know which version of MySQL we're connecting to.
| // MariaDB does not support SRS_ID column in information_schema.COLUMNS | ||
| // For MariaDB, SRID is parsed from COLUMN_TYPE conditional comments | ||
| $sridColumn = $this->platform instanceof MariaDBPlatform | ||
| ? 'NULL AS srs_id' | ||
| : 'c.SRS_ID AS srs_id'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't perform instanceof checks on the platform. Move this logic into the platform classes instead.
| try { | ||
| return json_encode($this->data, JSON_THROW_ON_ERROR); | ||
| } catch (JsonException $e) { | ||
| throw new InvalidArgumentException( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can a method without arguments raise errors on an "invalid argument"?
| /** | ||
| * Normalizes types array from positional or associative to associative format. | ||
| * | ||
| * @param array<int<0,max>, string|ParameterType|Type>|array<string, string|ParameterType|Type> $types | ||
| * @param list<string> $columnNames | ||
| * | ||
| * @return array<string, string|ParameterType|Type> | ||
| */ | ||
| private function normalizeTypes(array $types, array $columnNames): array | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have to do this?
| ->setUnquotedName('geom') | ||
| ->setTypeName(Types::GEOMETRY) | ||
| ->setGeometryType('POINT') | ||
| ->setSrid(4326) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this magic number documented anywhere?
| // MariaDB does not support SRID specification in column definitions | ||
| if (! $this->connection->getDatabasePlatform() instanceof MariaDBPlatform) { | ||
| self::assertSame(4326, $sridGeom->getSrid()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what is the expected behavior for MariaDB then?
| $alterSql = $this->connection->getDatabasePlatform()->getAlterTableSQL($tableDiff); | ||
|
|
||
| // Execute ALTER TABLE | ||
| foreach ($alterSql as $sql) { | ||
| $this->connection->executeStatement($sql); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema manager can apply the diff for us. We don't have to feed the SQL statements into the connection ourselves.
| } | ||
|
|
||
| $testConn->close(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this something our CI setup should do instead?
Summary
This PR introduces spatial data type support for DBAL, starting with PostgreSQL/PostGIS and MySQL. This implementation is intended to serve as a reference and guide for adding support for other database platforms in the future.
Core Spatial Types
Types::GEOMETRY) - For planar coordinate systemsTypes::GEOGRAPHY) - For spherical earth coordinatesSchema API Integration
ColumnandColumnEditorwithgeometryTypeandsridpropertiesColumn::editor()->setGeometryType('POINT')->setSrid(4326)What's next