\set VERBOSITY terse \pset null NULL --SET search_path TO sparql, pg_catalog; SELECT sparql.rdf_fdw_arguments_compatible('"abc"','"b"'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"','"b"^^'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"^^','"b"'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"^^','"b"^^'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@en','"b"'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@en','"b"^^xsd:string'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@en','"b"@en'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@fr','"b"@ja'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"','"b"@ja'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"','"b"@en'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"^^xsd:string','"b"@en'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) /* * A language tag is compared without regard to case (RDF 1.1 Concepts, 3.3), * so two literals carrying one tag written differently are compatible. Only * the part before the first hyphen is lowercased when a term is read, so a * region subtag reaches the comparison as it was written. */ SELECT sparql.rdf_fdw_arguments_compatible('"abc"@EN','"b"@en'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@en-GB','"b"@en-gb'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@en-gb','"b"@en-GB'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"abc"@zh-Hant-TW','"b"@ZH-HANT-tw'); rdf_fdw_arguments_compatible ------------------------------ t (1 row) /* a different tag is still a different tag */ SELECT sparql.rdf_fdw_arguments_compatible('"abc"@en-GB','"b"@en-US'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) /* * The compatibility rule is about literals. An IRI and a blank node are not * literals, so a string function has nothing to apply and answers with a type * error. Neither carries a language tag or a datatype, which is what a rule * written in terms of those alone reads as a simple literal. */ SELECT sparql.rdf_fdw_arguments_compatible('','"a"'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"a"',''); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('',''); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('_:b1','"b"'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('"b"','_:b1'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) SELECT sparql.rdf_fdw_arguments_compatible('_:b1','_:b1'); rdf_fdw_arguments_compatible ------------------------------ f (1 row) /* * The functions themselves therefore return NULL. What they returned before * was computed from the way the term is written: STRBEFORE cut an IRI at the * first "/" and handed back a literal that began with the opening angle * bracket. */ SELECT sparql.contains('','"abc"'); contains ---------- NULL (1 row) SELECT sparql.strstarts('','"','"/"'); strbefore ----------- NULL (1 row) SELECT sparql.strafter('','"org"'); strafter ---------- NULL (1 row) SELECT sparql.strends('','"abc>"'); strends --------- NULL (1 row) SELECT sparql.contains('_:b1','"b"'); contains ---------- NULL (1 row) /* LEX */ SELECT sparql.lex('"foo"'); lex ----- foo (1 row) SELECT sparql.lex('foo'); lex ----- foo (1 row) SELECT sparql.lex('"foo"@en'); lex ----- foo (1 row) SELECT sparql.lex('"foo"^^xsd:string'); lex ----- foo (1 row) SELECT sparql.lex(''); lex ----- (1 row) SELECT sparql.lex('""'); lex ----- (1 row) SELECT sparql.lex('"\""'); lex ----- \" (1 row) SELECT sparql.lex(NULL); lex ------ NULL (1 row) /* backslash runs of both parities before the closing quote. An even-length * run is made of complete escape pairs and does NOT escape that quote, so the * literal is well formed and lex() must return the run itself, with no quotes * folded into it, and the plain and language-tagged forms must agree. An * odd-length run does escape the closing quote, leaving the literal * unterminated: the whole input is then read as content, quotes and all, and * lang() must find no tag rather than reading past it. */ SELECT n, sparql.lex(('"' || repeat('\', n) || '"')::rdfnode) AS plain, sparql.lex(('"' || repeat('\', n) || '"@en')::rdfnode) AS tagged, sparql.lex(('"' || repeat('\', n) || '"')::rdfnode) = sparql.lex(('"' || repeat('\', n) || '"@en')::rdfnode) AS agree FROM generate_series(0, 8) AS n ORDER BY n; n | plain | tagged | agree ---+------------+---------------+------- 0 | | | t 1 | \"\" | \"\"@en | f 2 | \\ | \\ | t 3 | \"\\\" | \"\\\"@en | f 4 | \\\\ | \\\\ | t 5 | \"\\\\\" | \"\\\\\"@en | f 6 | \\\\\\ | \\\\\\ | t 7 | \"\\\\\\\" | \"\\\\\\\"@en | f 8 | \\\\\\\\ | \\\\\\\\ | t (9 rows) /* STRDT */ SELECT sparql.strdt(NULL, 'http://www.w3.org/2001/XMLSchema#string'); strdt ------- NULL (1 row) SELECT sparql.strdt('foo', NULL); strdt ------- NULL (1 row) SELECT sparql.strdt('', ''); strdt ------------------------------- ""^^ (1 row) SELECT sparql.strdt('foo', ''); ERROR: datatype IRI cannot be empty SELECT sparql.strdt('foo', ' '); ERROR: datatype IRI cannot contain whitespaces SELECT sparql.strdt('foo', ' xsd:boolean '); ERROR: datatype IRI cannot contain whitespaces SELECT sparql.strdt('foo', 'http://www.w3.org/2001/XMLSchema#string'); strdt -------------------------------------------------- "foo"^^ (1 row) SELECT sparql.strdt('f"oo', 'http://example.org/type'); strdt ------------------------------------ "f\"oo"^^ (1 row) SELECT sparql.strdt('"foo"@en', 'http://www.w3.org/2001/XMLSchema#int'); strdt ----------------------------------------------- "foo"^^ (1 row) SELECT sparql.strdt('"f\"oo"^^xsd:string', 'http://example.org/newtype'); strdt --------------------------------------- "f\"oo"^^ (1 row) SELECT sparql.strdt('foo', ''); strdt ---------------------------------- "foo"^^ (1 row) SELECT sparql.strdt('foo', 'foo:bar'); strdt ------------------ "foo"^^ (1 row) SELECT sparql.strdt('foo', 'xsd:string'); strdt -------------------------------------------------- "foo"^^ (1 row) SELECT sparql.strdt('foo', ''); strdt ------------------- "foo"^^ (1 row) SELECT sparql.strdt('_:b1', 'xsd:string'); ERROR: invalid argument: _:b1 /* STRLANG */ SELECT sparql.strlang('foo',NULL); strlang --------- NULL (1 row) SELECT sparql.strlang(NULL,'de'); strlang --------- NULL (1 row) SELECT sparql.strlang('','es'); strlang --------- ""@es (1 row) SELECT sparql.strlang(' ','en'); strlang --------- " "@en (1 row) SELECT sparql.strlang('foo','pt'); strlang ---------- "foo"@pt (1 row) SELECT sparql.strlang('"foo"@en','fr'); strlang ---------- "foo"@fr (1 row) SELECT sparql.strlang('"foo"','it'); strlang ---------- "foo"@it (1 row) SELECT sparql.strlang('"foo"^^xsd:string','pt'); strlang ---------- "foo"@pt (1 row) SELECT sparql.strlang('"foo"^^','es'); strlang ---------- "foo"@es (1 row) SELECT sparql.strlang(sparql.strlang('"foo"^^','es'),'de'); strlang ---------- "foo"@de (1 row) SELECT sparql.strlang(sparql.strlang('f"o"o','en'),'de'); strlang -------------- "f\"o\"o"@de (1 row) SELECT sparql.strlang(sparql.strlang('x\"y','pl'),'it'); strlang ----------- "x\"y"@it (1 row) SELECT sparql.strlang('foo', 'xyz'); strlang ----------- "foo"@xyz (1 row) SELECT sparql.strlang('foo', 'EN'); strlang ---------- "foo"@en (1 row) SELECT sparql.strlang('foo', 'EN-GB'); strlang ------------- "foo"@en-gb (1 row) SELECT sparql.strlang('foo', 'eN-gb'); strlang ------------- "foo"@en-gb (1 row) SELECT sparql.strlang('foo', 'EN-Latn-US-valencia'); strlang --------------------------- "foo"@en-latn-us-valencia (1 row) SELECT sparql.strlang('_:b1', 'en'); ERROR: invalid argument: _:b1 /* a malformed tag is rejected, as rdfnode input rejects it: the value built * with one used to be stored, but could not be read back */ SELECT sparql.strlang('"hello"', 'bad tag'); ERROR: invalid language tag: "bad tag" SELECT sparql.strlang('"hello"', 'en;DROP'); ERROR: invalid language tag: "en;DROP" SELECT sparql.strlang('"hello"', 'toolongtag'); ERROR: invalid language tag: "toolongtag" SELECT sparql.strlang('"hello"', 'en-'); ERROR: invalid language tag: "en-" /* STR */ SELECT sparql.str('foo'); str ------- "foo" (1 row) SELECT sparql.str('"foo"'); str ------- "foo" (1 row) SELECT sparql.str('"foo"@en'); str ------- "foo" (1 row) SELECT sparql.str('"foo"^^xsd:string'); str ------- "foo" (1 row) SELECT sparql.str('f"oo'); str --------- "f\"oo" (1 row) SELECT sparql.str('"f\"oo"'); str --------- "f\"oo" (1 row) SELECT sparql.str(''); str -------------------------- "http://example.org/foo" (1 row) SELECT sparql.str(''); str ----- "" (1 row) SELECT sparql.str(' '); str ----- " " (1 row) SELECT sparql.str(NULL); str ------ NULL (1 row) /* LANG */ SELECT sparql.lang('"foo"@en'); lang ------ en (1 row) SELECT sparql.lang(sparql.strlang('foo','fr')); lang ------ fr (1 row) SELECT sparql.lang(sparql.strdt('foo','xsd:string')); lang ------ (1 row) SELECT sparql.lang('"f"oo"@it'); ERROR: invalid input syntax for type rdfnode: ""f"oo"@it" at character 20 SELECT sparql.lang(''); lang ------ (1 row) SELECT sparql.lang(' '); lang ------ (1 row) SELECT sparql.lang(NULL); lang ------ NULL (1 row) /* * STRLEN, LANG and REPLACE are defined over literals, and an IRI or a blank * node has no lexical form for them to work on. They used to operate on the * term's written shape instead: STRLEN counted the angle brackets, REPLACE * rewrote the IRI and handed back a literal, and LANG reported the empty tag * that belongs to a plain literal. Fuseki and GraphDB leave all three unbound * for an IRI, and UCASE, LCASE, SUBSTR and CONCAT here already refuse one. */ SELECT sparql.strlen(''); ERROR: STRLEN does not allow IRIs: SELECT sparql.strlen('_:b1'); ERROR: STRLEN does not allow blank nodes: _:b1 SELECT sparql.replace('', 'a', 'Z'); ERROR: REPLACE does not allow IRIs: SELECT sparql.replace(''::rdfnode, 'a'::rdfnode, 'Z'::rdfnode); ERROR: REPLACE does not allow IRIs: SELECT sparql.replace('_:b1'::rdfnode, 'b'::rdfnode, 'Z'::rdfnode); ERROR: REPLACE does not allow blank nodes: _:b1 SELECT sparql.lang('_:b1'); ERROR: LANG does not allow blank nodes: _:b1 /* STR is the exception and must keep taking an IRI: STR of an IRI is defined * and gives its string form. */ SELECT sparql.str(''); str -------------------------- "http://example.org/abc" (1 row) /* STRLEN counts code points of a string literal's lexical form, and refuses a * literal that is not a string -- which is what every store does with it. */ SELECT sparql.strlen('"hello"') AS plain, sparql.strlen('"h\u00e9llo"@en') AS tagged, sparql.strlen('"hello"^^xsd:string') AS typed; plain | tagged | typed -------+--------+------- 5 | 5 | 5 (1 row) SELECT sparql.strlen('"42"^^xsd:integer'); ERROR: STRLEN does not allow non-string literals: SELECT sparql.lang(''); ERROR: LANG does not allow IRIs: /* a literal whose only inner quote is escaped has no closing quote, so it is * malformed and simply has no language tag. lang() used to locate the tag by * skipping the opening quote and then advancing by the length of lex(), which * for this input returns the whole string -- landing one byte past the end of * the allocation. Whatever happened to sit there was read, and when it was * '@' the scan walked on and copied adjacent heap bytes into the tag. */ SELECT sparql.lang('"abc\"@en'::rdfnode) AS unterminated_has_no_tag; unterminated_has_no_tag ------------------------- (1 row) /* the same read, swept across every lexical length so it is not left to one * allocation size to expose it: no tag may be recovered from any of them, and * the stored value must be the input quoted whole, with nothing read past its * end */ SELECT count(*) AS tags_recovered_from_past_the_end FROM generate_series(1, 255) AS k WHERE sparql.lang(('"' || repeat('a', k) || '\"@en')::rdfnode)::text <> ''; tags_recovered_from_past_the_end ---------------------------------- 0 (1 row) SELECT count(*) AS values_not_quoted_whole FROM generate_series(1, 255) AS k WHERE ('"' || repeat('a', k) || '\"@en')::rdfnode::text <> '"\"' || repeat('a', k) || '\"@en"'; values_not_quoted_whole ------------------------- 0 (1 row) /* a doubled quote is an escaped quote, not the end of the lexical form, so * the tag after the real closing quote is still found */ SELECT sparql.lex('"a""b"@en'::rdfnode) AS doubled_quote_lex, sparql.lang('"a""b"@en'::rdfnode) AS doubled_quote_lang; doubled_quote_lex | doubled_quote_lang -------------------+-------------------- a\"b | en (1 row) /* '@' and '^^' inside the lexical form are content: the annotation is what * follows the closing quote. Each of these used to be cut at the first '@' or * '^^', or to lose its tag or datatype. */ SELECT sparql.lang('"a@b"@en'::rdfnode) AS at_lang, sparql.datatype('"a@b.org"^^'::rdfnode) AS at_datatype, sparql.isliteral('"a@b.org"^^'::rdfnode) AS at_isliteral, sparql.lex('"x^^y"@en'::rdfnode) AS caret_lex; at_lang | at_datatype | at_isliteral | caret_lex ---------+-------------------------+--------------+----------- en | | t | x^^y (1 row) SELECT sparql.concat('"a"@en', '"^^b"@en') AS concat_tagged, sparql.concat('"a"^^xsd:string', '"^^b"^^xsd:string') AS concat_typed, sparql.ucase('"x^^y"@en') AS ucase, sparql.substr('"a^^bc"@en', 1, 3) AS substr, sparql.strlang('"user@host"', 'en') AS strlang; concat_tagged | concat_typed | ucase | substr | strlang ---------------+---------------------------------------------------+-----------+----------+---------------- "a^^b"@en | "a^^b"^^ | "X^^Y"@en | "a^^"@en | "user@host"@en (1 row) /* a datatype IRI is not a literal, and has no language tag to cut off */ SELECT sparql.strdt('"x"', 'http://example.org/dt@v1') AS strdt_iri_at; strdt_iri_at --------------------------------- "x"^^ (1 row) /* DATATYPE */ SELECT sparql.datatype('foo'); datatype ------------------------------------------- (1 row) SELECT sparql.datatype('"foo"^^xsd:string'); datatype ------------------------------------------- (1 row) SELECT sparql.datatype('"foo"^^'); datatype ------------------------------------------- (1 row) SELECT sparql.datatype(sparql.strdt('foo','xsd:string')); datatype ------------------------------------------- (1 row) SELECT sparql.datatype('"42"^^'); datatype ---------------------------------------- (1 row) SELECT sparql.datatype(sparql.strdt('foo','bar:xyz')); datatype ----------- (1 row) SELECT sparql.datatype(''); datatype ---------- NULL (1 row) SELECT sparql.datatype('_:bnode42'); datatype ---------- NULL (1 row) SELECT sparql.datatype('"foo"@es'); datatype --------------------------------------------------------- (1 row) SELECT sparql.datatype('"foo"@es'::name); datatype --------------------------------------------------------- (1 row) SELECT sparql.datatype(''); datatype ------------------------------------------- (1 row) SELECT sparql.datatype(''::name); datatype ------------------------------------------- (1 row) SELECT sparql.datatype(' '); datatype ------------------------------------------- (1 row) SELECT sparql.datatype('"foo"^'); ERROR: invalid input syntax for type rdfnode: ""foo"^" SELECT sparql.datatype('"foo"^^xsd:string>'); ERROR: invalid input syntax for type rdfnode: ""foo"^^xsd:string>" SELECT sparql.datatype('"foo"^^ (1 row) SELECT sparql.datatype(cast('2018-05-01 11:30:00' AS timestamp without time zone)); datatype --------------------------------------------- (1 row) SELECT sparql.datatype(cast('2018-05-01 11:30:00' AS timestamp with time zone)); datatype --------------------------------------------- (1 row) SELECT sparql.datatype(cast('11:30:00' AS time)); datatype ----------------------------------------- (1 row) SELECT sparql.datatype(42); datatype -------------------------------------------- (1 row) SELECT sparql.datatype(42.73); datatype -------------------------------------------- (1 row) SELECT sparql.datatype(cast(42 AS smallint)); datatype -------------------------------------------- (1 row) SELECT sparql.datatype(cast(42 AS bigint)); datatype -------------------------------------------- (1 row) SELECT sparql.datatype(cast(42.73 AS double precision)); datatype ------------------------------------------- (1 row) SELECT sparql.datatype(cast(42.73 AS numeric)); datatype -------------------------------------------- (1 row) SELECT sparql.datatype(cast(42.73 AS real)); datatype ------------------------------------------ (1 row) SELECT sparql.datatype(true); datatype -------------------------------------------- (1 row) SELECT sparql.datatype(NULL); datatype ---------- NULL (1 row) /* ENCODE_FOR_URI */ SELECT sparql.encode_for_uri('"Los Angeles"'); encode_for_uri ----------------- "Los%20Angeles" (1 row) SELECT sparql.encode_for_uri('"Los Angeles"@en'); encode_for_uri ----------------- "Los%20Angeles" (1 row) SELECT sparql.encode_for_uri('"Los Angeles"^^xsd:string'); encode_for_uri ----------------- "Los%20Angeles" (1 row) SELECT sparql.encode_for_uri('"Los Angeles"^^'); encode_for_uri ----------------- "Los%20Angeles" (1 row) SELECT sparql.encode_for_uri('"Los Angeles"^^'); encode_for_uri ----------------- "Los%20Angeles" (1 row) SELECT sparql.encode_for_uri('foo! *''();:@&=+$,/?#[]'); encode_for_uri ---------------------------------------------------------------- "foo%21%20%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D" (1 row) SELECT sparql.encode_for_uri('foo'); encode_for_uri ---------------- "foo" (1 row) SELECT sparql.encode_for_uri(''); encode_for_uri ---------------- "" (1 row) SELECT sparql.encode_for_uri(NULL); encode_for_uri ---------------- NULL (1 row) /* IRI / URI */ SELECT sparql.iri('"http://example/"'), sparql.iri('http://example/'), sparql.iri(''); iri | iri | iri -------------------+-------------------+------------------- | | (1 row) SELECT sparql.iri('"mailto:foo@example.com"'), sparql.iri('mailto:foo@example.com'), sparql.iri(''); iri | iri | iri --------------------------+--------------------------+-------------------------- | | (1 row) SELECT sparql.iri('"urn:uuid:123e4567-e89b-12d3-a456-426614174000"'), sparql.iri('urn:uuid:123e4567-e89b-12d3-a456-426614174000'), sparql.iri(''); iri | iri | iri -------------------------------------------------+-------------------------------------------------+------------------------------------------------- | | (1 row) SELECT sparql.iri('"file://etc/passwd"'), sparql.iri('file://etc/passwd'), sparql.iri(''); iri | iri | iri ---------------------+---------------------+--------------------- | | (1 row) SELECT sparql.iri('"foo:bar"'), sparql.iri('foo:bar'), sparql.iri(''); iri | iri | iri -----------+-----------+----------- | | (1 row) SELECT sparql.iri('"foo"'), sparql.iri('foo'), sparql.iri(''); iri | iri | iri -------+-------+------- | | (1 row) SELECT sparql.iri('"a:b:c"'), sparql.iri('a:b:c'), sparql.iri(''); iri | iri | iri ---------+---------+--------- | | (1 row) SELECT sparql.iri('"http:/not-a-scheme"'), sparql.iri('http:/not-a-scheme'), sparql.iri(''); iri | iri | iri ----------------------+----------------------+---------------------- | | (1 row) SELECT sparql.iri('"foo"@en'); iri ------- (1 row) SELECT sparql.iri('"42"^^'); iri ------ <42> (1 row) /* SPARQL 1.1 17.4.2.8 gives URI() as another name for IRI(), both returning an * iri. They must therefore agree on every input and be usable in the same * places -- uri() returned text, so its result went into nothing else. */ SELECT sparql.uri('http://example/') AS uri, sparql.uri('http://example/') = sparql.iri('http://example/') AS same_term, pg_typeof(sparql.uri('http://example/')) = pg_typeof(sparql.iri('http://example/')) AS same_type; uri | same_term | same_type -------------------+-----------+----------- | t | t (1 row) SELECT sparql.isiri(sparql.uri('http://example/')) AS composes; composes ---------- t (1 row) SELECT bool_and(sparql.uri(v) = sparql.iri(v)) AS agree_on_every_input FROM (VALUES ('"http://example/"'::rdfnode), ('http://example/'), (''), ('"foo"'), (''), ('"a:b:c"'), ('"foo"@en')) t(v); agree_on_every_input ---------------------- t (1 row) SELECT sparql.iri('_:b1'); ERROR: invalid argument: _:b1 SELECT sparql.iri('""'); ERROR: invalid IRI: <> /* isIRI / isURI */ SELECT sparql.isIRI(''); isiri ------- t (1 row) SELECT sparql.isIRI(''); isiri ------- t (1 row) SELECT sparql.isIRI('http://example/'); isiri ------- f (1 row) SELECT sparql.isIRI('"http://example/"'); isiri ------- f (1 row) SELECT sparql.isIRI('""'); isiri ------- f (1 row) SELECT sparql.isIRI('path'); isiri ------- f (1 row) SELECT sparql.isIRI('"path"'); isiri ------- f (1 row) SELECT sparql.isIRI('"foo"^^xsd:string'); isiri ------- f (1 row) SELECT sparql.isIRI('"foo"^^'); isiri ------- f (1 row) SELECT sparql.isIRI(sparql.strdt('foo', 'xsd:string')); isiri ------- f (1 row) SELECT sparql.isIRI('"foo"@en'); isiri ------- f (1 row) SELECT sparql.isIRI(''); isiri ------- f (1 row) SELECT sparql.isIRI(NULL); isiri ------- f (1 row) SELECT sparql.isIRI(''); isuri ------- t (1 row) SELECT sparql.isURI('path'); isuri ------- f (1 row) SELECT sparql.isURI('""'); isuri ------- f (1 row) /* STRSTARTS */ SELECT sparql.strstarts('"foobar"','"foo"'), sparql.strstarts('foobar','foo'); strstarts | strstarts -----------+----------- t | t (1 row) SELECT sparql.strstarts('"foobar"@en','"foo"@en'); strstarts ----------- t (1 row) SELECT sparql.strstarts('"foobar"^^','"foo"^^'); strstarts ----------- t (1 row) SELECT sparql.strstarts('"foobar"^^','"foo"'); strstarts ----------- t (1 row) SELECT sparql.strstarts('"foobar"','"foo"^^'); strstarts ----------- t (1 row) SELECT sparql.strstarts('"foobar"@en','"foo"'); strstarts ----------- t (1 row) SELECT sparql.strstarts('"foobar"@en','"foo"^^'); strstarts ----------- t (1 row) SELECT sparql.strstarts('foobar',''); strstarts ----------- t (1 row) SELECT sparql.strstarts('','xyz'); strstarts ----------- f (1 row) SELECT sparql.strstarts('foobar',NULL); strstarts ----------- NULL (1 row) SELECT sparql.strstarts(NULL,'xyz'); strstarts ----------- NULL (1 row) SELECT sparql.strstarts(NULL, NULL); strstarts ----------- NULL (1 row) SELECT sparql.strstarts(sparql.strlang('foobar','en'),'"foo"@fr'); strstarts ----------- NULL (1 row) SELECT sparql.strstarts(sparql.strlang('foobar','en'), sparql.strlang('foo','fr')); strstarts ----------- NULL (1 row) SELECT sparql.strstarts(sparql.strlang('foobar','en'), '"foo"^^'); strstarts ----------- t (1 row) SELECT sparql.strstarts(sparql.strlang('foobar','en'), sparql.strdt('foo','xsd:string')); strstarts ----------- t (1 row) SELECT sparql.strstarts('foobar', sparql.strdt('foo','xsd:string')); strstarts ----------- t (1 row) SELECT sparql.strstarts('foobar','"foo"^^'); strstarts ----------- t (1 row) SELECT sparql.strstarts('foobar', sparql.strlang('foo','it')); strstarts ----------- NULL (1 row) SELECT sparql.strstarts('foobar','"foo"@de'); strstarts ----------- NULL (1 row) /* STRENDS */ SELECT sparql.strends('"foobar"','"bar"'), sparql.strends('foobar','bar'); strends | strends ---------+--------- t | t (1 row) SELECT sparql.strends('"foobar"@en','"bar"@en'); strends --------- t (1 row) SELECT sparql.strends('"foobar"^^xsd:string', '"bar"^^xsd:string'); strends --------- t (1 row) SELECT sparql.strends('"foobar"^^xsd:string', '"bar"'); strends --------- t (1 row) SELECT sparql.strends('"foobar"', '"bar"^^xsd:string'); strends --------- t (1 row) SELECT sparql.strends('"foobar"@en', '"bar"'); strends --------- t (1 row) SELECT sparql.strends('"foobar"@en', '"bar"^^xsd:string'); strends --------- t (1 row) SELECT sparql.strends('foobar','xyz'); strends --------- f (1 row) SELECT sparql.strends('foobar',''); strends --------- t (1 row) SELECT sparql.strends('','xyz'); strends --------- f (1 row) SELECT sparql.strends('foobar',NULL); strends --------- NULL (1 row) SELECT sparql.strends(NULL,'xyz'); strends --------- NULL (1 row) SELECT sparql.strends(NULL, NULL); strends --------- NULL (1 row) SELECT sparql.strends('"foobar"@en','"bar"@fr'); strends --------- NULL (1 row) SELECT sparql.strends(sparql.strlang('foobar','en'),'"bar"@fr'); strends --------- NULL (1 row) SELECT sparql.strends(sparql.strlang('foobar','en'), '"bar"^^'); strends --------- t (1 row) SELECT sparql.strends(sparql.strlang('foobar','en'), sparql.strdt('bar','xsd:string')); strends --------- t (1 row) SELECT sparql.strends('foobar', sparql.strdt('bar','xsd:string')); strends --------- t (1 row) SELECT sparql.strends('foobar','"bar"^^'); strends --------- t (1 row) SELECT sparql.strends('foobar','"bar"@de'); strends --------- NULL (1 row) /* STRBEFORE */ SELECT sparql.strbefore('abc','b'), sparql.strbefore('"abc"','"b"'); strbefore | strbefore -----------+----------- "a" | "a" (1 row) SELECT sparql.strbefore('"abc"@en','bc'); strbefore ----------- "a"@en (1 row) SELECT sparql.strbefore('"abc"@en','"b"@cy'); strbefore ----------- NULL (1 row) SELECT sparql.strbefore('"abc"^^xsd:string',''), sparql.strbefore('"abc"^^xsd:string','""'); strbefore | strbefore -----------------------------------------------+----------------------------------------------- ""^^ | ""^^ (1 row) SELECT sparql.strbefore('abc','xyz'), sparql.strbefore('"abc"','"xyz"'); strbefore | strbefore -----------+----------- "" | "" (1 row) SELECT sparql.strbefore('"abc"@en', '"z"@en'); strbefore ----------- "" (1 row) SELECT sparql.strbefore('"abc"@en', '"z"'), sparql.strbefore('"abc"@en', 'z'); strbefore | strbefore -----------+----------- "" | "" (1 row) SELECT sparql.strbefore('"abc"@en', '""@en'); strbefore ----------- ""@en (1 row) SELECT sparql.strbefore('"abc"@en', '""'); strbefore ----------- ""@en (1 row) SELECT sparql.strbefore('"abc"^^','c'); strbefore ------------------------------------------------- "ab"^^ (1 row) SELECT sparql.strbefore('"abc"^^','"c"^^'); strbefore ------------------------------------------------- "ab"^^ (1 row) SELECT sparql.strbefore('"abc"^^','"c"^^xsd:string'); strbefore ------------------------------------------------- "ab"^^ (1 row) SELECT sparql.strbefore('"abc"^^http://www.w3.org/2001/XMLSchema#string','"c"^^'); ERROR: invalid input syntax for type rdfnode: ""abc"^^http://www.w3.org/2001/XMLSchema#string" at character 25 SELECT sparql.strbefore('"abc"^^xsd:string','"c"^^'); strbefore ------------------------------------------------- "ab"^^ (1 row) SELECT sparql.strbefore('"abc"@fr','"b"^^xsd:string'); strbefore ----------- "a"@fr (1 row) SELECT sparql.strbefore('"abc"^^','"b"@de'); strbefore ----------- NULL (1 row) SELECT sparql.strbefore('"abc"@en','"b"^^'); strbefore ----------- NULL (1 row) SELECT sparql.strbefore('abc', NULL); strbefore ----------- NULL (1 row) SELECT sparql.strbefore(NULL, 'xyz'); strbefore ----------- NULL (1 row) SELECT sparql.strbefore(NULL, NULL); strbefore ----------- NULL (1 row) SELECT sparql.strbefore('abc', ''); strbefore ----------- "" (1 row) SELECT sparql.strbefore('"abc"', ''); strbefore ----------- "" (1 row) SELECT sparql.strbefore('', 'xyz'); strbefore ----------- "" (1 row) SELECT sparql.strbefore('', ''); strbefore ----------- "" (1 row) SELECT sparql.strbefore('""','""'); strbefore ----------- "" (1 row) SELECT sparql.strbefore('"abc"', '"b"^^xsd:integer'); strbefore ----------- NULL (1 row) /* STRAFTER */ SELECT sparql.strafter('"abc"','"b"'); strafter ---------- "c" (1 row) SELECT sparql.strafter('"abc"@en','ab'); strafter ---------- "c"@en (1 row) SELECT sparql.strafter('"abc"@en','"b"@cy'); strafter ---------- NULL (1 row) SELECT sparql.strafter('"abc"^^xsd:string','""'); strafter -------------------------------------------------- "abc"^^ (1 row) SELECT sparql.strafter('"abc"^^','b'); strafter ------------------------------------------------ "c"^^ (1 row) SELECT sparql.strafter('"abc"^^','"b"^^'); strafter ------------------------------------------------ "c"^^ (1 row) SELECT sparql.strafter('"abc"^^','"b"^^xsd:string'); strafter ------------------------------------------------ "c"^^ (1 row) SELECT sparql.strafter('"abc"^^http://www.w3.org/2001/XMLSchema#string','"b"^^'); ERROR: invalid input syntax for type rdfnode: ""abc"^^http://www.w3.org/2001/XMLSchema#string" at character 24 SELECT sparql.strafter('"abc"^^xsd:string','"b"^^'); strafter ------------------------------------------------ "c"^^ (1 row) SELECT sparql.strafter('"abc"@fr','"b"^^xsd:string'); strafter ---------- "c"@fr (1 row) SELECT sparql.strafter('"abc"','"xyz"'); strafter ---------- "" (1 row) SELECT sparql.strafter('"abc"@en', '"z"@en'); strafter ---------- "" (1 row) SELECT sparql.strafter('"abc"@en', '"z"'); strafter ---------- "" (1 row) SELECT sparql.strafter('"abc"@en', '""@en'); strafter ---------- "abc"@en (1 row) SELECT sparql.strafter('"abc"@en', '""'); strafter ---------- "abc"@en (1 row) SELECT sparql.strafter('abc','b'); strafter ---------- "c" (1 row) SELECT sparql.strafter('abc','xyz'); strafter ---------- "" (1 row) SELECT sparql.strafter('abc', NULL); strafter ---------- NULL (1 row) SELECT sparql.strafter(NULL, 'xyz'); strafter ---------- NULL (1 row) SELECT sparql.strafter(NULL, NULL); strafter ---------- NULL (1 row) SELECT sparql.strafter('abc', ''); strafter ---------- "abc" (1 row) SELECT sparql.strafter('', 'xyz'); strafter ---------- "" (1 row) SELECT sparql.strafter('', ''); strafter ---------- "" (1 row) /* CONTAINS */ SELECT sparql.contains('"foobar"', '"bar"'), sparql.contains('foobar', 'bar'); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('"foobar"@en', '"foo"@en'), sparql.contains(sparql.strlang('"foobar"','en'), sparql.strlang('foo','en')); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('"foobar"^^xsd:string', '"bar"^^xsd:string'), sparql.contains(sparql.strdt('"foobar"','xsd:string'), sparql.strdt('"bar"','xsd:string')); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('"foobar"^^xsd:string', '"foo"'), sparql.contains('"foobar"^^xsd:string', 'foo'); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('"foobar"', '"bar"^^xsd:string'), sparql.contains('foobar', '"bar"^^xsd:string'); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('"foobar"@en', '"foo"'), sparql.contains('"foobar"@en', 'foo'); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('"foobar"@en', '"bar"^^xsd:string'); contains ---------- t (1 row) SELECT sparql.contains('"foobar"', '""'), sparql.contains('foobar', ''); contains | contains ----------+---------- t | t (1 row) SELECT sparql.contains('""', '"foo"'), sparql.contains('', 'foo'); contains | contains ----------+---------- f | f (1 row) SELECT sparql.contains('"foobar"', NULL), sparql.contains('foobar', NULL); contains | contains ----------+---------- NULL | NULL (1 row) SELECT sparql.contains(NULL, '"foo"'), sparql.contains(NULL, 'foo'); contains | contains ----------+---------- NULL | NULL (1 row) SELECT sparql.contains(NULL, NULL); contains ---------- NULL (1 row) SELECT sparql.contains('"foobar"@en', '"foo"@fr'); contains ---------- NULL (1 row) SELECT sparql.contains('"123"^^', '"2"'); contains ---------- NULL (1 row) SELECT sparql.contains('"abc"', '"def"@en'); contains ---------- NULL (1 row) SELECT sparql.contains('"foobar"@en', '"bar"^^xsd:string'); contains ---------- t (1 row) SELECT sparql.contains('"foobar"^^xsd:string', '"foo"'); contains ---------- t (1 row) /* LANGMATCHES */ SELECT sparql.langmatches(sparql.lang('"hello"@en'), '"en"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@EN-US'), '"en-us"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@fr'), '"FR"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en'), '"*"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@fr-ca'), '"*"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en-us'), '"en-*"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en'), '"en-*"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@fr-ca'), '"fr-*"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@fr'), '"en"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en-us'), '"fr-*"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en'), '"en-us-*"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"'), '"en"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"'), '"*"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('""@en'), '"en"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('""'), '"*"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"^^xsd:string'), '"en"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"^^xsd:string'), '"*"'); langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en'), '"en"^^xsd:string'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en'), '"*"^^xsd:string'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en-us'), '"EN-*"^^xsd:string'); langmatches ------------- t (1 row) SELECT sparql.langmatches('', ''); langmatches ------------- f (1 row) SELECT sparql.langmatches('en', ''); langmatches ------------- f (1 row) SELECT sparql.langmatches('', '"en"'); langmatches ------------- f (1 row) SELECT sparql.langmatches('en', '"en"'); langmatches ------------- t (1 row) SELECT sparql.langmatches(sparql.lang('"hello"@en'), ''); langmatches ------------- f (1 row) SELECT sparql.langmatches('', '"*"'); langmatches ------------- f (1 row) SELECT sparql.langmatches('en-US', 'en'); langmatches ------------- t (1 row) /* "*" must not match empty language tags */ SELECT sparql.langmatches('', '"*"'); -- f langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"'), '"*"'); -- f (no lang tag) langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('""'), '"*"'); -- f (empty literal, no lang) langmatches ------------- f (1 row) SELECT sparql.langmatches(sparql.lang('"hello"^^xsd:string'), '"*"'); -- f (typed, no lang) langmatches ------------- f (1 row) /* subtag prefix matching */ SELECT sparql.langmatches('en-Latn-US', 'en'); -- t langmatches ------------- t (1 row) SELECT sparql.langmatches('zh-Hant-TW', 'zh'); -- t langmatches ------------- t (1 row) SELECT sparql.langmatches('zh-Hant-TW', 'zh-Hant'); -- t langmatches ------------- t (1 row) SELECT sparql.langmatches('zh-Hant-TW', 'zh-Hans'); -- f langmatches ------------- f (1 row) /* ISBLANK */ SELECT sparql.isblank('_:b1'); isblank --------- t (1 row) SELECT sparql.isblank('_:node123'); isblank --------- t (1 row) SELECT sparql.isblank(''); isblank --------- f (1 row) SELECT sparql.isblank('"hello"'); isblank --------- f (1 row) SELECT sparql.isblank('"hello"@en'); isblank --------- f (1 row) SELECT sparql.isblank('"42"^^xsd:integer'); isblank --------- f (1 row) SELECT sparql.isblank('_notblank'); isblank --------- f (1 row) SELECT sparql.isblank(''); isblank --------- f (1 row) SELECT sparql.isblank('b1'); isblank --------- f (1 row) SELECT sparql.isblank('_:'); isblank --------- f (1 row) SELECT sparql.isblank('_'); isblank --------- f (1 row) SELECT sparql.isblank(' '); isblank --------- f (1 row) SELECT sparql.isblank(''); isblank --------- f (1 row) SELECT sparql.isblank(NULL); isblank --------- f (1 row) SELECT sparql.isblank('"_:b1"'); isblank --------- f (1 row) /* ISNUMERIC */ SELECT sparql.isnumeric('12'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('"12"'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('"12"^^xsd:nonNegativeInteger'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"1200"^^xsd:byte'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric(''); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('"12"^^xsd:integer'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:positiveInteger'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:negativeInteger'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('"12"^^xsd:nonPositiveInteger'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('"12"^^xsd:long'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:int'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:short'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:unsignedLong'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:unsignedInt'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:unsignedShort'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:unsignedByte'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:double'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:float'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric('"12"^^xsd:decimal'); isnumeric ----------- t (1 row) SELECT sparql.isnumeric(''); isnumeric ----------- f (1 row) SELECT sparql.isnumeric(' '); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('""'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric('" "'); isnumeric ----------- f (1 row) SELECT sparql.isnumeric(NULL); isnumeric ----------- f (1 row) /* ISLITERAL */ SELECT sparql.isliteral('"hello"'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"123"'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"12"^^xsd:integer'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"12"^^xsd:nonNegativeInteger'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"12.34"^^xsd:double'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"true"^^xsd:boolean'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"abc"^^'); -- true isliteral ----------- t (1 row) SELECT sparql.isliteral('"hello"@en'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"bonjour"@fr'); isliteral ----------- t (1 row) SELECT sparql.isliteral('12'); isliteral ----------- t (1 row) SELECT sparql.isliteral(''); isliteral ----------- f (1 row) SELECT sparql.isliteral('_:bnode'); isliteral ----------- f (1 row) SELECT sparql.isliteral('""'); isliteral ----------- t (1 row) SELECT sparql.isliteral('"_:bnode"'); isliteral ----------- t (1 row) SELECT sparql.isliteral(''); isliteral ----------- t (1 row) SELECT sparql.isliteral('" "'); isliteral ----------- t (1 row) SELECT sparql.isliteral('""'); isliteral ----------- t (1 row) SELECT sparql.isliteral(NULL); isliteral ----------- f (1 row) /* BNODE */ SELECT sparql.isblank(sparql.bnode()); isblank --------- t (1 row) SELECT sparql.bnode('xyz'); bnode ------- _:xyz (1 row) SELECT sparql.bnode('xyz'); bnode ------- _:xyz (1 row) SELECT sparql.bnode('"xyz"'); bnode ------- _:xyz (1 row) SELECT sparql.bnode('"xyz"@en'); bnode ------- _:xyz (1 row) SELECT sparql.bnode('"xyz"^^xsd:string'); bnode ------- _:xyz (1 row) SELECT sparql.bnode('hello world'); bnode --------------- _:hello_world (1 row) SELECT sparql.bnode('123!'); bnode -------- _:123_ (1 row) SELECT sparql.bnode(''); bnode ------- NULL (1 row) SELECT sparql.bnode('_:bnode'); bnode --------- _:bnode (1 row) SELECT sparql.bnode(''); bnode ------- NULL (1 row) SELECT sparql.bnode(NULL); bnode ------- NULL (1 row) /* UUID (not pushable) */ SELECT sparql.uuid()::text ~ '^$'; ?column? ---------- t (1 row) /* STRUUID() (not pushable) */ SELECT sparql.struuid()::text ~ '^"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"$' AS struuid_format; struuid_format ---------------- t (1 row) /* LCASE */ SELECT sparql.lcase('BAR'); lcase ------- "bar" (1 row) SELECT sparql.lcase('"BAR"'); lcase ------- "bar" (1 row) SELECT sparql.lcase('"BAR"@en'), sparql.lcase(sparql.strlang('BAR','en')); lcase | lcase ----------+---------- "bar"@en | "bar"@en (1 row) SELECT sparql.lcase('"BAR"^^xsd:string'), sparql.lcase(sparql.strdt('BAR','xsd:string')); lcase | lcase --------------------------------------------------+-------------------------------------------------- "bar"^^ | "bar"^^ (1 row) SELECT sparql.lcase('"ŁÓÒÁÀÂÆÄÉÈÊÍÌÎÓÒØÔÖÚÙÛÜÞ"'); lcase ---------------------------- "łóòáàâæäéèêíìîóòøôöúùûüþ" (1 row) SELECT sparql.lcase('"ŁÓÒÁÀÂÆÄÉÈÊÍÌÎÓÒØÔÖÚÙÛÜÞ"^^'); lcase ----------------------------------------------------------------------- "łóòáàâæäéèêíìîóòøôöúùûüþ"^^ (1 row) SELECT sparql.lcase('"ŁÓÒÁÀÂÆÄÉÈÊÍÌÎÓÒØÔÖÚÙÛÜÞ"@de'); lcase ------------------------------- "łóòáàâæäéèêíìîóòøôöúùûüþ"@de (1 row) SELECT sparql.lcase(''); ERROR: LCASE does not allow IRIs: SELECT sparql.lcase('_:xyz'); ERROR: LCASE does not allow blank nodes: _:xyz SELECT sparql.lcase(sparql.bnode('foo')); ERROR: LCASE does not allow blank nodes: _:foo SELECT sparql.lcase('123'); lcase ------- "123" (1 row) SELECT sparql.lcase('"123"'); lcase ------- "123" (1 row) SELECT sparql.lcase('"123"^^xsd:integer'); ERROR: LCASE does not allow non-string literals: SELECT sparql.lcase('"1990-10-03"^^xsd:date'); ERROR: LCASE does not allow non-string literals: SELECT sparql.lcase('"!§$%&/()?ß}{}[]°^|<>*"'); lcase ------------------------- "!§$%&/()?ß}{}[]°^|<>*" (1 row) SELECT sparql.lcase(NULL); lcase ------- NULL (1 row) SELECT sparql.lcase(''); lcase ------- "" (1 row) SELECT sparql.lcase('""'); lcase ------- "" (1 row) SELECT sparql.lcase('" "'); lcase ------- " " (1 row) SELECT sparql.lcase(' '); lcase ------- " " (1 row) /* UCASE */ SELECT sparql.ucase('bar'); ucase ------- "BAR" (1 row) SELECT sparql.ucase('"bar"'); ucase ------- "BAR" (1 row) SELECT sparql.ucase('"bar"@en'), sparql.ucase(sparql.strlang('bar','en')); ucase | ucase ----------+---------- "BAR"@en | "BAR"@en (1 row) SELECT sparql.ucase('"bar"^^xsd:string'), sparql.ucase(sparql.strdt('bar','xsd:string')); ucase | ucase --------------------------------------------------+-------------------------------------------------- "BAR"^^ | "BAR"^^ (1 row) SELECT sparql.ucase('"łóòáàâæäéèêíìîóòøôöúùûüþ"'); ucase ---------------------------- "ŁÓÒÁÀÂÆÄÉÈÊÍÌÎÓÒØÔÖÚÙÛÜÞ" (1 row) SELECT sparql.ucase('"łóòáàâæäéèêíìîóòøôöúùûüþ"@de'); ucase ------------------------------- "ŁÓÒÁÀÂÆÄÉÈÊÍÌÎÓÒØÔÖÚÙÛÜÞ"@de (1 row) SELECT sparql.ucase('"łóòáàâæäéèêíìîóòøôöúùûüþ"^^'); ucase ----------------------------------------------------------------------- "ŁÓÒÁÀÂÆÄÉÈÊÍÌÎÓÒØÔÖÚÙÛÜÞ"^^ (1 row) SELECT sparql.ucase(''); ERROR: UCASE does not allow IRIs: SELECT sparql.ucase('_:xyz'); ERROR: UCASE does not allow blank nodes: _:xyz SELECT sparql.ucase(sparql.bnode('foo')); ERROR: UCASE does not allow blank nodes: _:foo SELECT sparql.ucase('123'); ucase ------- "123" (1 row) SELECT sparql.ucase('"123"'); ucase ------- "123" (1 row) SELECT sparql.ucase('"123"^^xsd:integer'); ERROR: UCASE does not allow non-string literals: SELECT sparql.ucase('"1990-10-03"^^xsd:date'); ERROR: UCASE does not allow non-string literals: SELECT sparql.ucase('"!§$%&/()?ß}{}[]°^|<>*"'); ucase ------------------------- "!§$%&/()?ß}{}[]°^|<>*" (1 row) SELECT sparql.ucase(NULL); ucase ------- NULL (1 row) SELECT sparql.ucase(''); ucase ------- "" (1 row) SELECT sparql.ucase('""'); ucase ------- "" (1 row) SELECT sparql.ucase('" "'); ucase ------- " " (1 row) SELECT sparql.ucase(' '); ucase ------- " " (1 row) /* STRLEN */ SELECT sparql.strlen('chat'), sparql.strlen('"chat"'); strlen | strlen --------+-------- 4 | 4 (1 row) SELECT sparql.strlen('"chat"@en'), sparql.strlen(sparql.strlang('chat','en')); strlen | strlen --------+-------- 4 | 4 (1 row) SELECT sparql.strlen('"chat"^^xsd:string'), sparql.strlen(sparql.strdt('chat','xsd:string')); strlen | strlen --------+-------- 4 | 4 (1 row) SELECT sparql.strlen('""'), sparql.strlen(''); strlen | strlen --------+-------- 0 | 0 (1 row) SELECT sparql.strlen('" "'), sparql.strlen(' '); strlen | strlen --------+-------- 1 | 1 (1 row) SELECT sparql.strlen('"łø"'), sparql.strlen('łø'); strlen | strlen --------+-------- 2 | 2 (1 row) SELECT sparql.strlen('"łø"@de'), sparql.strlen(sparql.strlang('łø','de')); strlen | strlen --------+-------- 2 | 2 (1 row) SELECT sparql.strlen('"łø"@de'), sparql.strlen(sparql.strdt('łø','')); strlen | strlen --------+-------- 2 | 2 (1 row) SELECT sparql.strlen(NULL); strlen -------- NULL (1 row) /* SUBSTR */ SELECT sparql.substr('"foobar"', 4), sparql.substr('foobar', 4); substr | substr --------+-------- "bar" | "bar" (1 row) SELECT sparql.substr('"foobar"@en', 4), sparql.substr(sparql.strlang('foobar','en'), 4); substr | substr ----------+---------- "bar"@en | "bar"@en (1 row) SELECT sparql.substr('"foobar"^^xsd:string', 4), sparql.substr(sparql.strdt('foobar','xsd:string'), 4); substr | substr --------------------------------------------------+-------------------------------------------------- "bar"^^ | "bar"^^ (1 row) SELECT sparql.substr('"foobar"', 4, 1), sparql.substr('foobar', 4, 1); substr | substr --------+-------- "b" | "b" (1 row) SELECT sparql.substr('"foobar"@en', 4, 1), sparql.substr(sparql.strlang('foobar','en'), 4, 1); substr | substr --------+-------- "b"@en | "b"@en (1 row) SELECT sparql.substr('"foobar"^^xsd:string', 4, 1), sparql.substr(sparql.strdt('foobar','xsd:string'), 4, 1); substr | substr ------------------------------------------------+------------------------------------------------ "b"^^ | "b"^^ (1 row) SELECT sparql.substr('""', 42); substr -------- "" (1 row) SELECT sparql.substr('', 42); substr -------- "" (1 row) SELECT sparql.substr(NULL, 42); substr -------- NULL (1 row) SELECT sparql.substr('"foo"', NULL); substr -------- NULL (1 row) SELECT sparql.substr('"łóòáàâæäéèêíìîóòøôöúùûüþ"@de'::rdfnode, 1, 24); substr ------------------------------- "łóòáàâæäéèêíìîóòøôöúùûüþ"@de (1 row) SELECT sparql.substr('"łóòáàâæäéèêíìîóòøôöúùûüþ"'::rdfnode, 1, 24); substr ---------------------------- "łóòáàâæäéèêíìîóòøôöúùûüþ" (1 row) SELECT sparql.substr('"łóòáàâæäéèêíìîóòøôöúùûüþ"^^'::rdfnode, 1, 24); substr ----------------------------------------------------------------------- "łóòáàâæäéèêíìîóòøôöúùûüþ"^^ (1 row) SELECT sparql.substr('"foobar"', 0); -- SPARQL is 1-indexed; pos 0 is defined as "" or same as pos 1 per XPath substr ---------- "foobar" (1 row) SELECT sparql.substr('"foobar"', 1, 0); -- zero-length -> "" substr -------- "" (1 row) SELECT sparql.substr('"foobar"', 10); -- beyond length -> "" substr -------- "" (1 row) SELECT sparql.substr('"foobar"', 2, 100); -- length beyond string end -> "oobar" substr --------- "oobar" (1 row) SELECT sparql.substr('"foobar"', NULL, 3); -- NULL arg substr -------- NULL (1 row) /* XPath fn:substring, which SPARQL SUBSTR follows, selects the characters whose * position falls in [start, start + length). A start below 1 is not an error: * it simply places part of that interval before the string, and only the * overlap is returned. Start 0 with length 2 therefore yields one character, * not two, and a start far enough to the left yields nothing at all. */ SELECT s AS start, l AS len, sparql.lex(sparql.substr('"foobar"', s, l)) AS result FROM (VALUES (0,2),(0,1),(0,0),(-1,3),(-2,3),(0,7),(-5,20),(1,2)) t(s,l); start | len | result -------+-----+-------- 0 | 2 | f 0 | 1 | 0 | 0 | -1 | 3 | f -2 | 3 | 0 | 7 | foobar -5 | 20 | foobar 1 | 2 | fo (8 rows) /* without a length the interval is unbounded to the right, so any start at or * before the first character returns the whole string */ SELECT s AS start, sparql.lex(sparql.substr('"foobar"', s)) AS result FROM (VALUES (0),(-3),(1),(7)) t(s); start | result -------+-------- 0 | foobar -3 | foobar 1 | foobar 7 | (4 rows) /* CONCAT */ SELECT sparql.concat('"foo"', '"bar"'), sparql.concat('foo', 'bar'); concat | concat ----------+---------- "foobar" | "foobar" (1 row) SELECT sparql.concat('"foo"@en', '"bar"@en'), sparql.concat(sparql.strlang('foo','en'), sparql.strlang('bar','en')); concat | concat -------------+------------- "foobar"@en | "foobar"@en (1 row) SELECT sparql.concat('"foo"^^xsd:string', '"bar"^^xsd:string'), sparql.concat(sparql.strdt('foo','xsd:string'), sparql.strdt('bar','xsd:string')); concat | concat -----------------------------------------------------+----------------------------------------------------- "foobar"^^ | "foobar"^^ (1 row) SELECT sparql.concat('"foo"', '"bar"^^xsd:string'), sparql.concat('foo', sparql.strdt('bar','xsd:string')); concat | concat ----------+---------- "foobar" | "foobar" (1 row) SELECT sparql.concat('"foo"@en', '"bar"'), sparql.concat(sparql.strlang('foo','en'), 'bar'); concat | concat ----------+---------- "foobar" | "foobar" (1 row) SELECT sparql.concat('"foo"@en', '"bar"^^xsd:string'), sparql.concat(sparql.strlang('foo','en'), sparql.strdt('bar','xsd:string')); concat | concat ----------+---------- "foobar" | "foobar" (1 row) SELECT sparql.concat(NULL, 'bar'), sparql.concat('foo', NULL), sparql.concat(NULL, NULL); concat | concat | concat --------+--------+-------- NULL | NULL | NULL (1 row) SELECT sparql.concat('foo', ''), sparql.concat('', 'bar'), sparql.concat('', ''), sparql.concat('""', '""'); concat | concat | concat | concat --------+--------+--------+-------- "foo" | "bar" | "" | "" (1 row) SELECT sparql.concat('"foo"^^foo:bar', 'bar'), sparql.concat('"foo"', '"bar"^^foo:bar'); concat | concat ----------+---------- "foobar" | "foobar" (1 row) SELECT sparql.concat('"foo"@en','"&"@en', '"bar"@en'); concat -------------- "foo&bar"@en (1 row) SELECT sparql.concat('"foo"^^xsd:string','"&"^^xsd:string', '"bar"^^xsd:string'); concat ------------------------------------------------------ "foo&bar"^^ (1 row) SELECT sparql.concat('"foo"','"&"', '"bar"'); concat ----------- "foo&bar" (1 row) SELECT sparql.concat('"foo"^^xsd:string','"&"^^xsd:string', NULL); concat -------- NULL (1 row) SELECT sparql.concat('"foo"@en','"bar"@de'); concat ---------- "foobar" (1 row) SELECT sparql.concat('"foo"^^','"bar"^^'); concat ---------- "foobar" (1 row) SELECT sparql.concat('"foo"^^','"bar"'); concat ---------- "foobar" (1 row) SELECT sparql.concat('"foo"@en', '"bar"@de'); concat ---------- "foobar" (1 row) SELECT sparql.concat('"a"@en', '"b"@en', '"c"@fr'); -- should be "abc" (plain, not @en) concat -------- "abc" (1 row) SELECT sparql.concat(NULL, NULL, NULL, NULL); concat -------- NULL (1 row) /* REPLACE */ SELECT sparql.replace('"abcd"', '"b"', '"Z"'), sparql.replace('abcd', 'b', 'Z'); replace | replace ---------+--------- "aZcd" | "aZcd" (1 row) SELECT sparql.replace('"abab"', '"B"', '"Z"','"i"'), sparql.replace('abab', 'B', 'Z','i'); replace | replace ---------+--------- "aZaZ" | "aZaZ" (1 row) SELECT sparql.replace('"abab"', '"B."', '"Z"','"i"'), sparql.replace('abab', 'B.', 'Z','i'); replace | replace ---------+--------- "aZb" | "aZb" (1 row) SELECT sparql.replace('"abcd"@en', '"b"', '"Z"'), sparql.replace(sparql.strlang('abcd','en'), 'b', 'Z'); replace | replace -----------+----------- "aZcd"@en | "aZcd"@en (1 row) SELECT sparql.replace('"abab"^^xsd:string', '"B"', '"Z"','"i"'), sparql.replace(sparql.strdt('abab','xsd:string'), 'B', 'Z','i'); replace | replace ---------+--------- "aZaZ" | "aZaZ" (1 row) SELECT sparql.replace('"abcd"', '"b"@en', '"Z"'), sparql.replace('abcd', sparql.strlang('b','en'), 'Z'); replace | replace ---------+--------- "aZcd" | "aZcd" (1 row) SELECT sparql.replace('"abab"', '"B"^^xsd:string', '"Z"','"i"'), sparql.replace('abab', sparql.strdt('B','xsd:string'), 'Z','i'); replace | replace ---------+--------- "aZaZ" | "aZaZ" (1 row) SELECT sparql.replace('""', '"b"', '"Z"'), sparql.replace('', 'b', 'Z'); replace | replace ---------+--------- "" | "" (1 row) SELECT sparql.replace('"abcd"', '""', '"Z"'), sparql.replace('abcd', '', 'Z'); replace | replace -------------+------------- "ZaZbZcZdZ" | "ZaZbZcZdZ" (1 row) SELECT sparql.replace('"abcd"', '"b"', '""'), sparql.replace('abcd', 'b', ''); replace | replace ---------+--------- "acd" | "acd" (1 row) SELECT sparql.replace('"ab\"cd"', '"b"', '"Z"'), sparql.replace('ab\"cd', 'b', 'Z'); replace | replace ----------+---------- "aZ\"cd" | "aZ\"cd" (1 row) SELECT sparql.replace(NULL, 'b', 'Z'), sparql.replace('abcd', NULL, 'Z'), sparql.replace('abcd', 'b', NULL), sparql.replace('abcd', 'b', 'Z', NULL); replace | replace | replace | replace ---------+---------+---------+--------- NULL | NULL | NULL | NULL (1 row) SELECT sparql.replace('', 'a', 'Z'); -- Empty input string replace --------- "" (1 row) SELECT sparql.replace('abcd', '', 'Z'); -- Empty pattern replace ------------- "ZaZbZcZdZ" (1 row) SELECT sparql.replace('abcd', 'a', ''); -- Empty replacement replace --------- "bcd" (1 row) SELECT sparql.replace('', '', 'Z'); -- Empty pattern and replacement replace --------- "Z" (1 row) SELECT sparql.replace('abcd', 'a', 'Z'); -- Pattern at the beginning replace --------- "Zbcd" (1 row) SELECT sparql.replace('abcd', 'd', 'Z'); -- Pattern at the end replace --------- "abcZ" (1 row) SELECT sparql.replace('abcd', 'bc', 'Z'); -- Pattern in the middle replace --------- "aZd" (1 row) SELECT sparql.replace('aabbcc', 'b', 'Z'); -- Multiple occurrences of the pattern replace ---------- "aaZZcc" (1 row) SELECT sparql.replace('Abcd', 'a', 'Z'); -- Case mismatch pattern replace --------- "Abcd" (1 row) SELECT sparql.replace('abcd', 'A', 'Z'); -- Case mismatch pattern (uppercase in input) replace --------- "abcd" (1 row) SELECT sparql.replace('abcd', 'A', 'Z','i'); -- Case-insensitive replacement replace --------- "Zbcd" (1 row) SELECT sparql.replace('"abcd"', '"b"', '"Z"'); -- Special characters inside quotes replace --------- "aZcd" (1 row) SELECT sparql.replace('ab\cd', 'b\\', 'Z'); -- Escaped backslashes replace --------- "aZcd" (1 row) SELECT sparql.replace('ab"cd', '"b"', '"Z"'); -- Quotes in the input replace ---------- "aZ\"cd" (1 row) SELECT sparql.replace('ab"cd', 'b"', 'Z'); -- Quotes in pattern replace --------- "aZcd" (1 row) SELECT sparql.replace('abcdef', 'bc', 'ZY'); -- Multi-character pattern in the middle replace ---------- "aZYdef" (1 row) SELECT sparql.replace('abc abc', 'abc', 'XYZ'); -- Multiple occurrences of a multi-character pattern replace ----------- "XYZ XYZ" (1 row) SELECT sparql.replace('abcd', 'a', 'Z'); -- Pattern at the start replace --------- "Zbcd" (1 row) SELECT sparql.replace('abcd', 'd', 'Z'); -- Pattern at the end replace --------- "abcZ" (1 row) SELECT sparql.replace('abcdabcd', 'abcd', 'XYZ'); -- Pattern at the start and repeated replace ---------- "XYZXYZ" (1 row) SELECT sparql.replace(NULL, 'a', 'Z'); -- Input is NULL replace --------- NULL (1 row) SELECT sparql.replace('abcd', NULL, 'Z'); -- Pattern is NULL replace --------- NULL (1 row) SELECT sparql.replace('abcd', 'a', NULL); -- Replacement is NULL replace --------- NULL (1 row) SELECT sparql.replace(NULL, NULL, NULL); -- All NULLs replace --------- NULL (1 row) SELECT sparql.replace('"ab\"cd"', '"b"', '"Z"'); -- Escaped double quotes replace ---------- "aZ\"cd" (1 row) SELECT sparql.replace('"ab\"cd"', 'b', 'Z'); -- Escaped double quotes, no pattern replace ---------- "aZ\"cd" (1 row) /* * SPARQL 1.1 17.4.3.15 defines REPLACE as fn:replace, whose replacement * syntax (XPath F&O 7.6.3) writes a captured group "$N" and a literal dollar * "\$". PostgreSQL's regexp_replace writes the group "\N" instead, and reads * a bare "$" as itself, so the replacement has to be rewritten between the * two. Fuseki, GraphDB and Virtuoso agree on every case below. */ SELECT sparql.replace('"abab"', '"a(b)"', '"[$1]"'); -- captured group replace ---------- "[b][b]" (1 row) SELECT sparql.replace('"2025-01-02"', '"([0-9]{4})-([0-9]{2})-([0-9]{2})"', '"$3/$2/$1"'); -- several groups, reordered replace -------------- "02/01/2025" (1 row) SELECT sparql.replace('"aXb"', '"X"', '"\$"'); -- escaped dollar is a dollar replace --------- "a$b" (1 row) SELECT sparql.replace('"aXb"', '"X"', '"\\"'); -- escaped backslash is a backslash replace --------- "a\b" (1 row) SELECT sparql.replace('"aXb"', '"(X)"', '"\1"'); -- "\1" is no group reference replace --------- "a1b" (1 row) SELECT sparql.replace('"aXb"', '"X"', '"&"'); -- "&" is an ordinary character replace --------- "a&b" (1 row) SELECT sparql.replace('"abab"', '"a(b)"', '"[$1]"', '"i"'); -- the four-argument form replace ---------- "[b][b]" (1 row) SELECT sparql.replace('abab', 'a(b)', '[$1]'); -- and the text overload replace ---------- "[b][b]" (1 row) SELECT sparql.replace('"abcd"@en', 'a', 'Z'); -- Language-tagged literal replace ----------- "Zbcd"@en (1 row) SELECT sparql.replace('"abcd"^^xsd:string', 'a', 'Z'); -- Datatype-literal (xsd:string) replace --------- "Zbcd" (1 row) SELECT sparql.replace('"abcd"^^xsd:date', 'a', 'Z'); -- Datatype-literal (xsd:date) replace ------------------------------------------------- "Zbcd"^^ (1 row) SELECT sparql.replace('ababab', 'ab', 'XY', 'g'); -- Global replacement replace ---------- "XYXYXY" (1 row) SELECT sparql.replace('ababab', 'ab', 'XY'); -- Non-global replacement (should only replace first occurrence) replace ---------- "XYXYXY" (1 row) SELECT sparql.replace('abcd', '', 'Z', 'g'); -- Empty pattern with global flag replace ------------- "ZaZbZcZdZ" (1 row) SELECT sparql.replace('abcd', '', 'Z'); -- Empty pattern without global flag replace ------------- "ZaZbZcZdZ" (1 row) SELECT sparql.replace('abcd', 'z', 'Z'); -- No pattern match replace --------- "abcd" (1 row) SELECT sparql.replace('abcd', 'xy', 'Z'); -- No match for multi-character pattern replace --------- "abcd" (1 row) SELECT sparql.replace('a' || repeat('b', 1000) || 'c', 'b'::text, 'Z'::text); -- Long string with repeated pattern replace ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- "aZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZc" (1 row) SELECT sparql.replace('abcd', 'abcd', 'XYZ'); -- Pattern matches the entire string replace --------- "XYZ" (1 row) SELECT sparql.replace('abcdabcd', 'abcd', 'XYZ'); -- Pattern matches at the start replace ---------- "XYZXYZ" (1 row) SELECT sparql.replace('""', '"b"', '"Z"'); -- Empty literal as input replace --------- "" (1 row) SELECT sparql.replace('"b"', '""', '"Z"'); -- Empty pattern in replacement replace --------- "ZbZ" (1 row) SELECT sparql.replace('abcd', 'a.b', 'Z', 'g'); -- Dot in pattern (regex) replace --------- "abcd" (1 row) SELECT sparql.replace('abcd', '[a-b]', 'Z', 'g'); -- Range in regex pattern replace --------- "ZZcd" (1 row) SELECT sparql.replace('abcd', '(ab)', 'Z', 'g'); -- Group in regex pattern replace --------- "Zcd" (1 row) SELECT sparql.replace('abc.def', '[.]', 'X', 'g'); -- character class containing a literal dot, matches only the . replace ----------- "abcXdef" (1 row) SELECT sparql.replace('abc.def', '.', 'X', 'g'); -- regex wildcard, matches any character, so all 7 characters are replaced replace ----------- "XXXXXXX" (1 row) /* The result is built from lexical content, so it must be quoted as a literal * rather than read back as a serialised term: a replacement that happens to * look like an IRI or to carry a language tag is still just text. Content * ending in a backslash needs the backslash protected, or it would escape the * closing quote and the literal would not end where it appears to. */ SELECT sparql.replace('"x"', 'x', '') AS looks_like_iri; looks_like_iri -------------------------- "" (1 row) SELECT sparql.replace('"x"', 'x', 'y"@en') AS looks_like_lang_tag; looks_like_lang_tag --------------------- "y\"@en" (1 row) SELECT sparql.replace('"x"', 'x', 'y"^^xsd:date') AS looks_like_datatype; looks_like_datatype --------------------- "y\"^^xsd:date" (1 row) SELECT sparql.replace('"a"', 'a', 'b\\') AS trailing_backslash; trailing_backslash -------------------- "b\\" (1 row) /* the language tag and datatype of the first argument survive in every * overload, including the four-argument one */ SELECT sparql.replace('"HELLO"@en', 'hel', 'X', 'i') AS four_arg_lang; four_arg_lang --------------- "XLO"@en (1 row) SELECT sparql.replace('"ABCD"^^xsd:date', 'ab', 'Z', 'i') AS four_arg_datatype; four_arg_datatype ------------------------------------------------ "ZCD"^^ (1 row) SELECT sparql.replace('"abcd"^^xsd:string', 'a', 'Z') AS xsd_string_becomes_simple; xsd_string_becomes_simple --------------------------- "Zbcd" (1 row) /* cstring_to_rdfliteral() ownership. * * concat(), lcase(), ucase(), substr() and strafter() all pfree() the buffer * they hand to cstring_to_rdfliteral() right after storing its return value. * That is only correct if the result is always freshly allocated. It used not * to be: empty input returned a string constant, and an input that already * looked like a complete literal was returned as-is, so those callers freed * the very chunk they were about to return. * * The two inputs below took those two paths. Both lexical forms being empty * builds "" and used to reach the string-constant return, which pfree() then * read as a chunk header; '"a""@en' has no closing quote, but used to be taken * for a complete literal all the same, since a quote comes right before its * first '@', and was returned aliasing the buffer freed underneath it. Now that * the closing quote is looked for, it is read as content, quotes and all. */ SELECT sparql.concat(''::rdfnode, ''::rdfnode, 'x'::rdfnode) AS constant_freed; constant_freed ---------------- "x" (1 row) SELECT sparql.concat(''::rdfnode, ''::rdfnode, 'x'::rdfnode, 'y'::rdfnode) AS constant_freed_twice; constant_freed_twice ---------------------- "xy" (1 row) SELECT sparql.lcase('"a""@en'::rdfnode) AS aliased_lcase; aliased_lcase --------------- "\"a\"\"@en" (1 row) SELECT sparql.ucase('"a""@en'::rdfnode) AS aliased_ucase; aliased_ucase --------------- "\"A\"\"@EN" (1 row) SELECT sparql.strafter('"a""@en'::rdfnode, ''::rdfnode) AS aliased_strafter; aliased_strafter ------------------ "\"a\"\"@en" (1 row) SELECT sparql.concat('"a""@en'::rdfnode, ''::rdfnode) AS aliased_concat; aliased_concat ---------------- "\"a\"\"@en" (1 row) SELECT sparql.substr('"a""@en'::rdfnode, 1) AS aliased_substr; aliased_substr ---------------- "\"a\"\"@en" (1 row) /* rdf_fdw_concat() keeps the returned pointer across loop iterations and * pfree()s it again while processing the next element, so an aliased result * was pushed onto the freelist twice */ SELECT sparql.concat('"a""@en'::rdfnode, ''::rdfnode, 'x'::rdfnode) AS freed_twice; freed_twice --------------- "\"a\"\"@enx" (1 row) /* ABS */ SELECT sparql.abs('"-1"^^xsd:int'); abs --------------------------------------------- "1"^^ (1 row) SELECT sparql.abs('"-1.42"^^xsd:double'); abs --------------------------------------------------- "1.42"^^ (1 row) SELECT sparql.abs(sparql.strdt('-1.42','xsd:double')); abs --------------------------------------------------- "1.42"^^ (1 row) SELECT sparql.abs(sparql.strdt('-1.42238','xsd:double')); abs ------------------------------------------------------ "1.42238"^^ (1 row) SELECT sparql.abs(''); ERROR: invalid value for ABS(): "" SELECT sparql.abs(' '); ERROR: invalid value for ABS(): " " SELECT sparql.abs(NULL); abs ------ NULL (1 row) SELECT sparql.abs(CAST(-1.42 AS numeric)); abs ---------------------------------------------------- "1.42"^^ (1 row) SELECT sparql.abs(CAST(-1.42 AS double precision)); abs --------------------------------------------------- "1.42"^^ (1 row) --SELECT sparql.abs(CAST(-1.42 AS real)); SELECT sparql.abs(CAST(-1 AS bigint)); abs ---------------------------------------------- "1"^^ (1 row) SELECT sparql.abs(CAST(-1 AS smallint)); abs ----------------------------------------------- "1"^^ (1 row) SELECT sparql.abs(CAST(-1 AS int)); abs --------------------------------------------- "1"^^ (1 row) /* The exact numeric datatypes must stay exact. Computing them in floating * point rounds large integers and small decimals, and prints the result in an * exponent form that the lexical space of xsd:integer and xsd:decimal does not * admit, so the value comes back both wrong and ill-typed. */ SELECT sparql.abs('"-9007199254740993"^^xsd:integer'); abs ---------------------------------------------------------------- "9007199254740993"^^ (1 row) SELECT sparql.abs('"-0.000000000000001"^^xsd:decimal'); abs ----------------------------------------------------------------- "0.000000000000001"^^ (1 row) SELECT sparql.abs('"-1.50"^^xsd:decimal'); -- trailing zero is part of the value abs ---------------------------------------------------- "1.50"^^ (1 row) SELECT sparql.abs('"-42"^^xsd:int'); -- integer subtypes keep their datatype abs ---------------------------------------------- "42"^^ (1 row) /* the floating datatypes are still computed in floating arithmetic */ SELECT sparql.abs('"-1.1234567"^^xsd:float'), sparql.abs('"-1.5"^^xsd:double'); abs | abs -------------------------------------------------------+-------------------------------------------------- "1.1234567"^^ | "1.5"^^ (1 row) SELECT sparql.abs('"NaN"^^xsd:double'), sparql.abs('"-INF"^^xsd:double'); abs | abs --------------------------------------------------+-------------------------------------------------- "NaN"^^ | "INF"^^ (1 row) /* ROUND */ SELECT sparql.round('"2.4999"^^xsd:double'); round ------------------------------------------------ "2"^^ (1 row) SELECT sparql.round('"2.5"^^xsd:double'); round ------------------------------------------------ "3"^^ (1 row) SELECT sparql.round('"-2.5"^^xsd:decimal'); round -------------------------------------------------- "-2"^^ (1 row) SELECT sparql.round(''); ERROR: invalid value for ROUND(): "" SELECT sparql.round('""'); ERROR: invalid value for ROUND(): "" SELECT sparql.round(' '); ERROR: invalid value for ROUND(): " " SELECT sparql.round('" "'); ERROR: invalid value for ROUND(): " " SELECT sparql.round(NULL); round ------- NULL (1 row) SELECT sparql.round(CAST(2.49999 AS numeric)); round ------------------------------------------------- "2"^^ (1 row) SELECT sparql.round(CAST(2.5 AS double precision)); round ------------------------------------------------ "3"^^ (1 row) --SELECT sparql.round(CAST(-2.5 AS real)); SELECT sparql.round(CAST(42 AS bigint)); round ------------------------------------------------- "42"^^ (1 row) SELECT sparql.round(CAST(42 AS smallint)); round ------------------------------------------------- "42"^^ (1 row) SELECT sparql.round(CAST(42 AS int)); round ------------------------------------------------- "42"^^ (1 row) /* SPARQL ROUND breaks ties towards positive infinity, so the result of a * negative argument is not the mirror image of the positive one: ROUND(-2.5) * is -2 while ROUND(2.5) is 3. Zero and the negative fractions above -1 are * the cases a sign test gets wrong. */ SELECT v AS input, sparql.lex(sparql.round(('"'||v||'"^^xsd:decimal')::rdfnode)) AS as_decimal, sparql.lex(sparql.round(('"'||v||'"^^xsd:double')::rdfnode)) AS as_double FROM (VALUES ('-2.5'),('-1.5'),('-1.2'),('-0.6'),('-0.5'), ('0'),('0.5'),('1.2'),('1.5'),('2.5')) t(v); input | as_decimal | as_double -------+------------+----------- -2.5 | -2 | -2 -1.5 | -1 | -1 -1.2 | -1 | -1 -0.6 | -1 | -1 -0.5 | 0 | -0 0 | 0 | 0 0.5 | 1 | 1 1.2 | 1 | 1 1.5 | 2 | 2 2.5 | 3 | 3 (10 rows) /* the half is compared against the fractional part rather than added first: * in binary floating point 0.49999999999999994 + 0.5 is exactly 1 */ SELECT sparql.lex(sparql.round('"0.49999999999999994"^^xsd:double'::rdfnode)); lex ----- 0 (1 row) /* the datatype of the argument survives, and the special values pass through */ SELECT sparql.round('"1.5"^^xsd:float'), sparql.round('"1.5"^^xsd:double'), sparql.round('"1.5"^^xsd:decimal'), sparql.round('"3"^^xsd:integer'); round | round | round | round -----------------------------------------------+------------------------------------------------+-------------------------------------------------+------------------------------------------------- "2"^^ | "2"^^ | "2"^^ | "3"^^ (1 row) SELECT sparql.round('"NaN"^^xsd:double'), sparql.round('"INF"^^xsd:double'), sparql.round('"-INF"^^xsd:double'); round | round | round --------------------------------------------------+--------------------------------------------------+--------------------------------------------------- "NaN"^^ | "INF"^^ | "-INF"^^ (1 row) /* CEIL */ SELECT sparql.ceil('"10.5"^^xsd:double'); ceil ------------------------------------------------- "11"^^ (1 row) SELECT sparql.ceil('"-10.5"^^xsd:decimal'); ceil --------------------------------------------------- "-10"^^ (1 row) SELECT sparql.ceil(NULL); ceil ------ NULL (1 row) SELECT sparql.ceil(CAST(10.5 AS numeric)); ceil -------------------------------------------------- "11"^^ (1 row) SELECT sparql.ceil(CAST(-10.5 AS double precision)); ceil -------------------------------------------------- "-10"^^ (1 row) SELECT sparql.ceil(CAST(10.5 AS real)); ceil ------------------------------------------------- "11"^^ (1 row) SELECT sparql.ceil(CAST(-42 AS bigint)); ceil -------------------------------------------------- "-42"^^ (1 row) SELECT sparql.ceil(CAST(42 AS smallint)); ceil ------------------------------------------------- "42"^^ (1 row) SELECT sparql.ceil(CAST(-42 AS int)); ceil -------------------------------------------------- "-42"^^ (1 row) /* FLOOR */ SELECT sparql.floor('"10.5"^^xsd:double'); floor ------------------------------------------------- "10"^^ (1 row) SELECT sparql.floor('"-10.5"^^xsd:decimal'); floor --------------------------------------------------- "-11"^^ (1 row) SELECT sparql.floor(CAST(10.5 AS numeric)); floor -------------------------------------------------- "10"^^ (1 row) SELECT sparql.floor(CAST(-10.5 AS double precision)); floor -------------------------------------------------- "-11"^^ (1 row) SELECT sparql.floor(CAST(10.5 AS real)); floor ------------------------------------------------- "10"^^ (1 row) SELECT sparql.floor(CAST(-42 AS bigint)); floor -------------------------------------------------- "-42"^^ (1 row) SELECT sparql.floor(CAST(42 AS smallint)); floor ------------------------------------------------- "42"^^ (1 row) SELECT sparql.floor(CAST(-42 AS int)); floor -------------------------------------------------- "-42"^^ (1 row) /* YEAR */ SELECT sparql.year('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); year ------ 2011 (1 row) SELECT sparql.year('"2011-01-10T14:45:13.815-05:00"'); year ------ 2011 (1 row) SELECT sparql.year('2011-01-10T14:45:13.815-05:00'); year ------ 2011 (1 row) SELECT sparql.year('2011-01-10T14:45:13.815-05:00'::date); year ------ 2011 (1 row) SELECT sparql.year('2011-01-10T14:45:13.815-05:00'::timestamp); year ------ 2011 (1 row) SELECT sparql.year(NULL); year ------ NULL (1 row) /* MONTH */ SELECT sparql.month('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); month ------- 1 (1 row) SELECT sparql.month('"2011-01-10T14:45:13.815-05:00"'); month ------- 1 (1 row) SELECT sparql.month('2011-01-10T14:45:13.815-05:00'); month ------- 1 (1 row) SELECT sparql.month('2011-01-10T14:45:13.815-05:00'::date); month ------- 1 (1 row) SELECT sparql.month('2011-01-10T14:45:13.815-05:00'::timestamp); month ------- 1 (1 row) SELECT sparql.month(NULL); month ------- NULL (1 row) /* DAYS */ SELECT sparql.day('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); day ----- 10 (1 row) SELECT sparql.day('"2011-01-10T14:45:13.815-05:00"'); day ----- 10 (1 row) SELECT sparql.day('2011-01-10T14:45:13.815-05:00'); day ----- 10 (1 row) SELECT sparql.day('2011-01-10T14:45:13.815-05:00'::date); day ----- 10 (1 row) SELECT sparql.day('2011-01-10T14:45:13.815-05:00'::timestamp); day ----- 10 (1 row) SELECT sparql.day(NULL); day ------ NULL (1 row) /* HOURS */ SELECT sparql.hours('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); hours ------- 14 (1 row) SELECT sparql.hours('"2011-01-10T14:45:13.815-05:00"'); hours ------- 14 (1 row) SELECT sparql.hours('2011-01-10T14:45:13.815-05:00'); hours ------- 14 (1 row) SELECT sparql.hours('2011-01-10T14:45:13.815-05:00'::date); hours ------- 0 (1 row) SELECT sparql.hours('2011-01-10T14:45:13.815-05:00'::timestamp); hours ------- 14 (1 row) SELECT sparql.hours('14:45:13'::time); hours ------- 14 (1 row) SELECT sparql.hours(NULL); hours ------- NULL (1 row) /* MINUTES */ SELECT sparql.minutes('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); minutes --------- 45 (1 row) SELECT sparql.minutes('"2011-01-10T14:45:13.815-05:00"'); minutes --------- 45 (1 row) SELECT sparql.minutes('2011-01-10T14:45:13.815-05:00'); minutes --------- 45 (1 row) SELECT sparql.minutes('2011-01-10T14:45:13.815-05:00'::date); minutes --------- 0 (1 row) SELECT sparql.minutes('2011-01-10T14:45:13.815-05:00'::timestamp); minutes --------- 45 (1 row) SELECT sparql.minutes('14:45:13'::time); minutes --------- 45 (1 row) SELECT sparql.minutes(NULL); minutes --------- NULL (1 row) /* SECONDS */ SELECT pg_catalog.round(sparql.seconds('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'),3); round -------- 13.815 (1 row) SELECT pg_catalog.round(sparql.seconds('"2011-01-10T14:45:13.815-05:00"'),3); round -------- 13.815 (1 row) SELECT pg_catalog.round(sparql.seconds('2011-01-10T14:45:13.815-05:00'),3); round -------- 13.815 (1 row) SELECT pg_catalog.round(sparql.seconds('2011-01-10T14:45:13.815-05:00'::date),3); round ------- 0.000 (1 row) SELECT pg_catalog.round(sparql.seconds('2011-01-10T14:45:13.815-05:00'::timestamp),3); round -------- 13.815 (1 row) SELECT pg_catalog.round(sparql.seconds('14:45:13.815'::time),3); round -------- 13.815 (1 row) SELECT sparql.seconds(NULL); seconds --------- NULL (1 row) /* TIMEZONE */ SELECT sparql.timezone('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); timezone ------------------------------------------------------------- "-PT5H"^^ (1 row) SELECT sparql.timezone('"2011-01-10T14:45:13.815Z"^^xsd:dateTime'); timezone ------------------------------------------------------------ "PT0S"^^ (1 row) SELECT sparql.timezone('"2011-01-10T14:45:13.815"^^xsd:dateTime'); ERROR: TIMEZONE(): datetime has no time zone: 2011-01-10T14:45:13.815 SELECT sparql.timezone('"2020-12-01T08:00:00-05:00"^^xsd:dateTime'); timezone ------------------------------------------------------------- "-PT5H"^^ (1 row) SELECT sparql.timezone('"2020-12-01T08:00:00+02:30"^^xsd:dateTime'); timezone --------------------------------------------------------------- "PT2H30M"^^ (1 row) SELECT sparql.timezone('"2020-12-01T08:00:00Z"^^xsd:dateTime'); timezone ------------------------------------------------------------ "PT0S"^^ (1 row) SELECT sparql.timezone('"2020-12-01T08:00:00.123+00:00"^^xsd:dateTime'); timezone ------------------------------------------------------------ "PT0S"^^ (1 row) SELECT sparql.timezone('"2020-12-01T08:00:00.123456-04:45"^^xsd:dateTime'); timezone ---------------------------------------------------------------- "-PT4H45M"^^ (1 row) SELECT sparql.timezone('"2020-12-01T08:00:00"^^xsd:dateTime'); ERROR: TIMEZONE(): datetime has no time zone: 2020-12-01T08:00:00 SELECT sparql.timezone('"2020-12-01T08:00:00+25:00"^^xsd:dateTime'); ERROR: TIMEZONE(): invalid timezone offset: +25:00 SELECT sparql.timezone('"2020-12-01T08:00:00-99:99"^^xsd:dateTime'); ERROR: TIMEZONE(): invalid timezone offset: -99:99 SELECT sparql.timezone('"invalid-date"^^xsd:dateTime'); ERROR: TIMEZONE(): invalid xsd:dateTime format: invalid-date SELECT sparql.timezone('""^^xsd:dateTime'); ERROR: TIMEZONE(): invalid xsd:dateTime literal SELECT sparql.timezone(NULL); timezone ---------- NULL (1 row) SELECT sparql.timezone('"not a date"^^xsd:string'); ERROR: TIMEZONE(): argument must be xsd:dateTime, got /* TZ */ SELECT sparql.tz('"2011-01-10T14:45:13.815-05:00"^^xsd:dateTime'); tz ---------- "-05:00" (1 row) SELECT sparql.tz('"2011-01-10T14:45:13.815Z"^^xsd:dateTime'); tz ----- "Z" (1 row) /* SPARQL 1.1 17.4.5.8 gives this one the empty string, not an error: TZ is the total function of the pair, and TIMEZONE is the one that raises. The second form shows the result is a term a caller can go on using. */ SELECT sparql.tz('"2011-01-10T14:45:13.815"^^xsd:dateTime'); tz ---- "" (1 row) SELECT sparql.concat('"["', sparql.tz('"2011-01-10T14:45:13.815"^^xsd:dateTime'), '"]"'); concat -------- "[]" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00-05:00"^^xsd:dateTime'); tz ---------- "-05:00" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00+02:30"^^xsd:dateTime'); tz ---------- "+02:30" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00Z"^^xsd:dateTime'); tz ----- "Z" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00.123+00:00"^^xsd:dateTime'); tz ---------- "+00:00" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00.123456-04:45"^^xsd:dateTime'); tz ---------- "-04:45" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00"^^xsd:dateTime'); tz ---- "" (1 row) SELECT sparql.tz('"2020-12-01T08:00:00+25:00"^^xsd:dateTime'); ERROR: TZ(): invalid timezone offset: +25:00 SELECT sparql.tz('"2020-12-01T08:00:00-99:99"^^xsd:dateTime'); ERROR: TZ(): invalid timezone offset: -99:99 SELECT sparql.tz('"invalid-date"^^xsd:dateTime'); ERROR: TZ(): invalid xsd:dateTime format: invalid-date SELECT sparql.tz('""^^xsd:dateTime'); ERROR: TZ(): invalid xsd:dateTime format: SELECT sparql.tz(NULL); tz ------ NULL (1 row) SELECT sparql.tz('"not a date"^^xsd:string'); ERROR: TZ(): argument must be xsd:dateTime, got /*BOUND */ SELECT sparql.bound(NULL); bound ------- f (1 row) SELECT sparql.bound('abc'); bound ------- t (1 row) /* SAMETERM */ SELECT sparql.sameterm('"abc"', '"abc"'); sameterm ---------- t (1 row) SELECT sparql.sameterm('"abc"@en', '"abc"@en'); sameterm ---------- t (1 row) SELECT sparql.sameterm('"abc"@en', '"abc"'); sameterm ---------- f (1 row) SELECT sparql.sameterm('"abc"^^xsd:string', '"abc"'); sameterm ---------- f (1 row) SELECT sparql.sameterm('', ''); -- t (IRIs) sameterm ---------- t (1 row) SELECT sparql.sameterm('_:b1', '_:b1'); -- t (same blank node label) sameterm ---------- t (1 row) SELECT sparql.sameterm('_:b1', '_:b2'); -- f (different labels) sameterm ---------- f (1 row) SELECT sparql.sameterm('"1"^^xsd:integer', '"01"^^xsd:integer'); -- f (different lexical form) sameterm ---------- f (1 row) SELECT sparql.sameterm(NULL, '"abc"'); sameterm ---------- NULL (1 row) SELECT sparql.sameterm(NULL, NULL); sameterm ---------- NULL (1 row) /* COALESCE */ SELECT sparql.coalesce(NULL, NULL, 'foo'); coalesce ---------- "foo" (1 row) SELECT sparql.coalesce(NULL, NULL, '"foo"'); coalesce ---------- "foo" (1 row) SELECT sparql.coalesce(NULL, NULL, '"foo"^^xsd:string'); coalesce -------------------------------------------------- "foo"^^ (1 row) SELECT sparql.coalesce(NULL, NULL, '"foo"@fr'); coalesce ---------- "foo"@fr (1 row) SELECT sparql.coalesce(NULL, NULL, ''); coalesce ------------------- (1 row) SELECT sparql.coalesce(NULL, NULL, sparql.iri('"http://example/"')); coalesce ------------------- (1 row) SELECT sparql.coalesce(NULL, NULL, sparql.bnode('foo')); coalesce ---------- _:foo (1 row) /* no argument at all: an empty array, and no array */ SELECT sparql.coalesce(VARIADIC '{}'::rdfnode[]) IS NULL; ?column? ---------- t (1 row) SELECT sparql.coalesce(VARIADIC NULL::rdfnode[]) IS NULL; ?column? ---------- t (1 row) /* MD5 */ SELECT sparql.md5('abc'); md5 ------------------------------------ "900150983cd24fb0d6963f7d28e17f72" (1 row) SELECT sparql.md5('"abc"'); md5 ------------------------------------ "900150983cd24fb0d6963f7d28e17f72" (1 row) SELECT sparql.md5('"abc"^^xsd:string'); md5 ------------------------------------ "900150983cd24fb0d6963f7d28e17f72" (1 row) SELECT sparql.md5('"abc"^^xsd:string') = sparql.md5('abc'); ?column? ---------- t (1 row) SELECT sparql.md5('"abc"@en') = sparql.md5('"abc"'); ?column? ---------- t (1 row) SELECT sparql.md5('"abc"^^xsd:normalizedString'); md5 ------------------------------------ "900150983cd24fb0d6963f7d28e17f72" (1 row) SELECT sparql.md5('"abc"^^xsd:anyURI'); md5 ------------------------------------ "900150983cd24fb0d6963f7d28e17f72" (1 row) SELECT sparql.md5('123'); -- xsd:integer md5 ------------------------------------ "202cb962ac59075b964b07152d234b70" (1 row) SELECT sparql.md5('"2020-01-01T00:00:00Z"^^xsd:dateTime'); md5 ------------------------------------ "cbedd1c35c83b21c73dd2416abbe8f0b" (1 row) SELECT sparql.md5('"not_a_uri"^^xsd:anyURI'); md5 ------------------------------------ "e45ec53c1ba6595b0685f760b2cf4fc5" (1 row) SELECT sparql.md5('""'); md5 ------------------------------------ "d41d8cd98f00b204e9800998ecf8427e" (1 row) SELECT sparql.md5(NULL); md5 ------ NULL (1 row) SELECT sparql.md5('"Münster"'); md5 ------------------------------------ "9bb43a4ce6b060e4766588c1d88fcb1b" (1 row) SELECT sparql.md5(repeat('a', 10000)); md5 ------------------------------------ "0d0c9c4db6953fee9e03f528cafd7d3e" (1 row) /* RAND */ SELECT sparql.rand() >= 0 AND sparql.rand() < 1; -- should be t ?column? ---------- t (1 row) SELECT sparql.rand() != sparql.rand(); -- should be t (very low probability of being f) ?column? ---------- t (1 row) /* BNODE, UUID and STRUUID generate a new value per call, so they must not be * folded to a single constant for the whole query, and each must keep its own * result shape across rows: UUID yields an IRI, STRUUID a string literal. */ SELECT count(DISTINCT u) FROM (SELECT sparql.uuid() AS u FROM generate_series(1,100)) t; count ------- 100 (1 row) SELECT count(DISTINCT u) FROM (SELECT sparql.struuid() AS u FROM generate_series(1,100)) t; count ------- 100 (1 row) SELECT count(DISTINCT b) FROM (SELECT sparql.bnode() AS b FROM generate_series(1,100)) t; count ------- 100 (1 row) SELECT bool_and(sparql.isiri(sparql.uuid())) FROM generate_series(1,100); bool_and ---------- t (1 row) SELECT bool_and(sparql.isliteral(sparql.struuid())) FROM generate_series(1,100); bool_and ---------- t (1 row) SELECT bool_and(sparql.isblank(sparql.bnode())) FROM generate_series(1,100); bool_and ---------- t (1 row) /* The function bodies name the schema the extension was installed into when * they refer to the rdfnode type, so they do not depend on the caller having * that schema on its search_path. */ SET search_path = pg_catalog; SELECT sparql.round('"1.5"^^xsd:decimal'), sparql.abs('"-3"^^xsd:integer'), sparql.ceil('"1.2"^^xsd:decimal'), sparql.floor('"1.8"^^xsd:decimal'); round | abs | ceil | floor -------------------------------------------------+-------------------------------------------------+-------------------------------------------------+------------------------------------------------- "2"^^ | "3"^^ | "2"^^ | "1"^^ (1 row) SELECT sparql.year('"2025-04-16"^^xsd:date'), sparql.md5('"x"'); year | md5 ------+------------------------------------ 2025 | "9dd4e461268c8034f5c8564e155c67a6" (1 row) RESET search_path; /* IRI() builds a term that is asked for as an IRI, so a body that the SPARQL * grammar (rule [139]) forbids is a type error rather than a malformed term. * STRDT() routes a non-IRI datatype through the same constructor. */ SELECT sparql.iri('"http://e.org/a>.. SELECT sparql.uri('"http://e.org/a>.. SELECT sparql.iri('"http://e.org/{x}"'::rdfnode); ERROR: invalid IRI: SELECT sparql.strdt('"v"'::rdfnode, '"http://e.org/t>x"'::rdfnode); ERROR: invalid IRI: x> /* A lexical form keeps its escapes as written, while a term read from an * endpoint carries the characters themselves. Both spell one value, and the * functions defined over the value -- length, case, position, hash -- read the * characters, as the endpoint does: "a\"b" has three of them. */ SELECT sparql.strlen('"a\"b"'::rdfnode) AS quote, sparql.strlen('"a\\b"'::rdfnode) AS backslash, sparql.strlen('"a\nb"'::rdfnode) AS escaped_newline, sparql.strlen(E'"a\nb"'::rdfnode) AS raw_newline, sparql.strlen('"x\"\"y"@en'::rdfnode) AS two_quotes; quote | backslash | escaped_newline | raw_newline | two_quotes -------+-----------+-----------------+-------------+------------ 3 | 3 | 3 | 3 | 4 (1 row) SELECT sparql.ucase('"a\tb"'::rdfnode) = sparql.ucase(E'"a\tb"'::rdfnode) AS ucase_keeps_the_tab, sparql.substr('"a\"bc"'::rdfnode, 2, 2) AS substr_from_the_quote, sparql.md5('"a\"b"'::rdfnode) = sparql.md5(E'"a\\"b"'::rdfnode) AS md5_of_the_value, sparql.encode_for_uri('"a\"b"'::rdfnode) AS encoded, sparql.strstarts('"\"quoted\""'::rdfnode, '"\""'::rdfnode) AS starts_with_quote, sparql.strbefore('"a\\b"'::rdfnode, '"b"'::rdfnode) AS before_b, sparql.strafter('"a\nb"'::rdfnode, E'"\n"'::rdfnode) AS after_newline; ucase_keeps_the_tab | substr_from_the_quote | md5_of_the_value | encoded | starts_with_quote | before_b | after_newline ---------------------+-----------------------+------------------+---------+-------------------+----------+--------------- t | "\"b" | t | "a%22b" | t | "a\\" | "b" (1 row) /* well-formed constructions are unaffected */ SELECT sparql.iri('"http://e.org/ok"'::rdfnode); iri ------------------- (1 row) SELECT sparql.iri(''::rdfnode); iri ------------------------ (1 row) SELECT sparql.strdt('"v"'::rdfnode, '"xsd:integer"'::rdfnode); strdt ------------------------------------------------- "v"^^ (1 row) SELECT sparql.strdt('"v"'::rdfnode, ''::rdfnode); strdt ----------------------- "v"^^ (1 row)