Hello Anton,

There's no really convenient way, because the bnode usually appears in more
than one insert statement and there should be a way of remembering them
between calls.

Blank nodes have no persistent names but they have persistent IRI_IDs,
because they're nodes anyway.

The function DB.DBA.TTLP_EXEC_NEW_BLANK (in g varchar, inout app_env any)
returns IRI_ID
returns an IRI_ID of new blank node that never appeared before in the
database.

The parameter g is a 'hint', it should be equal to the IRI of graph where
the blank node will be used. If the bnode is used in more than one graph,
choose the 'most popular' one, if graph name is not known when the bnode is
created then pass a NULL value. Future versions of Virtuoso will tend to
group together all blank node IDs of every graph, to get slightly better
locality of disk access and slightly better compression of bitmap indexes.

The parameter app_env is a variable that should be set to NULL before the
first call and then used only in calls of DB.DBA.TTLP_EXEC_NEW_BLANK()
It's OK to 'forget' the value between consequent calls of
DB.DBA.TTLP_EXEC_NEW_BLANK() or to use many different variables for this
purpose. This is just a hint. So the following code is quite OK:

declare bnode_app_env any;
declare env_2 any;
bnode_app_env := NULL;

bnode1 := DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env);
bnode2 := DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env);
bnode_app_env := NULL;
bnode3 := DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env);
env_2 := NULL;
bnode4 := DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', env_2);
bnode5 := DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env);

That's the way of obtaining blank node ID that will stay valid in next
versions of Virtuoso. In current version (but maybe not in future versions),
the following code will work quite OK:

bnode6 := iri_id_from_num (sequence_next ('RDF_URL_IID_BLANK'));

If you should create one blank node per object, and you have arbitrary
number of objects, you may wish to create a dictionary (using dict_new()),
to keep there pairs of objects and their blank node IDs.

No matter how did you get bnode IRI_ID, you can 'cheat' and pass these
IRI_ID to DB.DBA.RDF_QUAD_URI(...) instead of sublect or object URI. Neither
graph nor predicate can be bnodes: no error is reported on invalid call but
SPARQL queries may incorrectly filter ill formed triples, because the SPARQL
optimizer hopes that P and G are always named IRI references. Hence the
following three queries will be compiled into the same SQL:

sparql select * where { graph ?g { ?s ?p ?o . FILTER (isIRI(?g)) } };
sparql select * where { graph ?g { ?s ?p ?o . FILTER (isIRI(?p)) } };
sparql select * where { graph ?g { ?s ?p ?o } };

whereas the following will be compiled into select ... where (1=2):

sparql select * where { graph ?g { ?s ?p ?o . FILTER (isBLANK(?p)) } };



Note: Allocation of bnodes is transaction-sensitive.

The created bnode IRI_ID may became invalid on transaction rollback. You
should not try to save the created bnode ID in a global variables or
dictionaries that may 'survive' the rollback of the transaction where the ID
is allocated. This is important when a function creates large number of
bnode IDs and then performs large number of insertions or updates, in a
loop, with deadlock retries. If the first insertion or update fails with
rollback, whole set of allocated IDs become invalid, use of these IDs may
result in 'collision in the air' because they can be allocated again for
other purpose by other transaction. When in doubt, use 'commit work' between
allocating IRI_IDs and an operation that may result in transaction rollback.



Note: Allocation of bnodes is a function with side effects, use with caution
in SELECT statements.

The following code may result in a weird error:

declare bnode_app_env any;
bnode_app_env := NULL;
for (
  select U_ID, U_NAME, U_E_MAIL,
     DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env) as U_BNODE
  from SYS_USERS )
do
  {
    COMPOSE_SOME_USER_DATA (U_ID, U_NAME, U_E_MAIL, U_BNODE);
    COMPOSE_MORE_USER_DATA (U_ID, U_NAME, U_E_MAIL, U_BNODE);
  }

The SQL optimizer has a right to make only one call of
DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env) per code invocation,
instead of one call per retrieved user. This is probably not what you
expect. The safe code is as follows:

declare bnode_app_env any;
bnode_app_env := NULL;
for (
  select U_ID, U_NAME, U_E_MAIL from SYS_USERS )
do
  {
    declare u_bnode IRI_ID;
    u_bnode := DB.DBA.TTLP_EXEC_NEW_BLANK ('mygraph', bnode_app_env);
    COMPOSE_SOME_USER_DATA (U_ID, U_NAME, U_E_MAIL, u_bnode);
    COMPOSE_MORE_USER_DATA (U_ID, U_NAME, U_E_MAIL, u_bnode);
  }



Best Regards,
IvAn Mikhailov



-----Original Message-----
From: virtuoso-users-boun...@lists.sourceforge.net
[mailto:virtuoso-users-boun...@lists.sourceforge.net] On Behalf Of Anton
Krasovsky
Sent: Friday, October 27, 2006 9:47 PM
To: virtuoso-users@lists.sourceforge.net
Subject: [Virtuoso-users] Virtuoso RDF API and bnodes

Hi,

is there any way to programmatically insert triples containing bnodes into
the store?

Something similar to DB.DBA.RDF_QUAD_URI(...)?

Regards,
Anton


Reply via email to