View Ticket
Not logged in
Ticket Hash: 597fce55f2884668a24b591840162ffebda390e1
Title: Adapting addVectorLayerAuth to look for triggers for SpatialViews
Status: Closed Type: Feature_Request
Severity: Important Priority: Immediate
Subsystem: Resolution: Fixed
Last Modified: 2017-05-30 09:59:19
Version Found In: 4.5.0dev
User Comments:
mj10777 added on 2017-05-12 09:02:06:

Providers have problems in supporting writable SpatialViews properly

    Gdal/Ogr (still) does not have any support
    QGis does this incorrectly (looks if a trigger exists, but not what type)

Since gaiaGetVectorLayersList collects capability information about all Vector Layers,
information about what a writable SpatialView is capable of, should be included.
typedef struct gaiaLayerAuthInfos

after IsReadOnly:
/** Flag indicating if the Capabilities of the SpatialView supports Inserting: TRUE or FALSE */
int HasTriggerInsert;
/** Flag indicating if the Capabilities of the SpatialView supports Updating: TRUE or FALSE */
int HasTriggerUpdate;
/** Flag indicating if the Capabilities of the SpatialView supports Deleting: TRUE or FALSE */
int HasTriggerDelete;

addVectorLayerAuth

Add 'sqlite3 * handle,' to function parameters, which is called from:
    gaiaGetVectorLayersList_v4 and
    get_table_auth_legacy

after setting of IsReadOnly:
  auth->HasTriggerInsert = 0;
  auth->HasTriggerUpdate = 0;
  auth->HasTriggerDelete = 0;
  if ((lyr->LayerType == GAIA_VECTOR_VIEW) && (!auth->IsReadOnly))
  {
   auth->IsReadOnly = 1; // Claims to be a Writable SpatialView, we shall see ...
   sql =
    sqlite3_mprintf("SELECT "
     "(SELECT Exists(SELECT rootpage FROM  sqlite_master WHERE (type = 'trigger' AND Lower(tbl_name) = Lower(%Q) AND (instr(upper(sql),'INSTEAD OF INSERT') > 0)))), "
     "(SELECT Exists(SELECT rootpage FROM  sqlite_master WHERE (type = 'trigger' AND Lower(tbl_name) = Lower(%Q) AND (instr(upper(sql),'INSTEAD OF UPDATE') > 0)))), "
     "(SELECT Exists(SELECT rootpage FROM  sqlite_master WHERE (type = 'trigger' AND Lower(tbl_name) = Lower(%Q) AND (instr(upper(sql),'INSTEAD OF DELETE') > 0))))"
     , table_name,table_name,table_name);
   ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
   sqlite3_free (sql);
   if (ret == SQLITE_OK)
   {
    while ( sqlite3_step( stmt ) == SQLITE_ROW )
    {
     if ( sqlite3_column_type( stmt, 0 ) != SQLITE_NULL )
     {
      if ( sqlite3_column_int( stmt, 0 ) == 1 )
      {
       auth->HasTriggerInsert = 1;
       auth->IsReadOnly = 0; // Yes, this could be a functional Writable SpatialView
      }
     }
     if ( sqlite3_column_type( stmt, 1 ) != SQLITE_NULL )
     {
      if ( sqlite3_column_int( stmt, 1 ) == 1 )
      {
       auth->HasTriggerUpdate = 1;
       auth->IsReadOnly = 0; // Yes, this could be a functional Writable SpatialView
      }
     }
     if ( sqlite3_column_type( stmt, 2 ) != SQLITE_NULL )
     {
      if ( sqlite3_column_int( stmt, 2 ) == 1 )
      {
       auth->HasTriggerDelete = 1;
       auth->IsReadOnly = 0; // Yes, this could be a functional Writable SpatialView
      }
     }
    }
    ret = sqlite3_finalize (stmt);
   }
  }

Should deal with this matter in a way that Providers could rely upon
(As always with no guarantee that the trigger works correctly).


sandro added on 2017-05-13 12:07:46:
patch accepted and immediately available from the Fossil repo.

mj10777 added on 2017-05-13 14:49:32:

2 corrections:

    has_trigger_insert was forgotten (2x has_trigger_delete)
    the && should be changed to ||, because if (at least) one is true - then the view is writable.
    if (*has_trigger_delete && *has_trigger_update && *has_trigger_delete)
    if (*has_trigger_insert || *has_trigger_update || *has_trigger_delete)


sandro added on 2017-05-13 15:41:53:
Yes I agree, a View supporting at least one between INSERT, UPDATE or DELETE technically speaking is a Writable View.

Anyway this patch is mainly intended to support the QGIS own data provider, and I easily imagine that in this specific context a set of Triggers only implementing an incomplete write support (i.e. INSERT and/or UPDATE but no DELETE) will very probably cause havoc.

mj10777 added on 2017-05-13 15:55:38:

if readonly is TRUE, no further checking is done. if readonly is FALSE, the 3 settings are checked and the appropriate Capabilities set:

  if ( mViewBased  &&  !mReadOnly )
  {
    // enabling editing for Spatialview when the corresponding TRIGGER exists
    if ( mTriggerDelete )
    {
      mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures | QgsVectorDataProvider::FastTruncate;
    }
    if ( mTriggerUpdate )
    {
      mEnabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
      mEnabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
    }
    if ( mTriggerInsert )
    {
      mEnabledCapabilities |= QgsVectorDataProvider::AddFeatures;
      mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes;
      mEnabledCapabilities |= QgsVectorDataProvider::CreateAttributeIndex;
    }
  }
In this way, when mTriggerInsert == FALSE, the gui will not allow new geometries to be created
    but will allow existing geometries to be changed when mTriggerUpdate == TRUE.


sandro added on 2017-05-14 09:27:03:
all right, this definitely clarifies what is the implementation expected by the QGIS data provider.

mj10777 added on 2017-05-26 09:18:41:

For SpatialViews, addVectorLayerAuth is never called

    Cause:
      SpatialViews are not contained in vector_layers_auth.
      Legacy: SpatialViews are not contained in geometry_columns_auth

Legacy:
    Add logic in get_view_layers_legacy after calling addVectorLayer
      addVectorLayerAuth(handle, list, table_name, geometry_column, 1, 0);

gaiaGetVectorLayersList_v4:
    Check returned result of layer_type, if at least one SpatialView is returned, if yes:
      after the call to addVectorLayerAuth for SpatialTables
    Do the same for SpatialViews

API-Documentation should be added for gaiaGetVectorLayersList
    that addLayerAttributeField will not be called if table==NULL
Took me a long time to find the
    if no "table" is set, we'll never return AttributeField Infos
comment.


sandro added on 2017-05-30 09:59:19:
supposed to be fixed by the latest commit.
please test if it now correctly works.