[
https://issues.apache.org/jira/browse/FINERACT-2468?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Farooq Ayoade updated FINERACT-2468:
------------------------------------
Description:
On PostgreSQL when using a *non-{{{}public{}}} schema* (e.g. tenant-specific
schema), creating a new datatable causes:
* *GET /datatables* to return *404* with "Datatable not found" for the new
datatable and break the entire list response
* Saving data to the new datatable to fail with "Datatable not found"
* Recovery only possible by deleting the problematic datatable
h2. Steps to reproduce
# Configure Fineract with PostgreSQL and a tenant that uses a
*non-{{{}public{}}} schema* (e.g. schema name = tenant identifier).
# Create a new datatable via POST /datatables
# Call *GET /datatables* (or GET /datatables/ with no filter)
h2. Root cause
In {*}{{fineract-core}}{*}, class {*}{{PostgreSQLQueryService}}{*}:
* *{{getTableColumns()}}* correctly uses *{{current_schema}}* (schema of the
current connection), so the table is found when it lives in the tenant schema.
* *{{getTableIndexes()}}* and *{{isTablePresent()}}* hardcode *{{'public'}}*
for the schema.
When listing datatables, the flow is:
# {{DatatableReadServiceImpl.retrieveDatatableNames()}} loads all permitted
datatables from {{{}x_registered_table{}}}.
# For each datatable it calls
{{{}GenericDataServiceImpl.fillResultsetColumnHeaders(datatableName){}}}.
# {{fillResultsetColumnHeaders()}} calls:
** {{getTableMetaData(tableName)}} → uses {{getTableColumns()}} → *finds the
table* (current_schema).
** {{getDatatableIndexData(tableName)}} → uses {{getTableIndexes()}} → looks
only in *{{public}}* → finds no rows → throws {{IllegalArgumentException("Table
... is not found")}} → mapped to {*}DatatableNotFoundException{*}.
So the table exists in the tenant schema, but the index query looks in
{{public}} and fails, breaking GET /datatables and any operation that needs
index metadata for that datatable.
*Stack trace (relevant part):*
{{org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException:
Datatable not found.
at
org.apache.fineract.infrastructure.dataqueries.service.GenericDataServiceImpl.getDatatableIndexData(GenericDataServiceImpl.java:299)
at
org.apache.fineract.infrastructure.dataqueries.service.GenericDataServiceImpl.fillResultsetColumnHeaders(GenericDataServiceImpl.java:98)
at
org.apache.fineract.infrastructure.dataqueries.service.DatatableReadServiceImpl.retrieveDatatableNames(DatatableReadServiceImpl.java:93)
at
org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource.getDatatables(DatatablesApiResource.java:92)}}
h2. Fix
*File:*
{{fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java}}
Use *{{current_schema}}* instead of *{{'public'}}* so behavior is consistent
with {{getTableColumns()}} and works with tenant/custom schemas.
h3. 1. {{getTableIndexes}}
*Before:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{String sql = "SELECT indexname FROM pg_indexes WHERE schemaname = 'public'
AND tablename = ?";}}
*After:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{// Use current_schema so tenant/schema-specific tables (e.g. datatables) are
found;
// hardcoding 'public' breaks GET /datatables and datatable operations in
multi-tenant setups.
String sql = "SELECT indexname FROM pg_indexes WHERE schemaname =
current_schema AND tablename = ?";}}
h3. 2. {{isTablePresent}}
*Before:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{Integer result = jdbcTemplate.queryForObject(
"SELECT COUNT(table_name) FROM information_schema.tables " + "WHERE
table_schema = 'public' AND table_name = ?",
Integer.class, tableName);}}
*After:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{// Use current_schema for consistency with getTableColumns/getTableIndexes
(tenant/schema-aware).
Integer result = jdbcTemplate.queryForObject(
"SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema =
current_schema AND table_name = ?",
Integer.class, tableName);}}
h2. Verification
* With PostgreSQL and a non-{{{}public{}}} schema (e.g. tenant schema):
** Create a new datatable.
** GET /datatables returns 200 and includes the new datatable.
** Saving data to the new datatable succeeds.
* With single-tenant {{public}} schema, behaviour unchanged
({{{}current_schema{}}} is {{{}public{}}}).
was:
On PostgreSQL when using a *non-{{{}public{}}} schema* (e.g. tenant-specific
schema), creating a new datatable causes:
* *GET /datatables* to return *404* with "Datatable not found" for the new
datatable and break the entire list response
* Saving data to the new datatable to fail with "Datatable not found"
* Recovery only possible by deleting the problematic datatable
h2. Steps to reproduce
# Configure Fineract with PostgreSQL and a tenant that uses a
*non-{{{}public{}}} schema* (e.g. schema name = tenant identifier).
# Create a new datatable via POST /datatables
# Call *GET /datatables* (or GET /datatables/ with no filter)
h2. Root cause
In {*}{{fineract-core}}{*}, class {*}{{PostgreSQLQueryService}}{*}:
* *{{getTableColumns()}}* correctly uses *{{current_schema}}* (schema of the
current connection), so the table is found when it lives in the tenant schema.
* *{{getTableIndexes()}}* and *{{isTablePresent()}}* hardcode *{{'public'}}*
for the schema.
When listing datatables, the flow is:
# {{DatatableReadServiceImpl.retrieveDatatableNames()}} loads all permitted
datatables from {{{}x_registered_table{}}}.
# For each datatable it calls
{{{}GenericDataServiceImpl.fillResultsetColumnHeaders(datatableName){}}}.
# {{fillResultsetColumnHeaders()}} calls:
** {{getTableMetaData(tableName)}} → uses {{getTableColumns()}} → *finds the
table* (current_schema).
** {{getDatatableIndexData(tableName)}} → uses {{getTableIndexes()}} → looks
only in *{{public}}* → finds no rows → throws {{IllegalArgumentException("Table
... is not found")}} → mapped to {*}DatatableNotFoundException{*}.
So the table exists in the tenant schema, but the index query looks in
{{public}} and fails, breaking GET /datatables and any operation that needs
index metadata for that datatable.
*Stack trace (relevant part):*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException:
Datatable not found.
at
org.apache.fineract.infrastructure.dataqueries.service.GenericDataServiceImpl.getDatatableIndexData(GenericDataServiceImpl.java:299)
at
org.apache.fineract.infrastructure.dataqueries.service.GenericDataServiceImpl.fillResultsetColumnHeaders(GenericDataServiceImpl.java:98)
at
org.apache.fineract.infrastructure.dataqueries.service.DatatableReadServiceImpl.retrieveDatatableNames(DatatableReadServiceImpl.java:93)
at
org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource.getDatatables(DatatablesApiResource.java:92)}}
h2. Fix
*File:*
{{fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java}}
Use *{{current_schema}}* instead of *{{'public'}}* so behavior is consistent
with {{getTableColumns()}} and works with tenant/custom schemas.
h3. 1. {{getTableIndexes}}
*Before:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{String sql = "SELECT indexname FROM pg_indexes WHERE schemaname = 'public'
AND tablename = ?";}}
*After:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{// Use current_schema so tenant/schema-specific tables (e.g. datatables) are
found;
// hardcoding 'public' breaks GET /datatables and datatable operations in
multi-tenant setups.
String sql = "SELECT indexname FROM pg_indexes WHERE schemaname =
current_schema AND tablename = ?";}}
h3. 2. {{isTablePresent}}
*Before:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{Integer result = jdbcTemplate.queryForObject(
"SELECT COUNT(table_name) FROM information_schema.tables " + "WHERE
table_schema = 'public' AND table_name = ?",
Integer.class, tableName);}}
*After:*
!http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
{{// Use current_schema for consistency with getTableColumns/getTableIndexes
(tenant/schema-aware).
Integer result = jdbcTemplate.queryForObject(
"SELECT COUNT(table_name) FROM information_schema.tables WHERE
table_schema = current_schema AND table_name = ?",
Integer.class, tableName);}}
h2. Verification
* With PostgreSQL and a non-{{{}public{}}} schema (e.g. tenant schema):
** Create a new datatable.
** GET /datatables returns 200 and includes the new datatable.
** Saving data to the new datatable succeeds.
* With single-tenant {{public}} schema, behaviour unchanged
({{{}current_schema{}}} is {{{}public{}}}).
> Datatable not found / GET /datatables 404 on PostgreSQL with non-public schema
> ------------------------------------------------------------------------------
>
> Key: FINERACT-2468
> URL: https://issues.apache.org/jira/browse/FINERACT-2468
> Project: Apache Fineract
> Issue Type: Bug
> Components: Data Tables
> Reporter: Farooq Ayoade
> Priority: Major
>
> On PostgreSQL when using a *non-{{{}public{}}} schema* (e.g. tenant-specific
> schema), creating a new datatable causes:
> * *GET /datatables* to return *404* with "Datatable not found" for the new
> datatable and break the entire list response
> * Saving data to the new datatable to fail with "Datatable not found"
> * Recovery only possible by deleting the problematic datatable
> h2. Steps to reproduce
> # Configure Fineract with PostgreSQL and a tenant that uses a
> *non-{{{}public{}}} schema* (e.g. schema name = tenant identifier).
> # Create a new datatable via POST /datatables
> # Call *GET /datatables* (or GET /datatables/ with no filter)
> h2. Root cause
> In {*}{{fineract-core}}{*}, class {*}{{PostgreSQLQueryService}}{*}:
> * *{{getTableColumns()}}* correctly uses *{{current_schema}}* (schema of the
> current connection), so the table is found when it lives in the tenant schema.
> * *{{getTableIndexes()}}* and *{{isTablePresent()}}* hardcode *{{'public'}}*
> for the schema.
> When listing datatables, the flow is:
> # {{DatatableReadServiceImpl.retrieveDatatableNames()}} loads all permitted
> datatables from {{{}x_registered_table{}}}.
> # For each datatable it calls
> {{{}GenericDataServiceImpl.fillResultsetColumnHeaders(datatableName){}}}.
> # {{fillResultsetColumnHeaders()}} calls:
> ** {{getTableMetaData(tableName)}} → uses {{getTableColumns()}} → *finds the
> table* (current_schema).
> ** {{getDatatableIndexData(tableName)}} → uses {{getTableIndexes()}} → looks
> only in *{{public}}* → finds no rows → throws
> {{IllegalArgumentException("Table ... is not found")}} → mapped to
> {*}DatatableNotFoundException{*}.
> So the table exists in the tenant schema, but the index query looks in
> {{public}} and fails, breaking GET /datatables and any operation that needs
> index metadata for that datatable.
> *Stack trace (relevant part):*
>
> {{org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException:
> Datatable not found.
> at
> org.apache.fineract.infrastructure.dataqueries.service.GenericDataServiceImpl.getDatatableIndexData(GenericDataServiceImpl.java:299)
> at
> org.apache.fineract.infrastructure.dataqueries.service.GenericDataServiceImpl.fillResultsetColumnHeaders(GenericDataServiceImpl.java:98)
> at
> org.apache.fineract.infrastructure.dataqueries.service.DatatableReadServiceImpl.retrieveDatatableNames(DatatableReadServiceImpl.java:93)
> at
> org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource.getDatatables(DatatablesApiResource.java:92)}}
> h2. Fix
> *File:*
> {{fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java}}
> Use *{{current_schema}}* instead of *{{'public'}}* so behavior is consistent
> with {{getTableColumns()}} and works with tenant/custom schemas.
> h3. 1. {{getTableIndexes}}
> *Before:*
> !http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
> {{String sql = "SELECT indexname FROM pg_indexes WHERE schemaname = 'public'
> AND tablename = ?";}}
> *After:*
> !http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
> {{// Use current_schema so tenant/schema-specific tables (e.g. datatables)
> are found;
> // hardcoding 'public' breaks GET /datatables and datatable operations in
> multi-tenant setups.
> String sql = "SELECT indexname FROM pg_indexes WHERE schemaname =
> current_schema AND tablename = ?";}}
> h3. 2. {{isTablePresent}}
> *Before:*
> !http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
> {{Integer result = jdbcTemplate.queryForObject(
> "SELECT COUNT(table_name) FROM information_schema.tables " + "WHERE
> table_schema = 'public' AND table_name = ?",
> Integer.class, tableName);}}
> *After:*
> !http://localhost:63342/markdownPreview/2018793464/docs?_ijt=2fh99ghqni20rs3r3bq41b8r3p!
> {{// Use current_schema for consistency with getTableColumns/getTableIndexes
> (tenant/schema-aware).
> Integer result = jdbcTemplate.queryForObject(
> "SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema =
> current_schema AND table_name = ?",
> Integer.class, tableName);}}
> h2. Verification
> * With PostgreSQL and a non-{{{}public{}}} schema (e.g. tenant schema):
> ** Create a new datatable.
> ** GET /datatables returns 200 and includes the new datatable.
> ** Saving data to the new datatable succeeds.
> * With single-tenant {{public}} schema, behaviour unchanged
> ({{{}current_schema{}}} is {{{}public{}}}).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)