On Thursday, 6 August 2015 20:12:05 UTC+2, Richard Males wrote:
Mark/Sandro:
Thank you for your quick responses.
I am a little unclear on some things in your replies:
1) Using QGIS 2.8 Wien, I most definitely was able to attach the spatialite database with the created spatial view and then add a feature, which did propagate back to the two tables in the database from which the view was created. This was exactly the behavior I was looking for. I just did this again moments ago to verify the behavior, it worked as desired. Are you telling me that this is really unsupported behavior at present?
If QGIS is using gdal-org for this, yes it very strange.
- gdal is used when:
-- 'Add Vector layer' or you drag a Database into QGIS and drop it
--- after loading, right Mouse on the geometry in the Tree: and the 'Toggle Editing' should be OFF for ALL Views
---> QgsOgrProvider::capabilities() is called when loading for 'SequentialWrite' [adding], 'DeleteFeature' [delete] and 'RandomWrite' [update]
----> this called the gdal-ogr function: TestCapability() [present 2.0 version]
-----> OGRSQLiteViewLayer will always return FALSE for these values
-----> OGRSQLiteTableLayer will always check for any errors and in most cases return TRUE for all 3
- for the planned 'OGRSQLiteVectorLayer' class
-- in function 'EstablishFeatureDefn', when it a View
--- it will check for existing TRIGGERS
SELECT 1 FROM sqlite_master WHERE ((type = 'trigger' AND tbl_name='%s') AND (sql LIKE '%%INSTEAD OF INSERT%%'))
SELECT 1 FROM sqlite_master WHERE ((type = 'trigger' AND tbl_name='%s') AND (sql LIKE '%%INSTEAD OF UPDATE%%'))
SELECT 1 FROM sqlite_master WHERE ((type = 'trigger' AND tbl_name='%s') AND (sql LIKE '%%INSTEAD OF DELETE%%'))
storing the result.
TestCapability() will then return this result.
In QGIS, if one of the 3 are true
- then 'Toggle Editing' will be activated
-- depending on the result of ADD,UPDATE or DELETE
--- the corresponding buttons will be activated
---
With 'Add Spatialite layer'
- this may work, but I cannot say for sure - since I rarely use it
-- this seems to use: QgsSpatiaLiteProvider
--- there, for views, the existence of TRIGGERS is queried (QgsSpatiaLiteProvider::hasTriggers())
---> if a least 1 is found, 'readonly' is set to false
---> thus allowing you to INSERT, DELETE, UPDATE even if the corresponding TRIGGER is missing
- this may be outdated, since I am using the source code of 2014-02-22 (v 2.1.0)
-- after the 2014-02-22 version
-- when updating the screen (moving in any way) a terrible flickering happens, which I find hard for the eyes
2) Is the creation of triggers something that happens automatically when using the spatialite GUI to create the read/write view? If so, is there any way to see what is going on 'behind the scenes' when I create the view from the GUI?
For spatialite_gui: yes, it builds the needed 'CREATE TRIGGER' sql-commands
- it uses a naming convention for the created triggers:
-- vw_upd_* for 'INSTEAD OF UPDATE'
-- vw_ins_* for 'INSTEAD OF INSERT'
-- vw_del_* for 'INSTEAD OF DELETE'
My scripts are also based on these conventions, but are not binding
so in spatialite_gui you will see these in the treeview inside the view after the field definitions
- 'Show trigger' will show you the sql used to create the trigger
I have attached a sql-script that
- created a gcp table
-- which is intended to collect known points for geo-referencing
-- there are different types
--- begin of street, end of street, street intersection, bridges etc
-- there valid_since, valid_until: to show only points that existed at a specific time
--- bridges that were moved to a new position at a specific date, so when geo-referencing a map of a specific date you can show only those points that were then valid
-- gcp_order
For the different types there are views
- 'vw_ins_gcp_points_street_end'
-- these define points that are the end of the street
--- if the street is extended, the valid_until will have a end-date for the old point and for the new a valid_since
--- on an INSERT the gcp_type is set to the correct value (here 102) [the TRIGGER will set this value during an insert]
--- there are 2 fields showing the position in clear text (map_x,map_y)
---- on INSERT or UPDATE these a (re-)set with ST_X(NEW.gcp_point) and ST_Y(NEW.gcp_point) [the TRIGGER keeps these values up to date]
--- use of 'CASE WHEN / THEN/ELSE/END' for setting of default values
---- for views based on DATES, this can be different than the TABLE default values
So there is a lot that a TRIGGER can do, so the study of the sqlite TRIGGER syntax is strongly advised
The possible complexity of a TRIGGER makes it impossible for a database-driver to determine if the TRIGGER will work correctly
- so a database-driver can only determine that the database claims to have a TRIGGER that will work
3) I am particularly interested in the creation of a spatial view. The example given does not show what other SQL is executed from the GUI to get the view treated as a spatial view, for example by populating views_geometry_columns
The 'create.gcp_master.sql'
- should show all of the needed steps
-- in a terminal where the sql-script is stored execute the following command
--> spatialite gcp_master.db < create.gcp_master.sql
- This assumes that you are using 'Soldner Berlin' srid=3068
-- change '3068' to '4326' to support Wsg84 or whatever projection you desire
4) I see that there is no way, in the GUI, to add a row to a view when the grid is opened via the query view. Is there SQL that will allow me to add a row, as I can through QGIS, or do I need to insert into the master table first, then the detail table second, via SQL?
For some unknown reason, sub-queries even on a normal table (i.e. WHERE name LIKE 'Some%' )
- no changes can be made
Also for views with an valid UPDATE-TRIGGER (and read_only=0)
- no changes can be made
Sandro has given reasons for views, which I do not agree with
- if the TRIGGER does not work correctly, then it the fault of the Database-Designer
But since sub-queries on normal tables can also not be changed
- may be the true cause of not being able to edit a writable view with a valid TRIGGER and read_only=0
-- a view being a sub-query on one/multiple normal table(s)
--
Note:
- while working out a INSERT sample to a view
-- and of course testing if it would work as expected
--> I found 2 different errors in the created TRIGGERS
---> 1 which failed during an INSERT (not during the creation)
----> 2 new fields I had created, were not listed in the fields list.but included in the INSERT Statement
---> 1 that produced rubbish during the successful INSERT
----> the VALUES order did not correspond to the field ORDER
------> gcp_type in position 7 in the field list but position 2 in the VALUE list
which proves again that : TRIGGERS are TRICKY
--
As Sandro has stated, INSERT command can be executed directly into a view:
INSERT INTO gcp_points_bridge
(name,notes,text,belongs_to_01,belongs_to_02,valid_since,valid_until,gcp_point,order_selected)
VALUES
(
'Promeranzenbrücke, Kleine',
'Süden, Lustgarten, Schloß',
'1658-1824 Von neue Packhof (Orangerie-Haus, Promeranzen Haus) über Communikationsgraben zur Lustgarten',
'Bezirk 18 - Schloß',
'Alt-Cölln',
'1658-01-01',
'1823-12-31',
GeomFromEWKT('SRID=3068;POINT(24671.85217766343 21397.99982336495)'),
1 -- precise point
);
This is one of the 4 INSERTS that the script does.
- 2 in bridges, 2 in know points
-- 1 is a 2nd order point, 3 a 1st order point (which also have views)
After UpdateLayerStatistics()
- vector_layers_statistics show proper (i.e non-NULL) values
-- for the VIEWs: bridges, known, gcp_order_01 and 02
-- for the gcp_master TABLE which store the points
So building a test script to fill all the views
- checking the results
-- is the best way to insure that the TRIGGERS work correctly
Hope this helps.
Mark
Thanks.
Dick