From c41882ba511a37cd7bdaa70b401608b40e7772d1 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 16:01:22 +0200 Subject: [PATCH 01/35] Added support for table synonyms --- pygeoapi/provider/oracle.py | 89 +++++++++++++++++++++++++++-------- tests/load_oracle_data.py | 13 +++++ tests/test_oracle_provider.py | 70 +++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 19 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 1acb5eae5..6623d2465 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -39,6 +39,7 @@ ProviderConnectionError, ProviderItemNotFoundError, ProviderQueryError, + ProviderGenericError, ) LOGGER = logging.getLogger(__name__) @@ -184,7 +185,8 @@ def __enter__(self): LOGGER.error(e) raise ProviderConnectionError(e) - # Check if table name has schema inside + # Check if table name has schema/owner inside + # If not, current user is set table_parts = self.table.split(".") if len(table_parts) == 2: schema = table_parts[0] @@ -196,34 +198,23 @@ def __enter__(self): LOGGER.debug("Schema: " + schema) LOGGER.debug("Table: " + table) - self.cur = self.conn.cursor() if self.context == "query": - # Get table column names and types, excluding geometry - query_cols = "select column_name, data_type \ - from all_tab_columns \ - where table_name = UPPER(:table_name) \ - and owner = UPPER(:owner) \ - and data_type != 'SDO_GEOMETRY'" - - self.cur.execute( - query_cols, {"table_name": table, "owner": schema} - ) - result = self.cur.fetchall() + column_list = self._get_table_columns(schema, table) # When self.properties is set, then the result would be filtered if self.properties: - result = [ - res - for res in result - if res[0].lower() + column_list = [ + col + for col in column_list + if col[0].lower() in [item.lower() for item in self.properties] ] # Concatenate column names with ', ' - self.columns = ", ".join([item[0].lower() for item in result]) + self.columns = ", ".join([item[0].lower() for item in column_list]) # Populate dictionary for columns with column type - for k, v in dict(result).items(): + for k, v in dict(column_list).items(): self.fields[k.lower()] = {"type": v} return self @@ -232,6 +223,66 @@ def __exit__(self, exc_type, exc_val, exc_tb): # some logic to commit/rollback self.conn.close() + def _get_table_columns(self, schema, table): + """ + Returns an array with all column names and data types + from Oracle table ALL_TAB_COLUMNS. + Lookup for public and private synonyms. + Throws ProviderGenericError when table not exist or accesable. + """ + cur = self.conn.cursor() + + sql = "SELECT COUNT(1) \ + FROM all_tables \ + WHERE table_name = UPPER(:table_name) \ + AND owner = UPPER(:owner)" + cur.execute(sql, {"table_name": table, "owner": schema}) + result = cur.fetchone() + + if result[0] == 0: + sql = "SELECT COUNT(1) \ + FROM all_synonyms \ + WHERE synonym_name = UPPER(:table_name) \ + AND owner = UPPER(:owner)" + cur.execute(sql, {"table_name": table, "owner": schema}) + result = cur.fetchone() + + if result[0] == 0: + sql = "SELECT COUNT(1) \ + FROM all_synonyms \ + WHERE synonym_name = UPPER(:table_name) \ + AND owner = 'PUBLIC'" + cur.execute(sql, {"table_name": table}) + result = cur.fetchone() + + if result[0] == 0: + raise ProviderGenericError("Table not found") + + else: + schema = "PUBLIC" + + sql = "SELECT table_owner, table_name \ + FROM all_synonyms \ + WHERE synonym_name = UPPER(:table_name) \ + AND owner = UPPER(:owner)" + cur.execute(sql, {"table_name": table, "owner": schema}) + result = cur.fetchone() + + schema = result[0] + table = result[1] + + # Get table column names and types, excluding geometry + query_cols = "select column_name, data_type \ + from all_tab_columns \ + where table_name = UPPER(:table_name) \ + and owner = UPPER(:owner) \ + and data_type != 'SDO_GEOMETRY'" + + cur.execute(query_cols, {"table_name": table, "owner": schema}) + result = cur.fetchall() + + return result + class OracleProvider(BaseProvider): def __init__(self, provider_def): diff --git a/tests/load_oracle_data.py b/tests/load_oracle_data.py index c3eef4555..886f6b403 100644 --- a/tests/load_oracle_data.py +++ b/tests/load_oracle_data.py @@ -23,6 +23,19 @@ cur.execute(sql) +sql = """ +CREATE PUBLIC SYNONYM lakes_public_syn FOR geo_test.lakes +""" + +cur.execute(sql) + +sql = """ +CREATE SYNONYM geo_test.lakes_private_syn FOR geo_test.lakes +""" + +cur.execute(sql) + + sql = """ INSERT INTO lakes (area, volume, name, wiki_link, geometry) VALUES (NULL, NULL, 'Lake Baikal', diff --git a/tests/test_oracle_provider.py b/tests/test_oracle_provider.py index f89e5afd7..7fc6eb9f0 100644 --- a/tests/test_oracle_provider.py +++ b/tests/test_oracle_provider.py @@ -137,6 +137,44 @@ def config(): } +@pytest.fixture() +def config_public_synonym(): + return { + "name": "Oracle", + "type": "feature", + "data": { + "host": HOST, + "port": PORT, + "service_name": SERVICE_NAME, + "user": USERNAME, + "password": PASSWORD, + }, + "id_field": "id", + "table": "lakes_public_syn", + "geom_field": "geometry", + "editable": True, + } + + +@pytest.fixture() +def config_private_synonym(): + return { + "name": "Oracle", + "type": "feature", + "data": { + "host": HOST, + "port": PORT, + "service_name": SERVICE_NAME, + "user": USERNAME, + "password": PASSWORD, + }, + "id_field": "id", + "table": "lakes_private_syn", + "geom_field": "geometry", + "editable": True, + } + + @pytest.fixture() def config_manipulator(config): return { @@ -235,6 +273,38 @@ def test_get_fields(config): assert provider.fields == expected_fields +def test_get_fields_private_synonym(config_private_synonym): + """Test get_fields from private synonym""" + expected_fields = { + "id": {"type": "NUMBER"}, + "area": {"type": "NUMBER"}, + "volume": {"type": "NUMBER"}, + "name": {"type": "VARCHAR2"}, + "wiki_link": {"type": "VARCHAR2"}, + } + + provider = OracleProvider(config_private_synonym) + + assert provider.get_fields() == expected_fields + assert provider.fields == expected_fields + + +def test_get_fields_public_synonym(config_public_synonym): + """Test get_fields from public synonym""" + expected_fields = { + "id": {"type": "NUMBER"}, + "area": {"type": "NUMBER"}, + "volume": {"type": "NUMBER"}, + "name": {"type": "VARCHAR2"}, + "wiki_link": {"type": "VARCHAR2"}, + } + + provider = OracleProvider(config_public_synonym) + + assert provider.get_fields() == expected_fields + assert provider.fields == expected_fields + + def test_get_fields_properties(config_properties): """ Test get_fields with subset of columns. From 7ba18bad92d7b2fc5e33ba1fc587e99f4e7c3daa Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 16:12:41 +0200 Subject: [PATCH 02/35] Added new parameters to query and manipulator call --- pygeoapi/provider/oracle.py | 16 ++++++++++++++-- tests/test_oracle_provider.py | 12 +++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 6623d2465..0c5c02383 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -441,9 +441,11 @@ def query( datetime_=None, properties=[], sortby=[], - select_properties=[], skip_geometry=False, + select_properties=[], + crs_transform_spec=None, q=None, + language=None, filterq=None, **kwargs, ): @@ -585,9 +587,19 @@ def query( sql_query, bind_variables, self.sql_manipulator_options, + offset, + limit, + resulttype, bbox, - self.source_crs, + datetime_, properties, + sortby, + skip_geometry, + select_properties, + crs_transform_spec, + q, + language, + filterq, ) # Clean up placeholders that aren't used by the diff --git a/tests/test_oracle_provider.py b/tests/test_oracle_provider.py index 7fc6eb9f0..a19f110ee 100644 --- a/tests/test_oracle_provider.py +++ b/tests/test_oracle_provider.py @@ -48,9 +48,19 @@ def process_query( sql_query, bind_variables, sql_manipulator_options, + offset, + limit, + resulttype, bbox, - source_crs, + datetime_, properties, + sortby, + skip_geometry, + select_properties, + crs_transform_spec, + q, + language, + filterq, ): sql = "ID = 10 AND :foo != :bar" From 7645a944b2f35d4c20c23fab21fbc6eed564beb5 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 16:31:30 +0200 Subject: [PATCH 03/35] Changed error types --- pygeoapi/provider/oracle.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 0c5c02383..3e15c0fa6 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -37,9 +37,10 @@ from pygeoapi.provider.base import ( BaseProvider, ProviderConnectionError, + ProviderGenericError, + ProviderInvalidQueryError, ProviderItemNotFoundError, ProviderQueryError, - ProviderGenericError, ) LOGGER = logging.getLogger(__name__) @@ -98,7 +99,7 @@ def __enter__(self): ) if "tns_name" not in self.conn_dict: - raise Exception( + raise ProviderConnectionError( "tns_name must be set for external authentication!" ) @@ -112,7 +113,7 @@ def __enter__(self): ) if "host" not in self.conn_dict: - raise Exception( + raise ProviderConnectionError( "Host must be set for connection with service_name!" ) @@ -129,7 +130,7 @@ def __enter__(self): ) if "host" not in self.conn_dict: - raise Exception( + raise ProviderConnectionError( "Host must be set for connection with sid!" ) @@ -472,12 +473,12 @@ def query( if self.mandatory_properties: for mand_col in self.mandatory_properties: if mand_col == "bbox" and not bbox: - raise ProviderQueryError( + raise ProviderInvalidQueryError( f"Missing mandatory filter property: {mand_col}" ) else: if mand_col not in property_dict: - raise ProviderQueryError( + raise ProviderInvalidQueryError( f"Missing mandatory filter property: {mand_col}" ) From ed7262414c780736b4d962e67c19aa3e3f9b1ece Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 17:02:22 +0200 Subject: [PATCH 04/35] Mount volumes to oracle container --- .github/workflows/main.yml | 6 ++++-- tests/data/oracle/init-db/01-create-user.sql | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/data/oracle/init-db/01-create-user.sql diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 047a172fc..87f039eec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,11 +51,13 @@ jobs: # Provide passwords and other environment variables to container env: ORACLE_RANDOM_PASSWORD: true - APP_USER: geo_test - APP_USER_PASSWORD: geo_test + # APP_USER: geo_test + # APP_USER_PASSWORD: geo_test # Forward Oracle port ports: - 1521:1521 + volumes: + - tests/oracle/init-db:/container-entrypoint-initdb.d # Provide healthcheck script options for startup options: >- --health-cmd healthcheck.sh diff --git a/tests/data/oracle/init-db/01-create-user.sql b/tests/data/oracle/init-db/01-create-user.sql new file mode 100644 index 000000000..5f0d86a8a --- /dev/null +++ b/tests/data/oracle/init-db/01-create-user.sql @@ -0,0 +1,3 @@ +CREATE USER geo_test IDENTIFIED BY geo_test QUOTA UNLIMITED ON USERS; + +GRANT CONNECT, RESOURCE, DBA TO geo_test; \ No newline at end of file From 875d4e75f3fd42248073bb322a31f6f94b30fbdb Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 17:08:38 +0200 Subject: [PATCH 05/35] workflow part 2 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 87f039eec..31a68ba14 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: ports: - 1521:1521 volumes: - - tests/oracle/init-db:/container-entrypoint-initdb.d + - tests/data/oracle/init-db:/container-entrypoint-initdb.d # Provide healthcheck script options for startup options: >- --health-cmd healthcheck.sh From 00715c6d441d56a7dde4f1435ed9a9e94cdf5c02 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 17:14:44 +0200 Subject: [PATCH 06/35] workflow part 3 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 31a68ba14..47e3c51dc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: ports: - 1521:1521 volumes: - - tests/data/oracle/init-db:/container-entrypoint-initdb.d + - ${{ github.workspace }}/tests/data/oracle/init-db:/container-entrypoint-initdb.d # Provide healthcheck script options for startup options: >- --health-cmd healthcheck.sh From f90d735cccd30db165cdfad9dbefb23ac3c7168d Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Fri, 20 Oct 2023 17:22:35 +0200 Subject: [PATCH 07/35] workflow part 4 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 47e3c51dc..8106c8255 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: ports: - 1521:1521 volumes: - - ${{ github.workspace }}/tests/data/oracle/init-db:/container-entrypoint-initdb.d + - ${{ github.workspace }}/tests/data/oracle/init-db:/docker-entrypoint-startdb.d # Provide healthcheck script options for startup options: >- --health-cmd healthcheck.sh From 31b31892fa5b0ba7fe68bf14bde49dfaa32903f7 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 10:24:33 +0200 Subject: [PATCH 08/35] Changed file permissions to 777 --- tests/data/oracle/init-db/01-create-user.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/data/oracle/init-db/01-create-user.sql diff --git a/tests/data/oracle/init-db/01-create-user.sql b/tests/data/oracle/init-db/01-create-user.sql old mode 100644 new mode 100755 From befb4c29e179c792b7fae797c339d249357c1daa Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 10:31:26 +0200 Subject: [PATCH 09/35] Deleted folder --- tests/data/oracle/init-db/01-create-user.sql | 3 --- 1 file changed, 3 deletions(-) delete mode 100755 tests/data/oracle/init-db/01-create-user.sql diff --git a/tests/data/oracle/init-db/01-create-user.sql b/tests/data/oracle/init-db/01-create-user.sql deleted file mode 100755 index 5f0d86a8a..000000000 --- a/tests/data/oracle/init-db/01-create-user.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE USER geo_test IDENTIFIED BY geo_test QUOTA UNLIMITED ON USERS; - -GRANT CONNECT, RESOURCE, DBA TO geo_test; \ No newline at end of file From 24cc032fca68dc50c9168e69c516a7f89863c49c Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 10:33:16 +0200 Subject: [PATCH 10/35] Recreated folder --- tests/data/oracle/init-db/01-create-user.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 tests/data/oracle/init-db/01-create-user.sql diff --git a/tests/data/oracle/init-db/01-create-user.sql b/tests/data/oracle/init-db/01-create-user.sql new file mode 100755 index 000000000..5f0d86a8a --- /dev/null +++ b/tests/data/oracle/init-db/01-create-user.sql @@ -0,0 +1,3 @@ +CREATE USER geo_test IDENTIFIED BY geo_test QUOTA UNLIMITED ON USERS; + +GRANT CONNECT, RESOURCE, DBA TO geo_test; \ No newline at end of file From 64cda6c52c5198052dee1e1897fae96d9711ae78 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 11:05:30 +0200 Subject: [PATCH 11/35] Changed to official Oracle Docker-Image --- .github/workflows/main.yml | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8106c8255..1cb9f7858 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,26 +45,44 @@ jobs: services: # Oracle service (label used to access the service container) + # oracle: + # # Docker Hub image (feel free to change the tag "latest" to any other available one) + # image: gvenzl/oracle-xe:latest + # # Provide passwords and other environment variables to container + # env: + # ORACLE_RANDOM_PASSWORD: true + # # APP_USER: geo_test + # # APP_USER_PASSWORD: geo_test + # # Forward Oracle port + # ports: + # - 1521:1521 + # volumes: + # - ${{ github.workspace }}/tests/data/oracle/init-db:/container-entrypoint-startdb.d + # # Provide healthcheck script options for startup + # options: >- + # --health-cmd healthcheck.sh + # --health-interval 10s + # --health-timeout 5s + # --health-retries 10 oracle: # Docker Hub image (feel free to change the tag "latest" to any other available one) - image: gvenzl/oracle-xe:latest + image: container-registry.oracle.com/database/express:latest # Provide passwords and other environment variables to container - env: - ORACLE_RANDOM_PASSWORD: true + # env: + # ORACLE_RANDOM_PASSWORD: true # APP_USER: geo_test # APP_USER_PASSWORD: geo_test # Forward Oracle port ports: - 1521:1521 volumes: - - ${{ github.workspace }}/tests/data/oracle/init-db:/docker-entrypoint-startdb.d + - ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup # Provide healthcheck script options for startup - options: >- - --health-cmd healthcheck.sh - --health-interval 10s - --health-timeout 5s - --health-retries 10 - + # options: >- + # --health-cmd healthcheck.sh + # --health-interval 10s + # --health-timeout 5s + # --health-retries 10 steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 From 772d82a919cef6925685a534dd1a4ea1255328bf Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 11:28:34 +0200 Subject: [PATCH 12/35] Added Chown user --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1cb9f7858..19024c3b0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -84,6 +84,9 @@ jobs: # --health-timeout 5s # --health-retries 10 steps: + - name: Chown user + run: | + sudo chown -R $USER:$USER $GITHUB_WORKSPACE - uses: actions/checkout@v2 - uses: actions/setup-python@v2 name: Setup Python ${{ matrix.python-version }} From aee3321fd0fd2572d461e779ed65ece4d542742d Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 12:48:47 +0200 Subject: [PATCH 13/35] back to gvenzl/oracle-xe:latest --- .github/workflows/main.yml | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 19024c3b0..5994d7058 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,44 +45,44 @@ jobs: services: # Oracle service (label used to access the service container) - # oracle: - # # Docker Hub image (feel free to change the tag "latest" to any other available one) - # image: gvenzl/oracle-xe:latest - # # Provide passwords and other environment variables to container - # env: - # ORACLE_RANDOM_PASSWORD: true - # # APP_USER: geo_test - # # APP_USER_PASSWORD: geo_test - # # Forward Oracle port - # ports: - # - 1521:1521 - # volumes: - # - ${{ github.workspace }}/tests/data/oracle/init-db:/container-entrypoint-startdb.d - # # Provide healthcheck script options for startup - # options: >- - # --health-cmd healthcheck.sh - # --health-interval 10s - # --health-timeout 5s - # --health-retries 10 oracle: # Docker Hub image (feel free to change the tag "latest" to any other available one) - image: container-registry.oracle.com/database/express:latest + image: gvenzl/oracle-xe:latest # Provide passwords and other environment variables to container - # env: - # ORACLE_RANDOM_PASSWORD: true + env: + ORACLE_RANDOM_PASSWORD: true # APP_USER: geo_test # APP_USER_PASSWORD: geo_test # Forward Oracle port ports: - 1521:1521 volumes: - - ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup + - ${{ github.workspace }}/tests/data/oracle/init-db:/container-entrypoint-startdb.d # Provide healthcheck script options for startup - # options: >- - # --health-cmd healthcheck.sh - # --health-interval 10s - # --health-timeout 5s - # --health-retries 10 + options: >- + --health-cmd healthcheck.sh + --health-interval 10s + --health-timeout 5s + --health-retries 10 + # oracle: + # # Docker Hub image (feel free to change the tag "latest" to any other available one) + # image: container-registry.oracle.com/database/express:latest + # # Provide passwords and other environment variables to container + # # env: + # # ORACLE_RANDOM_PASSWORD: true + # # APP_USER: geo_test + # # APP_USER_PASSWORD: geo_test + # # Forward Oracle port + # ports: + # - 1521:1521 + # volumes: + # - ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup + # # Provide healthcheck script options for startup + # # options: >- + # # --health-cmd healthcheck.sh + # # --health-interval 10s + # # --health-timeout 5s + # # --health-retries 10 steps: - name: Chown user run: | From bdc32b1a1c1100e73dd4079cf3821a4813f9b82d Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 13:05:55 +0200 Subject: [PATCH 14/35] Tried docker-entrypoint-startdb.d --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5994d7058..389407d01 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: ports: - 1521:1521 volumes: - - ${{ github.workspace }}/tests/data/oracle/init-db:/container-entrypoint-startdb.d + - ${{ github.workspace }}/tests/data/oracle/init-db:/docker-entrypoint-startdb.d # Provide healthcheck script options for startup options: >- --health-cmd healthcheck.sh From 8274f53430f7966aa91ccbd235ab7e998f53db1e Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 13:49:11 +0200 Subject: [PATCH 15/35] Added addnab/docker-run-action@v3 --- .github/workflows/main.yml | 45 +++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 389407d01..efb13eee9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,27 +43,27 @@ jobs: env: PYGEOAPI_CONFIG: "$(pwd)/pygeoapi-config.yml" - services: + #services: # Oracle service (label used to access the service container) - oracle: - # Docker Hub image (feel free to change the tag "latest" to any other available one) - image: gvenzl/oracle-xe:latest - # Provide passwords and other environment variables to container - env: - ORACLE_RANDOM_PASSWORD: true - # APP_USER: geo_test - # APP_USER_PASSWORD: geo_test - # Forward Oracle port - ports: - - 1521:1521 - volumes: - - ${{ github.workspace }}/tests/data/oracle/init-db:/docker-entrypoint-startdb.d - # Provide healthcheck script options for startup - options: >- - --health-cmd healthcheck.sh - --health-interval 10s - --health-timeout 5s - --health-retries 10 + # oracle: + # # Docker Hub image (feel free to change the tag "latest" to any other available one) + # image: gvenzl/oracle-xe:latest + # # Provide passwords and other environment variables to container + # env: + # ORACLE_RANDOM_PASSWORD: true + # # APP_USER: geo_test + # # APP_USER_PASSWORD: geo_test + # # Forward Oracle port + # ports: + # - 1521:1521 + # volumes: + # - ${{ github.workspace }}/tests/data/oracle/init-db:/docker-entrypoint-startdb.d + # # Provide healthcheck script options for startup + # options: >- + # --health-cmd healthcheck.sh + # --health-interval 10s + # --health-timeout 5s + # --health-retries 10 # oracle: # # Docker Hub image (feel free to change the tag "latest" to any other available one) # image: container-registry.oracle.com/database/express:latest @@ -128,6 +128,11 @@ jobs: with: packages: gdal-bin libgdal-dev version: 3.0.4 + - name: Install an run Oracle + uses: addnab/docker-run-action@v3 + with: + image: container-registry.oracle.com/database/express:latest + options: -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup - name: Install requirements 📦 run: | pip3 install -r requirements.txt From 207a784af338d201b54f35aaa17fc5794af17b02 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 14:01:22 +0200 Subject: [PATCH 16/35] Added port and deamon mode --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index efb13eee9..4f826b0f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -132,7 +132,7 @@ jobs: uses: addnab/docker-run-action@v3 with: image: container-registry.oracle.com/database/express:latest - options: -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup + options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 - name: Install requirements 📦 run: | pip3 install -r requirements.txt From 79e501ae6ca31c29a28d0551ec38b285ab002338 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 14:33:29 +0200 Subject: [PATCH 17/35] next try --- .github/workflows/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4f826b0f1..5efb6c786 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -128,11 +128,12 @@ jobs: with: packages: gdal-bin libgdal-dev version: 3.0.4 - - name: Install an run Oracle + - name: Install and run Oracle uses: addnab/docker-run-action@v3 with: image: container-registry.oracle.com/database/express:latest - options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 + options: -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 + run: echo "Container running" - name: Install requirements 📦 run: | pip3 install -r requirements.txt From ea8b9a2f8d33013b539103953f65ee401fc2da76 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 14:47:14 +0200 Subject: [PATCH 18/35] added job.container.network --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5efb6c786..e1fb1e856 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -132,6 +132,7 @@ jobs: uses: addnab/docker-run-action@v3 with: image: container-registry.oracle.com/database/express:latest + docker_network: ${{ job.container.network }} options: -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 run: echo "Container running" - name: Install requirements 📦 From 4463a1b3221d751f86a03efce4bfe56f1b1a3334 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 15:08:09 +0200 Subject: [PATCH 19/35] next try --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e1fb1e856..a02dc7c17 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -133,7 +133,7 @@ jobs: with: image: container-registry.oracle.com/database/express:latest docker_network: ${{ job.container.network }} - options: -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 + options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 run: echo "Container running" - name: Install requirements 📦 run: | From 3e2d2714d18833b21b5f25c8d12eaa16febfe127 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 15:22:59 +0200 Subject: [PATCH 20/35] + docker ps --- .github/workflows/main.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a02dc7c17..632d47f64 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,24 +65,11 @@ jobs: # --health-timeout 5s # --health-retries 10 # oracle: - # # Docker Hub image (feel free to change the tag "latest" to any other available one) # image: container-registry.oracle.com/database/express:latest - # # Provide passwords and other environment variables to container - # # env: - # # ORACLE_RANDOM_PASSWORD: true - # # APP_USER: geo_test - # # APP_USER_PASSWORD: geo_test - # # Forward Oracle port # ports: # - 1521:1521 # volumes: # - ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup - # # Provide healthcheck script options for startup - # # options: >- - # # --health-cmd healthcheck.sh - # # --health-interval 10s - # # --health-timeout 5s - # # --health-retries 10 steps: - name: Chown user run: | @@ -155,6 +142,7 @@ jobs: python3 tests/load_mongo_data.py tests/data/ne_110m_populated_places_simple.geojson gunzip < tests/data/hotosm_bdi_waterways.sql.gz | psql postgresql://postgres:${{ secrets.DatabasePassword || 'postgres' }}@localhost:5432/test psql postgresql://postgres:${{ secrets.DatabasePassword || 'postgres' }}@localhost:5432/test -f tests/data/dummy_data.sql + docker ps python3 tests/load_oracle_data.py - name: run unit tests ⚙️ env: From 0daf02414127bcb393295e1ede5f7c9c8c4973a0 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 15:37:24 +0200 Subject: [PATCH 21/35] next try --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 632d47f64..e300b9a94 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -121,7 +121,6 @@ jobs: image: container-registry.oracle.com/database/express:latest docker_network: ${{ job.container.network }} options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 - run: echo "Container running" - name: Install requirements 📦 run: | pip3 install -r requirements.txt From 0234c8aede8995dc97f4395d0b2f706a75a35660 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 15:46:47 +0200 Subject: [PATCH 22/35] using docker run --- .github/workflows/main.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e300b9a94..3be7b635e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -116,11 +116,13 @@ jobs: packages: gdal-bin libgdal-dev version: 3.0.4 - name: Install and run Oracle - uses: addnab/docker-run-action@v3 - with: - image: container-registry.oracle.com/database/express:latest - docker_network: ${{ job.container.network }} - options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 + #uses: addnab/docker-run-action@v3 + #with: + # image: container-registry.oracle.com/database/express:latest + # docker_network: ${{ job.container.network }} + # options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 + run: | + docker run -d --name oracledb -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 container-registry.oracle.com/database/express:21.3.0-xe - name: Install requirements 📦 run: | pip3 install -r requirements.txt From 3d8ecf4df4216e512664bc29ae42802e2792afed Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 17:09:19 +0200 Subject: [PATCH 23/35] next try --- .github/workflows/main.yml | 2 +- tests/data/oracle/init-db/01-create-user.sql | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3be7b635e..0614bb9a7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -122,7 +122,7 @@ jobs: # docker_network: ${{ job.container.network }} # options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 run: | - docker run -d --name oracledb -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 container-registry.oracle.com/database/express:21.3.0-xe + docker run -d --name oracledb -e ORACLE_PWD=oracle -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 container-registry.oracle.com/database/express:21.3.0-xe - name: Install requirements 📦 run: | pip3 install -r requirements.txt diff --git a/tests/data/oracle/init-db/01-create-user.sql b/tests/data/oracle/init-db/01-create-user.sql index 5f0d86a8a..3f3d7a830 100755 --- a/tests/data/oracle/init-db/01-create-user.sql +++ b/tests/data/oracle/init-db/01-create-user.sql @@ -1,3 +1,5 @@ +CONNECT sys/oracle@XEPDB1 AS SYSDBA; + CREATE USER geo_test IDENTIFIED BY geo_test QUOTA UNLIMITED ON USERS; GRANT CONNECT, RESOURCE, DBA TO geo_test; \ No newline at end of file From 8a64168e1897c4c38fcdc2d06d8eb6e98b5bdfb7 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 17:38:55 +0200 Subject: [PATCH 24/35] next try --- .github/workflows/main.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0614bb9a7..70d87b62c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,33 +43,6 @@ jobs: env: PYGEOAPI_CONFIG: "$(pwd)/pygeoapi-config.yml" - #services: - # Oracle service (label used to access the service container) - # oracle: - # # Docker Hub image (feel free to change the tag "latest" to any other available one) - # image: gvenzl/oracle-xe:latest - # # Provide passwords and other environment variables to container - # env: - # ORACLE_RANDOM_PASSWORD: true - # # APP_USER: geo_test - # # APP_USER_PASSWORD: geo_test - # # Forward Oracle port - # ports: - # - 1521:1521 - # volumes: - # - ${{ github.workspace }}/tests/data/oracle/init-db:/docker-entrypoint-startdb.d - # # Provide healthcheck script options for startup - # options: >- - # --health-cmd healthcheck.sh - # --health-interval 10s - # --health-timeout 5s - # --health-retries 10 - # oracle: - # image: container-registry.oracle.com/database/express:latest - # ports: - # - 1521:1521 - # volumes: - # - ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup steps: - name: Chown user run: | @@ -116,11 +89,6 @@ jobs: packages: gdal-bin libgdal-dev version: 3.0.4 - name: Install and run Oracle - #uses: addnab/docker-run-action@v3 - #with: - # image: container-registry.oracle.com/database/express:latest - # docker_network: ${{ job.container.network }} - # options: -d -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 run: | docker run -d --name oracledb -e ORACLE_PWD=oracle -v ${{ github.workspace }}/tests/data/oracle/init-db:/opt/oracle/scripts/startup -p 1521:1521 container-registry.oracle.com/database/express:21.3.0-xe - name: Install requirements 📦 From 8637be55eeaa9cc2670d4307cf14a46f2a6bcffb Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 25 Oct 2023 17:39:16 +0200 Subject: [PATCH 25/35] Changed len of array to 11 --- tests/test_filesystem_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_filesystem_provider.py b/tests/test_filesystem_provider.py index a007380e2..84e916445 100644 --- a/tests/test_filesystem_provider.py +++ b/tests/test_filesystem_provider.py @@ -54,7 +54,7 @@ def test_query(config): r = p.get_data_path(baseurl, urlpath, dirpath) - assert len(r['links']) == 10 + assert len(r['links']) == 11 r = p.get_data_path(baseurl, urlpath, '/poi_portugal') From 38a6e8be9bb19c5bb43a7aec184de11fea5f03ee Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 8 Nov 2023 14:06:38 +0100 Subject: [PATCH 26/35] Use sdo_util.from_geojsonfor create and update --- pygeoapi/provider/oracle.py | 49 +++++++++++++++++++++++------------ tests/test_oracle_provider.py | 24 +++++++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 3e15c0fa6..ab32f258a 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -468,6 +468,7 @@ def query( :returns: GeoJSON FeaturesCollection """ + # Check mandatory filter properties property_dict = dict(properties) if self.mandatory_properties: @@ -843,24 +844,32 @@ def filter_binds(pair): columns_str = ", ".join([col for col in columns]) values_str = ", ".join([f":{col}" for col in columns]) - sql_query = f"INSERT INTO {self.table} (\ - {columns_str}, \ - {self.geom}) \ - VALUES ({values_str}, :in_geometry) \ - RETURNING {self.id_field} INTO :out_id" + sql_query = f""" + INSERT INTO {self.table} ( + {columns_str}, + {self.geom} + ) + VALUES ( + {values_str}, + sdo_util.from_geojson(:in_geometry, NULL, :srid) + ) + RETURNING {self.id_field} INTO :out_id + """ # Out bind variable for the id of the created row out_id = cursor.var(int) # Bind variable for the SDO_GEOMETRY type - in_geometry = self._get_sdo_from_geojson_geometry( - db.conn, request_data.get("geometry").get("coordinates")[0] - ) + # in_geometry = self._get_sdo_from_geojson_geometry( + # db.conn, request_data.get("geometry").get("coordinates")[0] + # ) + in_geometry = request_data.get("geometry") bind_variables = { **bind_variables, "out_id": out_id, - "in_geometry": in_geometry, + "in_geometry": json.dumps(in_geometry), + "srid": self.source_crs, } # SQL manipulation plugin @@ -936,20 +945,28 @@ def filter_binds(pair): set_str = ", ".join([f" {col} = :{col}" for col in columns]) - sql_query = f"UPDATE {self.table} \ - SET {set_str} \ - , {self.geom} = :in_geometry \ - WHERE {self.id_field} = :in_id" + sql_query = f""" + UPDATE {self.table} + SET {set_str} + , {self.geom} = sdo_util.from_geojson( + :in_geometry, + NULL, + :srid + ) + WHERE {self.id_field} = :in_id + """ # Bind variable for the SDO_GEOMETRY type - in_geometry = self._get_sdo_from_geojson_geometry( - db.conn, request_data.get("geometry").get("coordinates")[0] - ) + # in_geometry = self._get_sdo_from_geojson_geometry( + # db.conn, request_data.get("geometry").get("coordinates")[0] + # ) + in_geometry = json.dumps(request_data.get("geometry")) bind_variables = { **bind_variables, "in_id": identifier, "in_geometry": in_geometry, + "srid": self.source_crs } # SQL manipulation plugin diff --git a/tests/test_oracle_provider.py b/tests/test_oracle_provider.py index a19f110ee..7488231a2 100644 --- a/tests/test_oracle_provider.py +++ b/tests/test_oracle_provider.py @@ -226,6 +226,18 @@ def create_geojson(): } +@pytest.fixture() +def create_point_geojson(): + return { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [9.603316032965449, 47.48872063967191], + }, + "properties": {"name": "Yachthafen Fischerinsel", "wiki_link": None}, + } + + @pytest.fixture() def update_geojson(): return { @@ -533,3 +545,15 @@ def test_delete_sql_manipulator(config_manipulator, config): down = p2.query(sortby=[{"property": "id", "order": "-"}]) assert down["features"][0]["id"] == identifier + + +def test_create_point(config, create_point_geojson): + """Test simple create""" + p = OracleProvider(config) + result = p.create(create_point_geojson) + + assert result == 28 + + data = p.get(28) + + assert data.get("geometry").get("type") == "Point" From 1bc6223c073a30f43fb5fc05e65cfcfe2e5d7927 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 8 Nov 2023 14:10:14 +0100 Subject: [PATCH 27/35] Flake8 changes --- pygeoapi/provider/oracle.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index ab32f258a..4b424b832 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -468,7 +468,6 @@ def query( :returns: GeoJSON FeaturesCollection """ - # Check mandatory filter properties property_dict = dict(properties) if self.mandatory_properties: @@ -966,7 +965,7 @@ def filter_binds(pair): **bind_variables, "in_id": identifier, "in_geometry": in_geometry, - "srid": self.source_crs + "srid": self.source_crs, } # SQL manipulation plugin From f30b53accd8cb5f4dbdbaf9826dcd9ec3a1c1dcd Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 8 Nov 2023 14:14:05 +0100 Subject: [PATCH 28/35] Fixed error with views --- pygeoapi/provider/oracle.py | 57 +++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 4b424b832..7afdfd6e2 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -233,39 +233,50 @@ def _get_table_columns(self, schema, table): """ cur = self.conn.cursor() - sql = "SELECT COUNT(1) \ - FROM all_tables \ - WHERE table_name = UPPER(:table_name) \ - AND owner = UPPER(:owner)" + sql = """ + SELECT COUNT(1) + FROM all_objects + WHERE object_type IN ('VIEW','TABLE','MATERIALIZED VIEW') + AND object_name = UPPER(:table_name) + AND owner = UPPER(:owner) + """ cur.execute(sql, {"table_name": table, "owner": schema}) result = cur.fetchone() if result[0] == 0: - sql = "SELECT COUNT(1) \ - FROM all_synonyms \ - WHERE synonym_name = UPPER(:table_name) \ - AND owner = UPPER(:owner)" + sql = """ + SELECT COUNT(1) + FROM all_synonyms + WHERE synonym_name = UPPER(:table_name) + AND owner = UPPER(:owner) + """ cur.execute(sql, {"table_name": table, "owner": schema}) result = cur.fetchone() if result[0] == 0: - sql = "SELECT COUNT(1) \ - FROM all_synonyms \ - WHERE synonym_name = UPPER(:table_name) \ - AND owner = 'PUBLIC'" + sql = """ + SELECT COUNT(1) + FROM all_synonyms + WHERE synonym_name = UPPER(:table_name) + AND owner = 'PUBLIC' + """ cur.execute(sql, {"table_name": table}) result = cur.fetchone() if result[0] == 0: - raise ProviderGenericError("Table not found") + raise ProviderGenericError( + f"Table {schema}.{table} not found!" + ) else: schema = "PUBLIC" - sql = "SELECT table_owner, table_name \ - FROM all_synonyms \ - WHERE synonym_name = UPPER(:table_name) \ - AND owner = UPPER(:owner)" + sql = """ + SELECT table_owner, table_name + FROM all_synonyms + WHERE synonym_name = UPPER(:table_name) + AND owner = UPPER(:owner) + """ cur.execute(sql, {"table_name": table, "owner": schema}) result = cur.fetchone() @@ -273,11 +284,13 @@ def _get_table_columns(self, schema, table): table = result[1] # Get table column names and types, excluding geometry - query_cols = "select column_name, data_type \ - from all_tab_columns \ - where table_name = UPPER(:table_name) \ - and owner = UPPER(:owner) \ - and data_type != 'SDO_GEOMETRY'" + query_cols = """ + SELECT column_name, data_type + FROM all_tab_columns + WHERE table_name = UPPER(:table_name) + AND owner = UPPER(:owner) + AND data_type != 'SDO_GEOMETRY' + """ cur.execute(query_cols, {"table_name": table, "owner": schema}) result = cur.fetchall() From 34b18d8e1002ec3752e9f1a0a9cca423cf36ace3 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Tue, 21 Nov 2023 13:39:01 +0100 Subject: [PATCH 29/35] Added crs_transform_spec support --- pygeoapi/provider/oracle.py | 99 +++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 27 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 7afdfd6e2..9f255029b 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -30,9 +30,11 @@ import importlib import json import logging +import oracledb +import pyproj from typing import Optional -import oracledb +from pygeoapi.api import DEFAULT_STORAGE_CRS, DEFAULT_CRS from pygeoapi.provider.base import ( BaseProvider, @@ -43,6 +45,8 @@ ProviderQueryError, ) +from pygeoapi.util import get_crs_from_uri + LOGGER = logging.getLogger(__name__) @@ -313,28 +317,36 @@ def __init__(self, provider_def): super().__init__(provider_def) + # Table properties self.table = provider_def["table"] self.id_field = provider_def["id_field"] self.conn_dic = provider_def["data"] self.geom = provider_def["geom_field"] self.properties = [item.lower() for item in self.properties] + self.mandatory_properties = provider_def.get("mandatory_properties") + # SQL manipulator properties self.sql_manipulator = provider_def.get("sql_manipulator") self.sql_manipulator_options = provider_def.get( "sql_manipulator_options" ) - self.mandatory_properties = provider_def.get("mandatory_properties") - self.source_crs = provider_def.get("source_crs", 4326) - self.target_crs = provider_def.get("target_crs", 4326) + + # CRS properties + storage_crs_uri = provider_def.get("storage_crs", DEFAULT_STORAGE_CRS) + self.storage_crs = get_crs_from_uri(storage_crs_uri) + default_crs_uri = provider_def.get("default_crs", DEFAULT_CRS) + self.default_crs = get_crs_from_uri(default_crs_uri) + + # SDO properties self.sdo_mask = provider_def.get("sdo_mask", "anyinteraction") LOGGER.debug("Setting Oracle properties:") LOGGER.debug(f"Name:{self.name}") LOGGER.debug(f"ID_field:{self.id_field}") LOGGER.debug(f"Table:{self.table}") - LOGGER.debug(f"source_crs: {self.source_crs}") - LOGGER.debug(f"target_crs: {self.target_crs}") LOGGER.debug(f"sdo_mask: {self.sdo_mask}") + LOGGER.debug(f"storage_crs {self.storage_crs}") + LOGGER.debug(f"default_crs: {self.default_crs}") self.get_fields() @@ -381,7 +393,7 @@ def _get_where_clauses( sdo_mask = f"mask={sdo_mask}" bbox_dict["properties"] = { - "srid": bbox_crs or 4326, + "srid": self._get_srid_from_crs(bbox_crs), "minx": bbox[0], "miny": bbox[1], "maxx": bbox[2], @@ -446,6 +458,20 @@ def _output_type_handler( oracledb.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize ) + def _get_srid_from_crs(self, crs): + """ + Works only for EPSG codes! + Anything else is hard coded! + """ + if crs == "OGC:CRS84": + srid = 4326 + elif crs == "OGC:CRS84h": + srid = 4326 + else: + srid = crs.to_epsg() + + return srid + def query( self, offset=0, @@ -507,7 +533,7 @@ def query( where_dict = self._get_where_clauses( properties=properties, bbox=bbox, - bbox_crs=self.source_crs, + bbox_crs=self.storage_crs, sdo_mask=self.sdo_mask, ) @@ -548,26 +574,43 @@ def query( where_dict = self._get_where_clauses( properties=properties, bbox=bbox, - bbox_crs=self.source_crs, + bbox_crs=self.storage_crs, sdo_mask=self.sdo_mask, ) + # Get correct SRID + if crs_transform_spec is not None: + source_crs = pyproj.CRS.from_wkt( + crs_transform_spec.source_crs_wkt + ) + source_srid = self._get_srid_from_crs(source_crs) + + target_crs = pyproj.CRS.from_wkt( + crs_transform_spec.target_crs_wkt + ) + target_srid = self._get_srid_from_crs(target_crs) + else: + source_srid = self._get_srid_from_crs(self.storage_crs) + target_srid = self._get_srid_from_crs(self.default_crs) + + LOGGER.debug(f"source_srid: {source_srid}") + LOGGER.debug(f"target_srid: {target_srid}") + # Build geometry column call # When a different output CRS is definded, the geometry # geometry column would be transformed. if skip_geometry: geom = "" - elif ( - not skip_geometry - and self.target_crs - and self.target_crs != self.source_crs - ): - geom = f", sdo_cs.transform(t1.{self.geom}, \ - :target_srid).get_geojson() \ - AS geometry " + + elif not skip_geometry and source_srid != target_srid: + geom = f""", sdo_cs.transform(t1.{self.geom}, + :target_srid).get_geojson() + AS geometry """ + where_dict["properties"].update( - {"target_srid": int(self.target_crs)} + {"target_srid": int(target_srid)} ) + else: geom = f", t1.{self.geom}.get_geojson() AS geometry " @@ -717,13 +760,15 @@ def get(self, identifier, **kwargs): cursor = db.conn.cursor() crs_dict = {} - if self.target_crs and self.target_crs != self.source_crs: - geom_sql = f", sdo_cs.transform(t1.{self.geom}, \ - :target_srid).get_geojson() \ - AS geometry " - crs_dict = {"target_srid": int(self.target_crs)} - else: - geom_sql = f", t1.{self.geom}.get_geojson() AS geometry " + # TODO !!!!!! + # if self.target_crs and self.target_crs != self.source_crs: + # geom_sql = f", sdo_cs.transform(t1.{self.geom}, \ + # :target_srid).get_geojson() \ + # AS geometry " + # crs_dict = {"target_srid": int(self.target_crs)} + # else: + # geom_sql = f", t1.{self.geom}.get_geojson() AS geometry " + geom_sql = f", t1.{self.geom}.get_geojson() AS geometry " sql_query = f"SELECT {db.columns} {geom_sql} \ FROM {self.table} t1 \ @@ -881,7 +926,7 @@ def filter_binds(pair): **bind_variables, "out_id": out_id, "in_geometry": json.dumps(in_geometry), - "srid": self.source_crs, + "srid": self._get_srid_from_crs(self.storage_crs), } # SQL manipulation plugin @@ -978,7 +1023,7 @@ def filter_binds(pair): **bind_variables, "in_id": identifier, "in_geometry": in_geometry, - "srid": self.source_crs, + "srid": self._get_srid_from_crs(self.storage_crs), } # SQL manipulation plugin From 809d2249f682c5af9933a2cacb331529256a3025 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 22 Nov 2023 15:36:40 +0100 Subject: [PATCH 30/35] Without default_crs --- pygeoapi/provider/oracle.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 9f255029b..7cca72f00 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -334,8 +334,10 @@ def __init__(self, provider_def): # CRS properties storage_crs_uri = provider_def.get("storage_crs", DEFAULT_STORAGE_CRS) self.storage_crs = get_crs_from_uri(storage_crs_uri) - default_crs_uri = provider_def.get("default_crs", DEFAULT_CRS) - self.default_crs = get_crs_from_uri(default_crs_uri) + + # TODO See Issue #1393 + # default_crs_uri = provider_def.get("default_crs", DEFAULT_CRS) + # self.default_crs = get_crs_from_uri(default_crs_uri) # SDO properties self.sdo_mask = provider_def.get("sdo_mask", "anyinteraction") @@ -346,7 +348,9 @@ def __init__(self, provider_def): LOGGER.debug(f"Table:{self.table}") LOGGER.debug(f"sdo_mask: {self.sdo_mask}") LOGGER.debug(f"storage_crs {self.storage_crs}") - LOGGER.debug(f"default_crs: {self.default_crs}") + + # TODO See Issue #1393 + #LOGGER.debug(f"default_crs: {self.default_crs}") self.get_fields() @@ -591,7 +595,10 @@ def query( target_srid = self._get_srid_from_crs(target_crs) else: source_srid = self._get_srid_from_crs(self.storage_crs) - target_srid = self._get_srid_from_crs(self.default_crs) + target_srid = source_srid + + # TODO See Issue #1393 + # target_srid = self._get_srid_from_crs(self.default_crs) LOGGER.debug(f"source_srid: {source_srid}") LOGGER.debug(f"target_srid: {target_srid}") From 58343ce556675ee99f526d1160e3d746c764b6ca Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 22 Nov 2023 16:02:57 +0100 Subject: [PATCH 31/35] Updated documentation for Oracle Provider --- docs/source/data-publishing/ogcapi-features.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/source/data-publishing/ogcapi-features.rst b/docs/source/data-publishing/ogcapi-features.rst index 15ef4859b..dc61f7cdf 100644 --- a/docs/source/data-publishing/ogcapi-features.rst +++ b/docs/source/data-publishing/ogcapi-features.rst @@ -26,6 +26,7 @@ parameters. `GeoJSON`_,✅/✅,results/hits,❌,❌,❌,✅,❌,❌,✅ `MongoDB`_,✅/❌,results,✅,✅,✅,✅,❌,❌,✅ `OGR`_,✅/❌,results/hits,✅,❌,❌,✅,❌,❌,✅ + `Oracle`_,✅/✅,results/hits,✅,❌,✅,✅,❌,❌,✅ `PostgreSQL`_,✅/✅,results/hits,✅,✅,✅,✅,✅,❌,✅ `SQLiteGPKG`_,✅/❌,results/hits,✅,❌,❌,✅,❌,❌,✅ `SensorThings API`_,✅/✅,results/hits,✅,✅,✅,✅,❌,❌,✅ @@ -300,8 +301,6 @@ Oracle # foo: bar # mandatory_properties: # - bbox - # source_crs: 31287 # defaults to 4326 if not provided - # target_crs: 31287 # defaults to 4326 if not provided The provider supports connection over host and port with SID or SERVICE_NAME. For TNS naming, the system environment variable TNS_ADMIN or the configuration parameter tns_admin must be set. From 98ba17a78df7c1d387f01cc7cd877a7685cb5399 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 22 Nov 2023 16:19:19 +0100 Subject: [PATCH 32/35] changes for flake8 --- pygeoapi/provider/oracle.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 7cca72f00..685dd51ae 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -34,7 +34,7 @@ import pyproj from typing import Optional -from pygeoapi.api import DEFAULT_STORAGE_CRS, DEFAULT_CRS +from pygeoapi.api import DEFAULT_STORAGE_CRS from pygeoapi.provider.base import ( BaseProvider, @@ -334,7 +334,7 @@ def __init__(self, provider_def): # CRS properties storage_crs_uri = provider_def.get("storage_crs", DEFAULT_STORAGE_CRS) self.storage_crs = get_crs_from_uri(storage_crs_uri) - + # TODO See Issue #1393 # default_crs_uri = provider_def.get("default_crs", DEFAULT_CRS) # self.default_crs = get_crs_from_uri(default_crs_uri) @@ -348,9 +348,9 @@ def __init__(self, provider_def): LOGGER.debug(f"Table:{self.table}") LOGGER.debug(f"sdo_mask: {self.sdo_mask}") LOGGER.debug(f"storage_crs {self.storage_crs}") - + # TODO See Issue #1393 - #LOGGER.debug(f"default_crs: {self.default_crs}") + # LOGGER.debug(f"default_crs: {self.default_crs}") self.get_fields() From 1fd3eaca09ef57e0d5880e9053b30ec8cc16bb54 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 22 Nov 2023 16:57:02 +0100 Subject: [PATCH 33/35] Added crs_transform_spec support to get function --- pygeoapi/provider/oracle.py | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 685dd51ae..363178ff2 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -749,7 +749,7 @@ def _get_next(self, cursor, identifier): return id - def get(self, identifier, **kwargs): + def get(self, identifier, crs_transform_spec=None, **kwargs): """ Query the provider for a specific feature id e.g: /collections/ocrl_lakes/items/1 @@ -767,15 +767,41 @@ def get(self, identifier, **kwargs): cursor = db.conn.cursor() crs_dict = {} - # TODO !!!!!! - # if self.target_crs and self.target_crs != self.source_crs: - # geom_sql = f", sdo_cs.transform(t1.{self.geom}, \ - # :target_srid).get_geojson() \ - # AS geometry " - # crs_dict = {"target_srid": int(self.target_crs)} - # else: - # geom_sql = f", t1.{self.geom}.get_geojson() AS geometry " - geom_sql = f", t1.{self.geom}.get_geojson() AS geometry " + + # Get correct SRIDs + if crs_transform_spec is not None: + source_crs = pyproj.CRS.from_wkt( + crs_transform_spec.source_crs_wkt + ) + source_srid = self._get_srid_from_crs(source_crs) + + target_crs = pyproj.CRS.from_wkt( + crs_transform_spec.target_crs_wkt + ) + target_srid = self._get_srid_from_crs(target_crs) + + else: + source_srid = self._get_srid_from_crs(self.storage_crs) + target_srid = source_srid + + # TODO See Issue #1393 + # target_srid = self._get_srid_from_crs(self.default_crs) + + LOGGER.debug(f"source_srid: {source_srid}") + LOGGER.debug(f"target_srid: {target_srid}") + + # Build geometry column call + # When a different output CRS is definded, the geometry + # geometry column would be transformed. + if source_srid != target_srid: + crs_dict = {"target_srid": target_srid} + + geom_sql = f""", sdo_cs.transform(t1.{self.geom}, + :target_srid).get_geojson() + AS geometry """ + + else: + geom_sql = f", t1.{self.geom}.get_geojson() AS geometry " sql_query = f"SELECT {db.columns} {geom_sql} \ FROM {self.table} t1 \ From e2a881849f87e17153961d29d0aa7314ec8ca932 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Thu, 23 Nov 2023 17:20:43 +0100 Subject: [PATCH 34/35] review changes --- pygeoapi/provider/oracle.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index 363178ff2..a56cbaaa4 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -235,7 +235,6 @@ def _get_table_columns(self, schema, table): Lookup for public and private synonyms. Throws ProviderGenericError when table not exist or accesable. """ - cur = self.conn.cursor() sql = """ SELECT COUNT(1) @@ -244,8 +243,9 @@ def _get_table_columns(self, schema, table): AND object_name = UPPER(:table_name) AND owner = UPPER(:owner) """ - cur.execute(sql, {"table_name": table, "owner": schema}) - result = cur.fetchone() + with self.conn.cursor() as cur: + cur.execute(sql, {"table_name": table, "owner": schema}) + result = cur.fetchone() if result[0] == 0: sql = """ @@ -254,8 +254,9 @@ def _get_table_columns(self, schema, table): WHERE synonym_name = UPPER(:table_name) AND owner = UPPER(:owner) """ - cur.execute(sql, {"table_name": table, "owner": schema}) - result = cur.fetchone() + with self.conn.cursor() as cur: + cur.execute(sql, {"table_name": table, "owner": schema}) + result = cur.fetchone() if result[0] == 0: sql = """ @@ -264,8 +265,9 @@ def _get_table_columns(self, schema, table): WHERE synonym_name = UPPER(:table_name) AND owner = 'PUBLIC' """ - cur.execute(sql, {"table_name": table}) - result = cur.fetchone() + with self.conn.cursor() as cur: + cur.execute(sql, {"table_name": table}) + result = cur.fetchone() if result[0] == 0: raise ProviderGenericError( @@ -281,8 +283,9 @@ def _get_table_columns(self, schema, table): WHERE synonym_name = UPPER(:table_name) AND owner = UPPER(:owner) """ - cur.execute(sql, {"table_name": table, "owner": schema}) - result = cur.fetchone() + with self.conn.cursor() as cur: + cur.execute(sql, {"table_name": table, "owner": schema}) + result = cur.fetchone() schema = result[0] table = result[1] @@ -295,9 +298,9 @@ def _get_table_columns(self, schema, table): AND owner = UPPER(:owner) AND data_type != 'SDO_GEOMETRY' """ - - cur.execute(query_cols, {"table_name": table, "owner": schema}) - result = cur.fetchall() + with self.conn.cursor() as cur: + cur.execute(query_cols, {"table_name": table, "owner": schema}) + result = cur.fetchall() return result @@ -599,6 +602,8 @@ def query( # TODO See Issue #1393 # target_srid = self._get_srid_from_crs(self.default_crs) + # If issue is not accepted, this block can be merged with + # the following block. LOGGER.debug(f"source_srid: {source_srid}") LOGGER.debug(f"target_srid: {target_srid}") @@ -609,7 +614,7 @@ def query( if skip_geometry: geom = "" - elif not skip_geometry and source_srid != target_srid: + elif source_srid != target_srid: geom = f""", sdo_cs.transform(t1.{self.geom}, :target_srid).get_geojson() AS geometry """ @@ -786,6 +791,8 @@ def get(self, identifier, crs_transform_spec=None, **kwargs): # TODO See Issue #1393 # target_srid = self._get_srid_from_crs(self.default_crs) + # If issue is not accepted, this block can be merged with + # the following block. LOGGER.debug(f"source_srid: {source_srid}") LOGGER.debug(f"target_srid: {target_srid}") From fd21e9ee09b20d1c73cd8bfceca90541e0f63289 Mon Sep 17 00:00:00 2001 From: Andreas Kosubek Date: Wed, 29 Nov 2023 22:40:14 +0100 Subject: [PATCH 35/35] Added configurable SDO operator --- .../data-publishing/ogcapi-features.rst | 59 +++++++++- pygeoapi/provider/oracle.py | 110 +++++++++++++----- 2 files changed, 132 insertions(+), 37 deletions(-) diff --git a/docs/source/data-publishing/ogcapi-features.rst b/docs/source/data-publishing/ogcapi-features.rst index dc61f7cdf..6277362aa 100644 --- a/docs/source/data-publishing/ogcapi-features.rst +++ b/docs/source/data-publishing/ogcapi-features.rst @@ -275,6 +275,8 @@ Oracle .. note:: Requires Python package oracledb +Connection +"""""""""" .. code-block:: yaml providers: @@ -296,19 +298,64 @@ Oracle table: lakes geom_field: geometry title_field: name - # sql_manipulator: tests.test_oracle_provider.SqlManipulator - # sql_manipulator_options: - # foo: bar - # mandatory_properties: - # - bbox -The provider supports connection over host and port with SID or SERVICE_NAME. For TNS naming, the system +The provider supports connection over host and port with SID, SERVICE_NAME or TNS_NAME. For TNS naming, the system environment variable TNS_ADMIN or the configuration parameter tns_admin must be set. The providers supports external authentication. At the moment only wallet authentication is implemented. Sometimes it is necessary to use the Oracle client for the connection. In this case init_oracle_client must be set to True. +SDO options +""""""""""" +.. code-block:: yaml + + providers: + - type: feature + name: OracleDB + data: + host: 127.0.0.1 + port: 1521 + service_name: XEPDB1 + user: geo_test + password: geo_test + id_field: id + table: lakes + geom_field: geometry + title_field: name + sdo_operator: sdo_relate # defaults to sdo_filter + sdo_param: mask=touch+coveredby # defaults to mask=anyinteract + +The provider supports two different SDO operators, sdo_filter and sdo_relate. When not set, the default is sdo_relate! +Further more it is possible to set the sdo_param option. When sdo_relate is used the default is anyinteraction! +`See Oracle Documentation for details `_. + +Mandatory properties +"""""""""""""""""""" +.. code-block:: yaml + + providers: + - type: feature + name: OracleDB + data: + host: 127.0.0.1 + port: 1521 + service_name: XEPDB1 + user: geo_test + password: geo_test + id_field: id + table: lakes + geom_field: geometry + title_field: name + manadory_properties: + - example_group_id + +On large tables it could be useful to disallow a query on the complete dataset. For this reason it is possible to +configure mandatory properties. When this is activated, the provoder throws an exception when the parameter +is not in the query uri. + +Custom SQL Manipulator Plugin +""""""""""""""""""""""""""""" The provider supports a SQL-Manipulator-Plugin class. With this, the SQL statement could be manipulated. This is useful e.g. for authorization at row level or manipulation of the explain plan with hints. diff --git a/pygeoapi/provider/oracle.py b/pygeoapi/provider/oracle.py index a56cbaaa4..255dc3103 100644 --- a/pygeoapi/provider/oracle.py +++ b/pygeoapi/provider/oracle.py @@ -343,13 +343,15 @@ def __init__(self, provider_def): # self.default_crs = get_crs_from_uri(default_crs_uri) # SDO properties - self.sdo_mask = provider_def.get("sdo_mask", "anyinteraction") + self.sdo_param = provider_def.get("sdo_param") + self.sdo_operator = provider_def.get("sdo_operator", "sdo_filter") LOGGER.debug("Setting Oracle properties:") LOGGER.debug(f"Name:{self.name}") LOGGER.debug(f"ID_field:{self.id_field}") LOGGER.debug(f"Table:{self.table}") - LOGGER.debug(f"sdo_mask: {self.sdo_mask}") + LOGGER.debug(f"sdo_param: {self.sdo_param}") + LOGGER.debug(f"sdo_operator: {self.sdo_operator}") LOGGER.debug(f"storage_crs {self.storage_crs}") # TODO See Issue #1393 @@ -373,7 +375,12 @@ def get_fields(self): return self.fields def _get_where_clauses( - self, properties, bbox, bbox_crs, sdo_mask="anyinteraction" + self, + properties, + bbox, + bbox_crs, + sdo_param=None, + sdo_operator="sdo_filter", ): """ Generarates WHERE conditions to be implemented in query. @@ -397,33 +404,72 @@ def _get_where_clauses( if bbox: bbox_dict = {"clause": "", "properties": {}} - sdo_mask = f"mask={sdo_mask}" - - bbox_dict["properties"] = { - "srid": self._get_srid_from_crs(bbox_crs), - "minx": bbox[0], - "miny": bbox[1], - "maxx": bbox[2], - "maxy": bbox[3], - "sdo_mask": sdo_mask, - } + if sdo_operator == "sdo_relate": + if not sdo_param: + sdo_param = "mask=anyinteract" + + bbox_dict["properties"] = { + "srid": self._get_srid_from_crs(bbox_crs), + "minx": bbox[0], + "miny": bbox[1], + "maxx": bbox[2], + "maxy": bbox[3], + "sdo_param": sdo_param, + } + + bbox_query = f""" + sdo_relate({self.geom}, + mdsys.sdo_geometry(2003, + :srid, + NULL, + mdsys.sdo_elem_info_array( + 1, + 1003, + 3 + ), + mdsys.sdo_ordinate_array( + :minx, + :miny, + :maxx, + :maxy + ) + ), + :sdo_param + ) = 'TRUE' + """ - bbox_dict[ - "clause" - ] = f"sdo_relate({self.geom}, \ - mdsys.sdo_geometry(2003, \ - :srid, \ - NULL, \ - mdsys.sdo_elem_info_array(\ - 1, \ - 1003, \ - 3\ - ), \ - mdsys.sdo_ordinate_array(:minx, \ - :miny, \ - :maxx, \ - :maxy)), \ - :sdo_mask) = 'TRUE'" + else: + bbox_dict["properties"] = { + "srid": self._get_srid_from_crs(bbox_crs), + "minx": bbox[0], + "miny": bbox[1], + "maxx": bbox[2], + "maxy": bbox[3], + "sdo_param": sdo_param, + } + + bbox_query = f""" + sdo_filter({self.geom}, + mdsys.sdo_geometry(2003, + :srid, + NULL, + mdsys.sdo_elem_info_array( + 1, + 1003, + 3 + ), + mdsys.sdo_ordinate_array( + :minx, + :miny, + :maxx, + :maxy + ) + ), + :sdo_param + ) = 'TRUE' + """ + + bbox_dict["clause"] = bbox_query where_conditions.append(bbox_dict["clause"]) where_dict["properties"].update(bbox_dict["properties"]) @@ -541,7 +587,8 @@ def query( properties=properties, bbox=bbox, bbox_crs=self.storage_crs, - sdo_mask=self.sdo_mask, + sdo_param=self.sdo_param, + sdo_operator=self.sdo_operator, ) # Not dangerous to use self.table as substitution, @@ -582,7 +629,8 @@ def query( properties=properties, bbox=bbox, bbox_crs=self.storage_crs, - sdo_mask=self.sdo_mask, + sdo_param=self.sdo_param, + sdo_operator=self.sdo_operator, ) # Get correct SRID