Skip to content
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

ExecuteSQL(): add a warning if the dialect name isn't recognized (fixes #8843), and improve doc #8844

Merged
merged 2 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions autotest/ogr/ogr_sql_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,31 @@ def data_ds():
yield ds


###############################################################################
# Test an unrecognized dialect name


def test_ogr_sql_unrecognized_dialect(data_ds):
class MyHandler:
def __init__(self):
self.warning_raised = False

def callback(self, err_type, err_no, err_msg):
if (
err_type == gdal.CE_Warning
and "Dialect 'unknown' is unsupported" in err_msg
):
self.warning_raised = True

my_error_handler = MyHandler()
with gdaltest.error_handler(my_error_handler.callback):
with data_ds.ExecuteSQL(
"SELECT * FROM poly WHERE eas_id < 167", dialect="unknown"
) as sql_lyr:
assert sql_lyr.GetFeatureCount(force=1) == 3
assert my_error_handler.warning_raised


###############################################################################
# Test a simple query with a where clause.

Expand Down
48 changes: 6 additions & 42 deletions doc/source/user/ogr_sql_dialect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ used to provide a subset of SQL SELECT capability to applications. This
page discusses the generic SQL implementation implemented within OGR, and
issues with driver specific SQL support.

An alternate "dialect", the SQLite dialect, can be used
instead of the OGRSQL dialect. Refer to the :ref:`sql_sqlite_dialect` page for more details.
The ``OGRSQL`` dialect can be requested with the ``OGRSQL`` string passed
as the dialect parameter of :cpp:func:`GDALDataset::ExecuteSQL`, or with the
`-dialect` option switch of the :ref:`ogrinfo` or :ref:`ogr2ogr` utilities.

An alternate dialect, the ``SQLite`` dialect, can be used
instead of the ``OGRSQL`` dialect. Refer to the :ref:`sql_sqlite_dialect` page for more details.

The OGRLayer class also supports applying an attribute query filter to
features returned using the :cpp:func:`OGRLayer::SetAttributeFilter()` method. The
Expand Down Expand Up @@ -678,43 +682,3 @@ supported on datasources that declare the ODsCDeleteLayer capability.
.. code-block::
DROP TABLE nation
ExecuteSQL()
------------

SQL is executed against an GDALDataset, not against a specific layer. The
call looks like this:

.. code-block:: cpp
OGRLayer * GDALDataset::ExecuteSQL( const char *pszSQLCommand,
OGRGeometry *poSpatialFilter,
const char *pszDialect );
The ``pszDialect`` argument is in theory intended to allow for support of
different command languages against a provider, but for now applications
should always pass an empty (not NULL) string to get the default dialect.

The ``poSpatialFilter`` argument is a geometry used to select a bounding rectangle
for features to be returned in a manner similar to the
:cpp:func:`OGRLayer::SetSpatialFilter` method. It may be NULL for no special spatial
restriction.

The result of an ExecuteSQL() call is usually a temporary OGRLayer representing
the results set from the statement. This is the case for a SELECT statement
for instance. The returned temporary layer should be released with
:cpp:func:`GDALDataset::ReleaseResultsSet` method when no longer needed. Failure
to release it before the datasource is destroyed may result in a crash.

Non-OGR SQL
-----------

All OGR drivers for database systems: :ref:`vector.mysql`, :ref:`vector.pg`,
:ref:`vector.oci`, :ref:`vector.sqlite`, :ref:`vector.odbc`, :ref:`vector.pgeo`,
:ref:`vector.hana` and :ref:`vector.mssqlspatial`,
override the :cpp:func:`GDALDataset::ExecuteSQL` function with dedicated implementation
and, by default, pass the SQL statements directly to the underlying RDBMS.
In these cases the SQL syntax varies in some particulars from OGR SQL.
Also, anything possible in SQL can then be accomplished for these particular
databases. Only the result of SQL WHERE statements will be returned as
layers.
52 changes: 52 additions & 0 deletions doc/source/user/ogr_sql_sqlite_dialect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,60 @@
OGR SQL dialect and SQLITE SQL dialect
================================================================================

The GDALDataset supports executing commands against a datasource via the
:cpp:func:`GDALDataset::ExecuteSQL` method. How such commands are evaluated
is dependent on the datasets.

- For most file formats (e.g. Shapefiles, GeoJSON, MapInfo files), the built-in
:ref:`ogr_sql_dialect` dialect will be used by defaults. It is also possible
to request the :ref:`sql_sqlite_dialect` alternate dialect to be used, which
will use the SQLite engine to evaluate commands on GDAL datasets.

- All OGR drivers for database systems: :ref:`vector.mysql`, :ref:`vector.pg`,
:ref:`vector.oci`, :ref:`vector.sqlite`, :ref:`vector.gpkg`,
:ref:`vector.odbc`, :ref:`vector.pgeo`, :ref:`vector.hana` and :ref:`vector.mssqlspatial`,
override the :cpp:func:`GDALDataset::ExecuteSQL` function with dedicated implementation
and, by default, pass the SQL statements directly to the underlying RDBMS.
In these cases the SQL syntax varies in some particulars from OGR SQL.
Also, anything possible in SQL can then be accomplished for these particular
databases. Generally, only the result of SELECT statements will be returned as
layers. For those drivers, it is also possible to explicitly request the
``OGRSQL`` and ``SQLITE`` dialects, although performance will generally be
much less as the native SQL engine of those database systems.

Dialects
--------

.. toctree::
:maxdepth: 1

ogr_sql_dialect
sql_sqlite_dialect


ExecuteSQL()
------------

SQL is executed against an GDALDataset, not against a specific layer. The
call looks like this:

.. code-block:: cpp
OGRLayer * GDALDataset::ExecuteSQL( const char *pszSQLCommand,
OGRGeometry *poSpatialFilter,
const char *pszDialect );
The ``pszDialect`` argument is in theory intended to allow for support of
different command languages against a provider, but for now applications
should always pass an empty (not NULL) string to get the default dialect.

The ``poSpatialFilter`` argument is a geometry used to select a bounding rectangle
for features to be returned in a manner similar to the
:cpp:func:`OGRLayer::SetSpatialFilter` method. It may be NULL for no special spatial
restriction.

The result of an ExecuteSQL() call is usually a temporary OGRLayer representing
the results set from the statement. This is the case for a SELECT statement
for instance. The returned temporary layer should be released with
:cpp:func:`GDALDataset::ReleaseResultsSet` method when no longer needed. Failure
to release it before the datasource is destroyed may result in a crash.
10 changes: 5 additions & 5 deletions doc/source/user/sql_sqlite_dialect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ SQL SQLite dialect

.. highlight:: sql

The SQLite "dialect" can be used as an alternate SQL dialect to the
The ``SQLite`` dialect can be used as an alternate SQL dialect to the
:ref:`ogr_sql_dialect`.
This assumes that GDAL/OGR is built with support for SQLite, and preferably
with `Spatialite <https://www.gaia-gis.it/fossil/libspatialite/index>`_ support too to benefit from spatial functions.

The SQLite dialect may be used with any OGR datasource, like the OGR SQL dialect. It
is available through the GDALDataset::ExecuteSQL() method by specifying the pszDialect to
"SQLITE". For the :ref:`ogrinfo` or :ref:`ogr2ogr`
utility, you must specify the "-dialect SQLITE" option.
The SQLite dialect may be used with any OGR datasource, like the OGR SQL dialect.
The ``SQLite`` dialect can be requested with the ``SQLite`` string passed
as the dialect parameter of :cpp:func:`GDALDataset::ExecuteSQL`, or with the
rouault marked this conversation as resolved.
Show resolved Hide resolved
`-dialect` option of the :ref:`ogrinfo` or :ref:`ogr2ogr` utilities.

This is mainly aimed to execute SELECT statements, but, for datasources that support
update, INSERT/UPDATE/DELETE statements can also be run. GDAL is internally using
Expand Down
30 changes: 30 additions & 0 deletions gcore/gdaldataset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6478,6 +6478,36 @@ GDALDataset::ExecuteSQL(const char *pszStatement, OGRGeometry *poSpatialFilter,
#endif
}

if (pszDialect != nullptr && !EQUAL(pszDialect, "") &&
!EQUAL(pszDialect, "OGRSQL"))
{
std::string osDialectList = "'OGRSQL'";
#ifdef SQLITE_ENABLED
osDialectList += ", 'SQLITE'";
#endif
const char *pszDialects =
GetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS);
if (pszDialects)
{
const CPLStringList aosTokens(
CSLTokenizeString2(pszDialects, " ", 0));
for (int i = 0; i < aosTokens.size(); ++i)
{
if (!EQUAL(aosTokens[i], "OGRSQL") &&
!EQUAL(aosTokens[i], "SQLITE"))
{
osDialectList += ", '";
osDialectList += aosTokens[i];
osDialectList += "'";
}
}
}
CPLError(CE_Warning, CPLE_NotSupported,
"Dialect '%s' is unsupported. Only supported dialects are %s. "
"Defaulting to OGRSQL",
pszDialect, osDialectList.c_str());
}

/* -------------------------------------------------------------------- */
/* Handle CREATE INDEX statements specially. */
/* -------------------------------------------------------------------- */
Expand Down
8 changes: 5 additions & 3 deletions ogr/ogrsf_frmts/gpkg/ogrgeopackagedatasource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7310,11 +7310,13 @@ OGRLayer *GDALGeoPackageDataset::ExecuteSQL(const char *pszSQLCommand,
return nullptr;
}

if (pszDialect != nullptr && EQUAL(pszDialect, "OGRSQL"))
return GDALDataset::ExecuteSQL(osSQLCommand, poSpatialFilter,
pszDialect);
else if (pszDialect != nullptr && EQUAL(pszDialect, "INDIRECT_SQLITE"))
return GDALDataset::ExecuteSQL(osSQLCommand, poSpatialFilter, "SQLITE");
else if (pszDialect != nullptr && !EQUAL(pszDialect, "") &&
!EQUAL(pszDialect, "NATIVE") && !EQUAL(pszDialect, "SQLITE") &&
!EQUAL(pszDialect, "DEBUG"))
return GDALDataset::ExecuteSQL(osSQLCommand, poSpatialFilter,
pszDialect);

#if SQLITE_VERSION_NUMBER < 3007017
// Emulate PRAGMA application_id
Expand Down
10 changes: 6 additions & 4 deletions ogr/ogrsf_frmts/sqlite/ogrsqlitedatasource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3206,12 +3206,14 @@ OGRLayer *OGRSQLiteDataSource::ExecuteSQL(const char *pszSQLCommand,
}
}

if (pszDialect != nullptr && EQUAL(pszDialect, "OGRSQL"))
return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
pszDialect);
else if (pszDialect != nullptr && EQUAL(pszDialect, "INDIRECT_SQLITE"))
if (pszDialect != nullptr && EQUAL(pszDialect, "INDIRECT_SQLITE"))
return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
"SQLITE");
else if (pszDialect != nullptr && !EQUAL(pszDialect, "") &&
!EQUAL(pszDialect, "NATIVE") && !EQUAL(pszDialect, "SQLITE"))

return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
pszDialect);

/* -------------------------------------------------------------------- */
/* Special case DELLAYER: command. */
Expand Down
Loading