1.15 Support for SPARQL Update Operations on an RDF Graph

Effective with Oracle Database Release 12.2, you can perform SPARQL Update operations on an RDF graph.

The W3C provides SPARQL 1.1 Update (https://www.w3.org/TR/2013/REC-sparql11-update-20130321/), an update language for RDF graphs. SPARQL 1.1 Update is supported in Oracle Database semantic technologies through the SEM_APIS.UPDATE_RDF_GRAPH procedure.

Before performing any SPARQL Update operations on an RDF graph, some prerequisites apply:

  • The SEM_APIS.CREATE_SPARQL_UPDATE_TABLES procedure should be run in the schema of each user that will be using the SEM_APIS.UPDATE_RDF_GRAPH procedure.
  • To update an RDF graph, the user should have SELECT, INSERT, DELETE, UPDATE, and QUERY privileges on the network and the target RDF graph. Note that these privileges are automatically present for the network owner. See Sharing Schema-Private RDF Networks to enable other users to update the RDF graph.
  • To run a LOAD operation, the user must have the CREATE ANY DIRECTORY and DROP ANY DIRECTORY privileges, or the user must be granted READ privileges on an existing directory object whose name is supplied in the options parameter.

The following examples show update operations being performed on an RDF graph. These examples assume a schema-private RDF network named NET1 owned by a database user named RDFUSER.

Example 1-117 INSERT DATA Operation

This example shows an INSERT DATA operation that inserts several triples in the default electronics graph.

-- Dataset before operation:
#Empty default graph
-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    INSERT DATA {
       :camera1 :name "Camera 1" .
       :camera1 :price 120 .
       :camera1 :cameraType :Camera .
       :camera2 :name "Camera 2" .
       :camera2 :price 150 .
       :camera2 :cameraType :Camera .
      } ', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera1 :name "Camera 1";
         :price 120;
         :cameraType :Camera .
:camera2 :name "Camera 2"; 
         :price 150;
         :cameraType :Camera .

Example 1-118 DELETE DATA Operation

This example shows a DELETE DATA operation that removes a single triple from the default electronics RDF graph.

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera1 :name "Camera 1";
         :price 120;
         :cameraType :Camera .
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    DELETE DATA { :camera1 :price 120 . } ', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera1 :name "Camera 1";
         :cameraType :Camera .
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .

Example 1-119 DELETE/INSERT Operation on Default Graph

This example performs a DELETE/INSERT operation. The :cameraType of :camera1 is updated to :digitalCamera.

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera1 :name "Camera 1";
         :cameraType :Camera .
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    DELETE { :camera1 :cameraType ?type . } 
    INSERT { :camera1 :cameraType :digitalCamera . } 
    WHERE  { :camera1 :cameraType ?type . }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera1 :name "Camera 1";
         :cameraType :digitalCamera .
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .

Example 1-120 DELETE/INSERT Operation Involving Default Graph and Named Graph

Graphs can also be specified inside the DELETE and INSERT templates, as well as inside the WHERE clause. This example moves all triples corresponding to digital cameras from the default graph to the graph :digitalCameras.

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera1 :name "Camera 1";
         :cameraType :digitalCamera .
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Empty graph :digitalCameras

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    DELETE { ?s ?p ?o } 
    INSERT { graph :digitalCameras { ?s ?p ?o } }
    WHERE  { ?s :cameraType :digitalCamera .
             ?s ?p ?o }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

Example 1-121 INSERT WHERE and DELETE WHERE Operations

One of either the DELETE template or the INSERT template can be omitted from a DELETE/INSERT operation. In addition, the template following DELETE can be omitted as a shortcut for using the WHERE pattern as the DELETE template. This example uses an INSERT WHERE statement to insert the contents of the :digitalCameras graph to the :cameras graph, and it uses a DELETE WHERE statement (with syntactic shortcut) to delete all contents of the :cameras graph.


-- INSERT WHERE
-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Empty graph :cameras

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    INSERT { graph :cameras { ?s ?p ?o } }
    WHERE  { graph :digitalCameras { ?s ?p ?o } }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

-- DELETE WHERE
-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    DELETE WHERE { graph :cameras { ?s ?p ?o } }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Empty graph :cameras

Example 1-122 COPY Operation

This example performs a COPY operation. All data from the default graph is inserted into the graph :cameras. Existing data from :cameras, if any, is removed before the insertion.

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera3 :name "Camera 3" .
}

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    COPY DEFAULT TO GRAPH :cameras', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1"; 
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}

Example 1-123 ADD Operation

This example adds all the triples in the graph :digitalCameras to the graph :cameras.

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    ADD GRAPH :digitalCameras TO GRAPH :cameras', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}

Example 1-124 MOVE Operation

This example moves all the triples in the graph :digitalCameras to the graph :digCam.

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Graph :digitalCameras
GRAPH :digitalCameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}
#Graph :digCam
GRAPH :digCam {
  :camera4 :cameraType :digCamera .
}

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    MOVE GRAPH :digitalCameras TO GRAPH :digCam', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2" .
         :camera2 :price 150 .
         :camera2 :cameraType :Camera .
#Empty graph :digitalCameras
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}
#Graph :digCam
GRAPH :digCam {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

Example 1-125 CLEAR Operation

This example performs a CLEAR operation, deleting all the triples in the default graph. Because empty graphs are not stored in the RDF graph, the CLEAR operation always succeeds and is equivalent to a DROP operation. (For the same reason, the CREATE operation has no effect on the RDF graph.)

-- Dataset before operation:
@prefix : <http://www.example.org/electronics/> 
#Default graph
:camera2 :name "Camera 2";
         :price 150;
         :cameraType :Camera .
#Empty graph :digitalCameras
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera 
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}
#Graph :digCam
GRAPH :digCam {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

-- Update operation:
BEGIN
  sem_apis.update_rdf_graph('electronics',
   'CLEAR DEFAULT ', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Empty Default graph
#Empty graph :digitalCameras
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}
#Graph :digCam
GRAPH :digCam {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

Example 1-126 LOAD Operation

N-Triple, N-Quad, Turtle, and Trig files can be loaded from the local file system using the LOAD operation. Note that the simpler N-Triple, and N-Quad formats can be loaded faster than Turtle and Trig. An optional INTO clause can be used to load the file into a specific named graph. To perform a LOAD operation, the user must either (1) have CREATE ANY DIRECTORY and DROP ANY DIRECTORY privileges or (2) supply the name of an existing directory object in the options parameter of UPDATE_RDF_GRAPH. This example loads the /home/oracle/example.nq N-Quad file into an RDF graph..

Note that the use of an INTO clause with an N-Quad or Trig file will override any named graph information in the file. In this example, INTO GRAPH :cameras overrides :myGraph for the first quad, so the subject, property, object triple component of this quad is inserted into the :cameras graph instead.

-- Datafile: /home/oracle/example.nq
<http://www.example.org/electronics/camera3> <http://www.example.org/electronics/name> "Camera 3" <http://www.example.org/electronics/myGraph> .
<http://www.example.org/electronics/camera3> <http://www.example.org/electronics/price> "125"^^<http://www.w3.org/2001/XMLSchema#decimal> .

-- Dataset before operation:
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
}
#Graph :digCam
GRAPH :digCam {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

-- Update operation:
CREATE OR REPLACE DIRECTORY MY_DIR AS '/home/oracle';

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    LOAD <file:///example.nq> INTO GRAPH :cameras',
   options=>'LOAD_DIR={MY_DIR}', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
END;
/

-- Dataset after operation:
@prefix : <http://www.example.org/electronics/> 
#Graph :cameras
GRAPH :cameras {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
  :camera2 :name "Camera 2";
           :price 150;
           :cameraType :Camera .
  :camera3 :name "Camera 3";
           :price 125.
}
#Graph :digCam
GRAPH :digCam {
  :camera1 :name "Camera 1";
           :cameraType :digitalCamera .
}

Several files under the same directory can be loaded in parallel with a single LOAD operation. To specify extra N-Triple or N-Quad files to be loaded, you can use the LOAD_OPTIONS hint. The degree of parallelism for the load can be specified with PARALLEL(n) in the options string.. The following example shows how to load the files /home/oracle/example1.nq, /home/oracle/example2.nq, and /home/oracle/example3.nq into an RDF graph. A degree of parallelism of 3 is used for this example.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    LOAD <file:///example1.nq>',
   options=> ' PARALLEL(3) LOAD_OPTIONS={ example2.nq example3.nq } LOAD_DIR={MY_DIR} ', 
   network_owner=>'RDFUSER', network_name=>'NET1' );
END;
/

Related subtopics:

1.15.1 Tuning the Performance of SPARQL Update Operations

In some cases it may be necessary to tune the performance of SPARQL Update operations. Because SPARQL Update operations involve executing one or more SPARQL queries based on the WHERE clause in the UPDATE statement, the Best Practices for Query Performance also apply to SPARQL Update operations. The following considerations also apply:

  • Delete operations require an appropriate index on the application table (associated with the apply_model parameter in SEM_APIS.UPDATE_RDF_GRAPH) for good performance. Assuming an application table named APP_TAB with the SDO_RDF_TRIPLE_S column named TRIPLE, an index similar to the following is recommended (this is the same index used by RDF Graph Support for Apache Jena):

    -- Application table index for 
    --  (graph_id, subject_id, predicate_id, canonical_object_id)
    CREATE INDEX app_tab_idx ON app_tab app (
      BITAND(app.triple.rdf_m_id,79228162514264337589248983040)/4294967296,
      app.triple.rdf_s_id,
      app.triple.rdf_p_id,
      app.triple.rdf_c_id)
    COMPRESS;
    
  • Performance-related SEM_MATCH options can be passed to the match_options parameter of SEM_APIS.UPDATE_RDF_GRAPH, and performance-related options such as PARALLEL and DYNAMIC_SAMPLING can be specified in the options parameter of that procedure. The following example uses the options parameter to specify a degree of parallelism of 4 and an optimizer dynamic sampling level of 6 for the update. In addition, the example uses ALLOW_DUP=T as a match option when matching against the RDF graph collection VM1.

    BEGIN
      sem_apis.update_rdf_graph(
       'electronics',
       'PREFIX : <http://www.example.org/electronics/> 
        INSERT { graph :digitalCameras { ?s ?p ?o } }
        WHERE  { ?s :cameraType :digitalCamera .
                 ?s ?p ?o }',
       match_models=>sem_models('VM1'),
       match_models=>sem_models('VM1'),
       match_options=>' ALLOW_DUP=T ',
       options=>' PARALLEL(4) DYNAMIC_SAMPLING(6) ', 
       network_owner=>'RDFUSER', network_name=>'NET1');
    END;
    /
    
  • Inline Query Optimizer Hints can be specified in the WHERE clause. The following example extends the preceding example by using the HINT0 hint in the WHERE clause and the FINAL_VALUE_NL hint in the match_options parameter.

    BEGIN
      sem_apis.update_rdf_graph(
       'electronics',
       'PREFIX : <http://www.example.org/electronics/> 
        INSERT { graph :digitalCameras { ?s ?p ?o } }
        WHERE  { # HINT0={ LEADING(t0 t1) USE_NL(t0 t1)
                 ?s :cameraType :digitalCamera .
                 ?s ?p ?o }',
       match_models=>sem_models('VM1'),
       match_options=>' ALLOW_DUP=T FINAL_VALUE_NL ',
       options=>' PARALLEL(4) DYNAMIC_SAMPLING(6) ', 
       network_owner=>'RDFUSER', network_name=>'NET1');
    END;
    /
    

1.15.2 Transaction Management with SPARQL Update Operations

You can exercise some control over the number of transactions used and whether they are automatically committed by a SEM_APIS.UPDATE_RDF_GRAPH operation.

By default, the SEM_APIS.UPDATE_RDF_GRAPH procedure executes in a single transaction that is either committed upon successful completion or rolled back if an error occurs. For example, the following call executes three update operations (separated by semicolons) in a single transaction:

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert camera data
    INSERT DATA {
      elec:camera1 elec:name "Camera 1" .
      elec:camera1 elec:price 120 .
      elec:camera1 elec:cameraType elec:DigitalCamera .
      elec:camera2 elec:name "Camera 2" .
      elec:camera2 elec:price 150 .
      elec:camera2 elec:cameraType elec:DigitalCamera . }; 
    # insert ecom:price triples
    INSERT { ?c  ecom:price ?p }
    WHERE  { ?c  elec:price ?p };
    # delete elec:price triples
    DELETE WHERE { ?c elec:price ?p }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

PL/SQL procedure successfully completed.

By contrast, the following example uses three separate SEM_APIS.UPDATE_RDF_GRAPH calls to execute the same three update operations in three separate transactions:

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert camera data
    INSERT DATA {
      elec:camera1 elec:name "Camera 1" .
      elec:camera1 elec:price 120 .
      elec:camera1 elec:cameraType elec:DigitalCamera .
      elec:camera2 elec:name "Camera 2" .
      elec:camera2 elec:price 150 .
      elec:camera2 elec:cameraType elec:DigitalCamera . }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;

PL/SQL procedure successfully completed.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert ecom:price triples
    INSERT { ?c  ecom:price ?p }
    WHERE  { ?c  elec:price ?p }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

PL/SQL procedure successfully completed.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert elec:price triples
    DELETE WHERE { ?c elec:price ?p }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

PL/SQL procedure successfully completed.

The AUTOCOMMIT=F option can be used to prevent separate transactions for each SEM_APIS.UPDATE_RDF_GRAPH call. With this option, transaction management is the responsibility of the caller. The following example shows how to execute the update operations in the preceding example as a single transaction instead of three separate ones.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert camera data
    INSERT DATA {
      elec:camera1 elec:name "Camera 1" .
      elec:camera1 elec:price 120 .
      elec:camera1 elec:cameraType elec:DigitalCamera .
      elec:camera2 elec:name "Camera 2" .
      elec:camera2 elec:price 150 .
      elec:camera2 elec:cameraType elec:DigitalCamera . }',
   options=>' AUTOCOMMIT=F ',
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

PL/SQL procedure successfully completed.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert ecom:price triples
    INSERT { ?c  ecom:price ?p }
    WHERE  { ?c  elec:price ?p }',
   options=>' AUTOCOMMIT=F ',
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

PL/SQL procedure successfully completed.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    # insert elec:price triples
    DELETE WHERE { ?c elec:price ?p }',
   options=>' AUTOCOMMIT=F ',
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

PL/SQL procedure successfully completed.

COMMIT;

Commit complete.

However, the following cannot be used with the AUTOCOMMIT=F option:

  • Bulk operations (FORCE_BULK=T, DEL_AS_INS=T)

  • LOAD operations

  • Materialization of intermediate data (STREAMING=F)

1.15.2.1 Transaction Isolation Levels

Oracle Database supports three different transaction isolation levels: read committed, serializable, and read-only.

Read committed isolation level is the default. Queries in a transaction using this isolation level see only data that was committed before the query – not the transaction – began and any changes made by the transaction itself. This isolation level allows the highest degree of concurrency.

Serializable isolation level queries see only data that was committed before the transaction began and any changes made by the transaction itself.

Read-only isolation level behaves like serializable isolation level but data cannot be modified by the transaction.

SEM_APIS.UPDATE_RDF_GRAPH supports read committed and serializable transaction isolation levels, and read committed is the default. SPARQL UPDATE operations are processed in the following basic steps.

  1. A query is executed to obtain a set of triples to be deleted.

  2. A query is executed to obtain a set of triples to be inserted.

  3. Triples obtained in Step 1 are deleted.

  4. Triples obtained in Step 2 are inserted.

With the default read committed isolation level, the underlying triple data may be modified by concurrent transactions, so each step may see different data. In addition, changes made by concurrent transactions will be visible to subsequent update operations within the same SEM_APIS.UPDATE_RDF_GRAPH call. Note that steps 1 and 2 happen as a single step when using materialization of intermediate data (STREAMING=F), so underlying triple data cannot be modified between steps 1 and 2 with this option. See Support for Bulk Operations for more information about materialization of intermediate data.

Serializable isolation level can be used by specifying the SERIALIZABLE=T option. In this case, each step will only see data that was committed before the update RDF graph operation began, and multiple update operations executed in a single SEM_APIS.UPDATE_RDF_GRAPH call will not see modifications made by concurrent update operations in other transactions. However, ORA-08177 errors will be raised if a SEM_APIS.UPDATE_RDF_GRAPH execution tries to update triples that were modified by a concurrent transaction. When using SERIALIZABLE=T, the application should detect and handle ORA-08177 errors (for example, retry the update command if it could not be serialized on the first attempt).

The following cannot be used with the SERIALIZABLE=T option:

  • Bulk operations (FORCE_BULK=T, DEL_AS_INS=T)

  • LOAD operations

  • Materialization of intermediate data (STREAMING=F)

1.15.3 Support for Bulk Operations

SEM_APIS.UPDATE_RDF_GRAPH supports bulk operations for efficient execution of large updates. The following options are provided; however, when using any of these bulk operations, serializable isolation (SERIALIZABLE=T) and autocommit false (AUTOCOMMMIT=F) cannot be used.

1.15.3.1 Materialization of Intermediate Data (STREAMING=F)

By default, SEM_APIS.UPDATE_RDF_GRAPH executes two queries for a basic DELETE INSERT SPARQL Update operation: one query to find triples to delete and one query to find triples to insert. For some update operations with WHERE clauses that are expensive to evaluate, executing two queries may not give the best performance. In these cases, executing a single query for the WHERE clause, materializing the results, and then using the materialized results to construct triples to delete and triples to insert may give better performance. This approach incurs overhead from a DDL operation, but overall performance is likely to be better for complex update statements.

The following example update using this option (STREAMING=F). Note that STREAMING=F is not allowed with serializable isolation (SERIALIZABLE=T) or autocommit false (AUTOCOMMIT=F).

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    DELETE { ?s ?p ?o } 
    INSERT { graph :digitalCameras { ?s ?p ?o } }
    WHERE  { ?s :cameraType :digitalCamera .
             ?s ?p ?o }',
  options=>' STREAMING=F ', 
  network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

1.15.3.2 Using SEM_APIS.BULK_LOAD_RDF_GRAPH

For updates that insert a large number of triples (such as tens of thousands), the default approach of incremental DML on the application table may not give acceptable performance. In such cases, the FORCE_BULK=T option can be specified so that SEM_APIS.BULK_LOAD_RDF_GRAPH is used instead of incremental DML.

However, not all update operations can use this optimization. The FORCE_BULK=T option is only allowed for a SEM_APIS.UPDATE_RDF_GRAPH call with either a single ADD operation or a single INSERT WHERE operation. The use of SEM_APIS.BULK_LOAD_RDF_GRAPH forces a series of commits and autonomous transactions, so the AUTOCOMMIT=F and SERIALIZABLE=T options are not allowed with FORCE_BULK=T. In addition, bulk load cannot be used with CLOB_UPDATE_SUPPORT=T.

SEM_APIS.BULK_LOAD_RDF_GRAPH allows various customizations through its flags parameter. SEM_APIS.UPDATE_RDF_GRAPH supports the BULK_OPTIONS={ OPTIONS_STRING } flag so that OPTIONS_STRING can be passed into the flags input of SEM_APIS.BULK_LOAD_RDF_GRAPH to customize bulk load options. The following example shows a SEM_APIS.UPDATE_RDF_GRAPH invocation using the FORCE_BULK=T option and BULK_OPTIONS flag.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX elec: <http://www.example.org/electronics/>
    PREFIX ecom: <http://www.example.org/ecommerce/> 
    INSERT { ?c  ecom:price ?p }
    WHERE  { ?c  elec:price ?p }',
   options=>' FORCE_BULK=T BULK_OPTIONS={  parallel=4 parallel_create_index }', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

1.15.3.3 Using Delete as Insert (DEL_AS_INS=T)

For updates that delete a large number of triples (such as tens of thousands), the default approach of incremental DML on the application table may not give acceptable performance. For such cases, the DEL_AS_INS=T option can be specified. With this option, a large delete operation is implemented as INSERT, TRUNCATE, and EXCHANGE PARTITION operations.

The use of DEL_AS_INS=T causes a series of commits and autonomous transactions, so this option cannot be used with SERIALIZABLE=T or AUTOCOMMIT=F. In addition, this option can only be used with SEM_APIS.UPDATE_RDF_GRAPH calls that involve a single DELETE WHERE operation, a single DROP operation, or a single CLEAR operation.

Delete as insert internally uses SEM_APIS.MERGE_RDF_GRAPHS during intermediate operations. The string OPTIONS_STRING from the MM_OPTIONS={ OPTIONS_STRING } flag can be specified to customize options for merging. The following example shows a SEM_APIS.UPDATE_RDF_GRAPH invocation using the DEL_AS_INS=T option and MM_OPTIONS flag.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'CLEAR NAMED',
   options=>' DEL_AS_INS=T MM_OPTIONS={  dop=4 } ', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

1.15.4 Setting UPDATE_RDF_GRAPH Options at the Session Level

Some settings that affect the SEM_APIS.UPDATE_RDF_GRAPH procedure’s behavior can be modified at the session level through the use of the special MDSYS.SDO_SEM_UPDATE_CTX.SET_PARAM procedure. The following options can be set to true or false at the session level: autocommit, streaming, strict_bnode, and clob_support.

The MDSYS.SDO_SEM_UPDATE_CTX contains the following subprograms to get and set SEM_APIS.UPDATE_RDF_GRAPH parameters at the session level:

SQL> describe mdsys.sdo_sem_update_ctx
FUNCTION GET_PARAM RETURNS VARCHAR2
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 NAME                           VARCHAR2                IN
PROCEDURE SET_PARAM
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 NAME                           VARCHAR2                IN
 VALUE                          VARCHAR2                IN

The following example causes all subsequent calls to the SEM_APIS.UPDATE_RDF_GRAPH procedure to use the AUTOCOMMIT=F setting, until the end of the session or the next call to SEM_APIS.UPDATE_RDF_GRAPH that specifies a different autocommit value.

begin
  mdsys.sdo_sem_update_ctx.set_param('autocommit','false');
end;
/

1.15.5 Load Operations: Special Considerations for SPARQL Update

The format of the file to load affects the amount of parallelism that can be used during the load process. Load operations have two phases:

  1. Loading from the file system to a staging table

  2. Calling SEM_APIS.BULK_LOAD_RDF_GRAPH to load from a staging table into an RDF graph

All supported data formats can use parallel execution in phase 2, but only N-Triple and N-Quad formats can use parallel execution in phase 1. In addition, if a load operation is interrupted during phase 2 after the staging table has been fully populated, loading can be resumed with the RESUME_LOAD=T keyword in the options parameter.

Load operations for RDF documents that contain object values longer than NETWORK_MAX_STRING_SIZE bytes may require additional operations. Load operations on Turtle and Trig documents will automatically load all triples/quads regardless of object value size. However, load operations on N-Triple and N-Quad documents will only load triples/quads with object values that are less than NETWORK_MAX_STRING_SIZE bytes in length. For N-Triple and N-Quad data, a second load operation should be issued with the LOAD_CLOB_ONLY=T option to also load triples/quads with object values larger than NETWORK_MAX_STRING_SIZE bytes.

Loads from Unix named pipes are only supported for N-Triple and N-Quad formats. Turtle and Trig files should be uncompressed, physical files.

Unicode characters are handled differently depending on the format of the RDF file to load. Unicode characters in N-Triple and N-Quad files should be escaped as \u<HEX><HEX><HEX><HEX> or \U<HEX><HEX><HEX><HEX><HEX><HEX><HEX><HEX> using the hex value of the Unicode codepoint value. Turtle and Trig files do not require Unicode escaping and can be directly loaded with unescaped Unicode values.

Example 1-127 Short and Long Literal Load for N-Quad Data

BEGIN
  -- short literal load
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    LOAD <file:///example1.nq>',
   options=> ' LOAD_DIR={MY_DIR} ', 
   network_owner=>'RDFUSER', network_name=>'NET1');

  -- long literal load
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/>
    LOAD <file:///example1.nq>',
   options=> ' LOAD_DIR={MY_DIR} LOAD_CLOB_ONLY=T ',
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

1.15.6 Long Literals: Special Considerations for SPARQL Update

By default, SPARQL Update operations do not manipulate values longer than NETWORK_MAX_STRING_SIZE bytes. To enable long literals support, specify CLOB_UPDATE_SUPPORT=T in the options parameter with the SEM_APIS.UPDATE_RDF_GRAPH procedure.

Bulk load does not work for long literals; the FORCE_BULK=T option is ignored when used with the CLOB_UPDATE_SUPPORT=T option.

1.15.7 Blank Nodes: Special Considerations for SPARQL Update

Some update operations only affect the graph of a set of RDF triples. Specifically, these operations are ADD, COPY and MOVE. For example, the MOVE operation example in Support for SPARQL Update Operations on an RDF Graph can be performed only updating triples having :digitalCameras as the graph. However, the performance of such operations can be improved by using ID-only operations over the RDF graph. To run a large ADD, COPY, or MOVE operation as an ID-only operation, you can specify the STRICT_BNODE=F hint in the options parameter for the SEM_APIS.UPDATE_RDF_GRAPH procedure.

ID-only operations may lead to incorrect blank nodes, however, because no two graphs should share the same blank node. RDF graph uses a blank node prefixing scheme based on the model (RDF graph) and named graph combination that contains a blank node. These prefixes ensure that blank node identifiers are unique across models (RDF graphs) and named graphs. An ID-only approach for ADD, COPY, and UPDATE operations does not update blank node prefixes.

Example 1-128 ID-Only Update Causing Incorrect Blank Node Values

The update in the following example leads to the same blank node subject for both triples in graphs :cameras and :cameras2. This can be verified running the provided SEM_MATCH query.

BEGIN
  sem_apis.update_rdf_graph('electronics',
   'PREFIX : <http://www.example.org/electronics/> 
    INSERT DATA { 
       GRAPH :cameras { :camera2 :owner _:bn1 .
                        _:bn1 :name "Axel" }
    };
    COPY :cameras TO :cameras2',
   options=>' STRICT_BNODE=F ', 
   network_owner=>'RDFUSER', network_name=>'NET1');
END;
/

SELECT count(s)
FROM TABLE( SEM_MATCH('
  PREFIX : <http://www.example.org/electronics/> 
  SELECT * 
  WHERE { { graph :cameras  {?s :name "Axel"  } }
          { graph :cameras2 {?s :name "Axel"  } } }
', sem_models('electronics'),null,null,null,null,' STRICT_DEFAULT=T ',
null, null, 'RDFUSER', 'NET1'));

To avoid such errors, you should specify the STRICT_BNODE=F hint in the options parameter for the SEM_APIS.UPDATE_RDF_GRAPH procedure only when you are sure that blank nodes are not involved in the ADD, COPY, or MOVE update operation.

However, ADD, COPY, and MOVE operations on large graphs with the STRICT_BNODE=F option may run significantly faster than they would run using the default method. If you need to run a series of ID-only updates, another option is to use the STRICT_BNODE=F option, and then execute the SEM_APIS.CLEANUP_BNODES procedure at the end. This approach resets the prefix of all blank nodes in a given RDF graph, which effectively corrects ("cleans up") all erroneous blank node labels.

Note that this two-step strategy should not be used with a small number of ADD, COPY, or MOVE operations. Performing a few operations using the default approach will execute faster than running a few ID-only operations and then executing the SEM_APIS.CLEANUP_BNODES procedure.

The following example corrects blank nodes in the RDF graph named electronics.

EXECUTE sem_apis.cleanup_bnodes('electronics');