CREATE SERVER testserver FOREIGN DATA WRAPPER rdf_fdw OPTIONS ( endpoint 'https://dbpedia.org/sparql', connect_timeout '42' ); /* invalid column option - OPTION 'foo' does not exist */ CREATE FOREIGN TABLE table_error1 ( name text OPTIONS (foo '?s') ) SERVER testserver OPTIONS (sparql 'SELECT * WHERE {?s ?p ?o}'); ERROR: invalid rdf_fdw option 'foo' /* invalid column option - the column OPTION 'variable' cannot be empty. */ CREATE FOREIGN TABLE table_error2 ( name text OPTIONS (variable '') ) SERVER testserver OPTIONS (sparql 'SELECT * WHERE {?s ?p ?o}'); ERROR: empty value in option 'variable' /* invalid foreign table option - SERVER option 'foo' does not exist. */ CREATE FOREIGN TABLE table_error3 ( name text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (foo 'SELECT * WHERE {?s ?p ?o}'); ERROR: invalid rdf_fdw option 'foo' /* invalid foreign table option - log_sparql must be boolean */ CREATE FOREIGN TABLE table_error5 ( name text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT * WHERE {?s ?p ?o}', log_sparql 'foo'); ERROR: invalid log_sparql: 'foo' HINT: This parameter expects boolean values ('true' or 'false'). CREATE FOREIGN TABLE t1 ( name text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s WHERE {?s ?p ?o} LIMIT 1', log_sparql 'true'); /* invalid SPARQL - missing closing curly braces (\n)*/ CREATE FOREIGN TABLE t2 (s text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql ' SELECT ?s {?s ?p ?o '); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s {?s ?p ?o HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. /* invalid SPARQL - missing closing curly braces */ CREATE FOREIGN TABLE t3 (s text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o'); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s {?s ?p ?o HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. /* invalid SPARQL - missing closing curly braces (\t) */ CREATE FOREIGN TABLE t4 (s text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql ' SELECT ?s {?s ?p ?o'); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s {?s ?p ?o HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. /* invalid SPARQL - missing opening curly braces (\n)*/ CREATE FOREIGN TABLE t5 (s text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql ' SELECT ?s ?s ?p ?o}'); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s ?s ?p ?o} HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. /* invalid SPARQL - both curly braces, but the closing one first. The graph * pattern lies between the first '{' and the last '}', and with the two the * other way round it was copied with a length running past the end of the * option, which pnstrdup() on PostgreSQL 10 and older copies in full */ CREATE FOREIGN TABLE t6 (s rdfnode OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s WHERE } {'); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s WHERE } { HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. CREATE FOREIGN TABLE t6 (s rdfnode OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s WHERE }{?s ?p ?o'); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s WHERE }{?s ?p ?o HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. /* the option is validated the same way when it is changed */ CREATE FOREIGN TABLE t6 (s rdfnode OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s WHERE {?s ?p ?o}'); ALTER FOREIGN TABLE t6 OPTIONS (SET sparql 'SELECT ?s WHERE } {'); ERROR: unable to parse SPARQL WHERE clause: SELECT ?s WHERE } { HINT: The WHERE clause expects at least one triple pattern wrapped by curly braces, e.g. '{?s ?p ?o}'. DROP FOREIGN TABLE t6; /* missing SELECT */ CREATE FOREIGN TABLE t7 (s text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql '?s {?s ?p ?o}'); ERROR: unable to parse SPARQL SELECT clause: ?s {?s ?p ?o}. /* empty nodetype */ CREATE FOREIGN TABLE t7 (s text OPTIONS (variable '?s', nodetype '') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: empty value in option 'nodetype' /* invalid nodetype */ CREATE FOREIGN TABLE t7 (s text OPTIONS (variable '?s', nodetype 'foo') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid nodetype: 'foo' HINT: This parameter expects node types ('iri' or 'literal'). /* invalid literaltype - contains whitespaces */ CREATE FOREIGN TABLE t7 (s text OPTIONS (variable '?s', literaltype ' xsd:string') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid literaltype: ' xsd:string' HINT: Whitespaces are not allowed in 'literal_type' option. /* invalid language - contains whitespaces */ CREATE FOREIGN TABLE t7 (s text OPTIONS (variable '?s', language 'de ') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid language: 'de ' HINT: Whitespaces are not allowed in 'language' option. /* invalid combination of 'literaltype' and 'language' */ CREATE FOREIGN TABLE t8 (s text OPTIONS (variable '?s', literaltype 'iri', language 'es') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid language: 'es' HINT: The parameters 'literal_type' and 'language' cannot be combined. /* invalid 'variable' */ CREATE FOREIGN TABLE t9 (s text OPTIONS (variable 's', expression 'now()') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid variable: 's' HINT: A query variable must start with either "?" or "$"; the "?" or "$" is not part of the variable name. Allowable characters for the name are [a-z], [A-Z], [0-9], _ and diacrictics. /* invalid 'variable' */ CREATE FOREIGN TABLE t10 (s text OPTIONS (variable '?a-z', expression 'now()') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid variable: '?a-z' HINT: A query variable must start with either "?" or "$"; the "?" or "$" is not part of the variable name. Allowable characters for the name are [a-z], [A-Z], [0-9], _ and diacrictics. /* invalid 'variable' */ CREATE FOREIGN TABLE t11 (s text OPTIONS (variable '?a$z', expression 'now()') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid variable: '?a$z' HINT: A query variable must start with either "?" or "$"; the "?" or "$" is not part of the variable name. Allowable characters for the name are [a-z], [A-Z], [0-9], _ and diacrictics. /* invalid 'variable' */ CREATE FOREIGN TABLE t12 (s text OPTIONS (variable '?a?z', expression 'now()') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid variable: '?a?z' HINT: A query variable must start with either "?" or "$"; the "?" or "$" is not part of the variable name. Allowable characters for the name are [a-z], [A-Z], [0-9], _ and diacrictics. /* invalid 'variable' */ CREATE FOREIGN TABLE t13 (s text OPTIONS (variable ' ?a', expression 'now()') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ERROR: invalid variable: ' ?a' HINT: A query variable must start with either "?" or "$"; the "?" or "$" is not part of the variable name. Allowable characters for the name are [a-z], [A-Z], [0-9], _ and diacrictics. /* invalid foreign table option - fetch_size empty */ CREATE FOREIGN TABLE t14 ( name text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}', fetch_size ''); ERROR: empty value in option 'fetch_size' /* invalid foreign table option - fetch_size negative */ CREATE FOREIGN TABLE t15 ( name text OPTIONS (variable '?s') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}', fetch_size '-1'); ERROR: invalid fetch_size: '-1' HINT: Expected an integer between 0 and 2147483647. /* invalid option for rdfnode column*/ CREATE FOREIGN TABLE t16 ( name rdfnode OPTIONS (variable '?s', expression 'STR(?s)') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); SELECT * FROM t16; ERROR: invalid option "expression" for column "name" HINT: rdfnode columns accept only the "variable" option. /* The 'variable' option is required, but PostgreSQL only runs an FDW * validator for columns that actually carry an OPTIONS clause, so a column * declared with no options at all never reached it and left the SPARQL * variable unset. Every consumer dereferences it unconditionally, starting * with the pstrdup() that builds the SPARQL SELECT clause, so planning a * query against such a table used to crash the backend. It is rejected at * plan time now; EXPLAIN is enough to reach the check, and no request is * made to the endpoint. */ CREATE FOREIGN TABLE t17 ( name rdfnode ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); EXPLAIN (COSTS OFF) SELECT name FROM t17; ERROR: column "name" of foreign table "t17" has no "variable" option HINT: Every column of an rdf_fdw foreign table must be mapped to a SPARQL variable, e.g. OPTIONS (variable '?name'). /* the same applies to a column that the query never selects: the option is * required of every column, not only of the ones a given query happens to * touch */ CREATE FOREIGN TABLE t18 ( name rdfnode OPTIONS (variable '?s'), untouched rdfnode ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); EXPLAIN (COSTS OFF) SELECT name FROM t18; ERROR: column "untouched" of foreign table "t18" has no "variable" option HINT: Every column of an rdf_fdw foreign table must be mapped to a SPARQL variable, e.g. OPTIONS (variable '?untouched'). /* A dropped column is not mapped to anything, whatever the catalogue still * says. ALTER FOREIGN TABLE ... DROP COLUMN clears the column's options from * PostgreSQL 18 on and leaves them in place before that, so a table that had a * mapped column removed must plan the same way on either. */ CREATE FOREIGN TABLE t19 ( name rdfnode OPTIONS (variable '?s'), gone rdfnode OPTIONS (variable '?g') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ALTER FOREIGN TABLE t19 DROP COLUMN gone; EXPLAIN (COSTS OFF) SELECT name FROM t19; QUERY PLAN ------------------------------ Foreign Scan on t19 Foreign Server: testserver Pushdown: enabled Remote Select: ?s (4 rows) /* a whole-row reference asks for every column of the row, and the dropped one * still has its place in it; there is no variable to ask the endpoint for, and * handing its absent one to pstrdup() terminated the backend while planning */ EXPLAIN (VERBOSE, COSTS OFF) SELECT t19 FROM t19; QUERY PLAN ------------------------------ Foreign Scan on public.t19 Output: t19.* Foreign Server: testserver Pushdown: enabled Remote Select: ?s (5 rows) EXPLAIN (VERBOSE, COSTS OFF) SELECT t19, name FROM t19; QUERY PLAN ------------------------------ Foreign Scan on public.t19 Output: t19.*, name Foreign Server: testserver Pushdown: enabled Remote Select: ?s (5 rows) /* nor may a dropped column be named by the deprecated-types warning, which * used to report "........pg.dropped.N........" as a column using a * deprecated native PostgreSQL type */ CREATE FOREIGN TABLE t20 ( name text OPTIONS (variable '?s'), gone rdfnode OPTIONS (variable '?g') ) SERVER testserver OPTIONS (sparql 'SELECT ?s {?s ?p ?o}'); ALTER FOREIGN TABLE t20 DROP COLUMN gone; EXPLAIN (COSTS OFF) SELECT name FROM t20; WARNING: the rdf_fdw FOREIGN TABLE "t20" has columns using native PostgreSQL types which are deprecated: name HINT: Use the "rdfnode" type instead. QUERY PLAN ------------------------------ Foreign Scan on t20 Foreign Server: testserver Pushdown: enabled Remote Select: ?s (4 rows) /* SPARQL names a variable with either sigil, and "?x" and "$x" are the same * variable. A mapping declared with "$" must therefore behave exactly like the * same mapping declared with "?", including in the SELECT clause rdf_fdw * generates - the result bindings an endpoint sends back carry no sigil, and * are matched against the mapped variable as "?name". */ CREATE FOREIGN TABLE t21 ( s rdfnode OPTIONS (variable '$s'), o rdfnode OPTIONS (variable '$o') ) SERVER testserver OPTIONS (sparql 'SELECT * {$s ?p $o}'); EXPLAIN (COSTS OFF) SELECT s, o FROM t21 WHERE o = 1::rdfnode; QUERY PLAN ----------------------------------------------------------------------- Foreign Scan on t21 Foreign Server: testserver Pushdown: enabled Remote Select: ?s ?o Remote Filter: ((?o = "1"^^)) (5 rows) /* clean up */ DROP SERVER testserver CASCADE; NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to foreign table t1 drop cascades to foreign table t16 drop cascades to foreign table t17 drop cascades to foreign table t18 drop cascades to foreign table t19 drop cascades to foreign table t20 drop cascades to foreign table t21