Bug 130564 - ODBC: Unable to create view through Base-GUI, if no view is created before
Summary: ODBC: Unable to create view through Base-GUI, if no view is created before
Status: NEW
Alias: None
Product: LibreOffice
Classification: Unclassified
Component: Base (show other bugs)
Version:
(earliest affected)
6.1.5.2 release
Hardware: x86-64 (AMD64) Linux (All)
: medium normal
Assignee: Not Assigned
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-02-10 14:53 UTC by Robert Großkopf
Modified: 2024-02-17 10:31 UTC (History)
3 users (show)

See Also:
Crash report or crash signature:


Attachments
View execution image (35.01 KB, image/png)
2022-02-13 05:28 UTC, Stang
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Robert Großkopf 2020-02-10 14:53:51 UTC
Connect to a postgreSQL-database through ODBC.
Try to create a view in the table-pane.
Try Insert > View Design - will be greyed out.
Try Insert > View (Simple) - also greyed out.

Now create a view in the database through Tools > SQL:
CREATE VIEW "public"."tview" AS SELECT * FROM "public"."table1"

The view will be created. You could see this after setting View > Refresh Tables.

Close the *.odb-file after saving it.
Reopen the *.odb-file.

Now the possibility appears to create a view through the GUI of LO. You will see in the table-pane "Create View" and all the other possibilities are available.
Comment 1 Alex Thurgood 2020-02-12 09:55:22 UTC
I imagine that this is linked to the problem identified in:

https://bugs.documentfoundation.org/show_bug.cgi?id=124531#c7
Comment 2 Julien Nabet 2020-02-13 22:45:01 UTC
On pc Debian x86-64 with master sources updated today, I could reproduce this.
Comment 3 Julien Nabet 2020-02-13 22:51:54 UTC
Indeed, we enter the "if" block
if ( aReturn.bEnabled )
{
    Reference<XViewsSupplier> xViewsSup( getConnection(), UNO_QUERY );
    aReturn.bEnabled = xViewsSup.is();
}
but xViewsSup.is() returns false.

Whereas we use the same odbc implementation in LO, I don't reproduce this with an ODBC connection to a Firebird database. Perhaps not related to LO but to ODBC postgresql driver?
Comment 4 QA Administrators 2022-02-13 03:35:14 UTC Comment hidden (obsolete)
Comment 5 Stang 2022-02-13 05:28:30 UTC
Created attachment 178250 [details]
View execution image
Comment 6 Stang 2022-02-13 05:31:34 UTC
Sorry image added to wrong bug. S/B tdf#124531
will post there
Comment 7 Robert Großkopf 2022-02-13 07:11:21 UTC
Bug is still the same in LO 7.3.0.3 und OpenSUSE 15.3 64bit rpm Linux.

You couldn't create a view in PostgreSQL through ODBC (and direct connection) in GUI. You have to create a vie in Tools → SQL before.

JDBC-connector shows the possibility to create a view directly.
Comment 8 Julien Nabet 2022-02-13 11:44:07 UTC
It's not specific to Postgresql via ODBC, I could reproduce this too with MariaDB.
So I suppose it's rather specific to ODBC.
Comment 9 Julien Nabet 2022-02-15 20:47:12 UTC
After some debugging, here's a code pointer which explains why the creating a view option only shows when a view exists:
    306         // check if we support types
    307         if ( xMeta.is() )
    308         {
    309             Reference<XResultSet> xRes = xMeta->getTableTypes();
    310             if(xRes.is())
    311             {
    312                 Reference<XRow> xRow(xRes,UNO_QUERY);
    313                 while(xRes->next())
    314                 {
    315                     OUString sValue = xRow->getString(1);
    316                     if( !xRow->wasNull() && sValue == "VIEW")
    317                     {
    318                         m_bSupportsViews = true;
    319                         break;
    320                     }
    321                 }
    322             }
    323             // some dbs don't support this type so we should ask if a XViewsSupplier is supported
    324             if(!m_bSupportsViews)
    325             {
    326                 Reference< XViewsSupplier > xMaster(getMasterTables(),UNO_QUERY);
    327 
    328                 if (xMaster.is() && xMaster->getViews().is())
    329                     m_bSupportsViews = true;
    330             }
    331             if(m_bSupportsViews)
    332             {
    333                 m_pViews.reset( new OViewContainer(*this, m_aMutex, this, bCase, this, m_nInAppend) );
    334                 m_pViews->addContainerListener(m_pTables.get());
    335                 m_pTables->addContainerListener(m_pViews.get());
    336             }
see https://opengrok.libreoffice.org/xref/core/dbaccess/source/core/dataaccess/connection.cxx?r=bc3a0205#307

getTableTypes is implemented for each database connector so also for ODBC.
Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes(  )
{
    Reference< XResultSet > xRef;
    try
    {
        rtl::Reference<ODatabaseMetaDataResultSet> pResult = new ODatabaseMetaDataResultSet(m_pConnection);
        xRef = pResult;
        pResult->openTablesTypes();
    }
    catch(SQLException&)
    {
        xRef = new ::connectivity::ODatabaseMetaDataResultSet(::connectivity::ODatabaseMetaDataResultSet::eTableTypes);
    }
    return xRef;
}
See https://opengrok.libreoffice.org/xref/core/connectivity/source/drivers/odbc/ODatabaseMetaData.cxx?r=dffe9495#715

It uses "openTablesTypes"
void ODatabaseMetaDataResultSet::openTablesTypes( )
{
    SQLRETURN nRetcode = N3SQLTables(m_aStatementHandle,
                            nullptr,0,
                            nullptr,0,
                            nullptr,0,
                            reinterpret_cast<SDB_ODBC_CHAR *>(const_cast<char *>(SQL_ALL_TABLE_TYPES)),SQL_NTS);
    OTools::ThrowException(m_pConnection.get(),nRetcode,m_aStatementHandle,SQL_HANDLE_STMT,*this);

    m_aColMapping.clear();
    m_aColMapping.push_back(-1);
    m_aColMapping.push_back(4);
    m_xMetaData = new OResultSetMetaData(m_pConnection.get(),m_aStatementHandle,std::vector(m_aColMapping));
    checkColumnCount();
}
See https://opengrok.libreoffice.org/xref/core/connectivity/source/drivers/odbc/ODatabaseMetaDataResultSet.cxx?r=7c3990c3#872

The pb is the ODBC function used (https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqltables-function?view=sql-server-ver15) retrieves types from existing elements not types from every elements possible in a database like original "getTableTypes" from JDBC (see https://docs.oracle.com/javase/7/docs/api/java/sql/DatabaseMetaData.html#getTableTypes()).
So if there's no existing VIEW in the database (Mysql, Postgresql, whatever), the first method never goes in line 318 ( m_bSupportsViews = true;)

So LO tries another way by calling getMasterTables() and when digging a bit, it tries to call getDataDefinitionByURLAndConnection()
(See https://opengrok.libreoffice.org/xref/core/connectivity/source/commontools/dbtools2.cxx?r=d8538d7f#660)
    660 Reference< XTablesSupplier> getDataDefinitionByURLAndConnection(
    661             const OUString& _rsUrl,
    662             const Reference< XConnection>& _xConnection,
    663             const Reference< XComponentContext >& _rxContext)
    664 {
    665     Reference< XTablesSupplier> xTablesSup;
    666     try
    667     {
    668         Reference< XDriverManager2 > xManager = DriverManager::create( _rxContext );
    669         Reference< XDataDefinitionSupplier > xSupp( xManager->getDriverByURL( _rsUrl ), UNO_QUERY );
    670 
    671         if ( xSupp.is() )
    672         {
    673             xTablesSup = xSupp->getDataDefinitionByConnection( _xConnection );
    674             OSL_ENSURE(xTablesSup.is(),"No table supplier!");
    675         }
    676     }
    677     catch( const Exception& )
    678     {
    679         DBG_UNHANDLED_EXCEPTION("connectivity.commontools");
    680     }
    681     return xTablesSup;
    682 }


and finally since ODBC connector in LO doesn't implement XDataDefinitionSupplier, it doesn't go into the "if" and returns empty ref "xTablesSup".

So now, what to do?
Taking a look how to implement "XDataDefinitionSupplier", it seems we need catalog part, which needs tables part and perhaps views part.

I tried this with Mysql direct connector, it takes some time and perhaps there are regressions in what I did. Moreover, calling ODBC functions is less easy than looking into INFORMATION_SCHEMA.

A radical (and I suppose wrong) workaround would be in ODatabaseMetaData::getTableTypes from odbc part to just return "VIEW" (in a Reference< XResultSet >) since getTableTypes is only used to know if "VIEW" concept is known.
It would mean we'd consider every database which can be accessed from ODBC driver would support VIEWS (!!).

Lionel: if you've got some idea here, don't hesitate! :-)
Comment 10 Lionel Elie Mamane 2022-02-16 11:37:44 UTC
Call
  SQLGetInfo(handle, SQL_CREATE_VIEW, &result, ...)
Assuming a "good" ODBC driver, the database supports views if and only if
  (result & SQL_CV_CREATE_VIEW)


to test whether the database supports creations of views.

Documentation from https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetinfo-function

An SQLUINTEGER bitmask enumerating the clauses in the CREATE VIEW statement, as defined in SQL-92, supported by the data source.

The following bitmasks are used to determine which clauses are supported:
SQL_CV_CREATE_VIEW
SQL_CV_CHECK_OPTION
SQL_CV_CASCADED
SQL_CV_LOCAL

A return value of "0" means that the CREATE VIEW statement is not supported.

An SQL-92 Entry level-conformant driver will always return the SQL_CV_CREATE_VIEW and SQL_CV_CHECK_OPTION options as supported.

An SQL-92 Full level-conformant driver will always return all of these options as supported.
Comment 11 Julien Nabet 2022-02-16 19:54:19 UTC
(In reply to Lionel Elie Mamane from comment #10)
> Call
>   SQLGetInfo(handle, SQL_CREATE_VIEW, &result, ...)
> Assuming a "good" ODBC driver, the database supports views if and only if
>   (result & SQL_CV_CREATE_VIEW)
> ...

Thank you Lionel for your feedback!
I began to implement this in https://gerrit.libreoffice.org/c/core/+/130052 but don't understand at all ODatabaseMetaDataResultSet, especially m_aColMapping mechanism.
It would be so easier getTableTypes() just returns an std::vector with known types for a database instead of all these complicated (at least I hope useful) mechanisms.
Comment 12 QA Administrators 2024-02-17 03:13:06 UTC Comment hidden (obsolete)
Comment 13 Robert Großkopf 2024-02-17 10:31:28 UTC
Bug is still the same in LO 24.2.0.3 on OpenSUSE 15.4 64bit rpm Linux.