\pset null '(null)' CREATE SERVER fuseki FOREIGN DATA WRAPPER rdf_fdw OPTIONS ( endpoint 'http://fuseki:3030/dt/sparql', update_url 'http://fuseki:3030/dt/update'); CREATE FOREIGN TABLE ft ( subject rdfnode OPTIONS (variable '?s'), predicate rdfnode OPTIONS (variable '?p'), object rdfnode OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o}', sparql_update_pattern '?s ?p ?o .' ); CREATE USER MAPPING FOR postgres SERVER fuseki OPTIONS (user 'admin', password 'secret'); INSERT INTO ft (subject, predicate, object) VALUES ('', '', '"Westfälische Wilhelms-Universität Münster"@de'), ('', '', '"University of Münster"@en'), ('', '', '"Univerrrsity of Münsterrr"@en-US'), ('', '', '"Univêrsity of Münsta"@en-GB'), ('', '', '"18:18:42"^^'), ('', '', '"1780-04-16"^^'), ('', '', '"2025-12-24T18:30:42"^^'), ('', '', '"2025-12-24T13:00:42Z"^^'), ('', '', '"51.9636"^^'), ('', '', '"7.6136"^^'), ('', '', '"Johannes Wessels"'), ('', '', ''), ('', '', ''), ('', '', '"Hello 👋 PostgreSQL 🐘"@en'), ('', '', '"unknown literal type"^^'), ('', '', '"explicit string literal"^^'), ('', '', '""'), ('', '', '". <= pontos => ."@pt'), ('', '', '"\n <= salto de línea => \n"@es'), ('', '', '"\" <= double-quotes => \""@en'), ('', '', '"\t <= Tabulatorzeichen => \t"@de'), ('', '', '"1924"^^'), ('', '', '"49098"^^'), ('', '', '"4956"^^'), ('', '', '"803600000"^^'), ('', '', '"1500.00"^^'), ('', '', '"9999999999999999999"^^'), ('', '', '"0.000000000000001"^^'), ('', '', '"2024-02-29"^^'), ('', '', '"true"^^'); CALL rdf_fdw_clone_table( foreign_table => 'public.ft', target_table => 'public.t1', fetch_size => 4, verbose => true, create_table => true, commit_page => false ); INFO: Target TABLE "public.t1" created based on FOREIGN TABLE "public.ft": CREATE TABLE public.t1 AS SELECT * FROM public.ft WITH NO DATA; INFO: == Parameters == foreign_table: 'ft' target_table: 'public.t1' create_table: 'true' fetch_size: 4 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 4]: 4 records inserted INFO: [4 - 8]: 4 records inserted INFO: [8 - 12]: 4 records inserted INFO: [12 - 16]: 4 records inserted INFO: [16 - 20]: 4 records inserted INFO: [20 - 24]: 4 records inserted INFO: [24 - 28]: 4 records inserted INFO: [28 - 32]: 2 records inserted SELECT * FROM public.t1 ORDER BY object::text COLLATE "C"; subject | predicate | object -------------------------------+---------------------------------------------------+--------------------------------------------------------------------- | | " <= Tabulatorzeichen => "@de | | " + | | <= salto de línea => + | | "@es | | "" | | ". <= pontos => ."@pt | | "0.000000000000001"^^ | | "1500.0e0"^^ | | "1780-04-16"^^ | | "18:18:42"^^ | | "1924"^^ | | "2024-02-29"^^ | | "2025-12-24T13:00:42Z"^^ | | "2025-12-24T18:30:42"^^ | | "49098"^^ | | "4956"^^ | | "51.9636"^^ | | "7.6136"^^ | | "803600000"^^ | | "9999999999999999999"^^ | | "Hello 👋 PostgreSQL 🐘"@en | | "Johannes Wessels" | | "Univerrrsity of Münsterrr"@en-us | | "University of Münster"@en | | "Univêrsity of Münsta"@en-gb | | "Westfälische Wilhelms-Universität Münster"@de | | "\" <= double-quotes => \""@en | | "explicit string literal" | | "true"^^ | | "unknown literal type"^^ | | | | (30 rows) /* text data type */ CREATE FOREIGN TABLE ft2 ( subject text OPTIONS (variable '?s'), predicate text OPTIONS (variable '?p'), object text OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o}' ); CALL rdf_fdw_clone_table( foreign_table => 'public.ft2', target_table => 'public.t2', fetch_size => 4, verbose => true, create_table => true, commit_page => false ); WARNING: the rdf_fdw FOREIGN TABLE "ft2" has columns using native PostgreSQL types which are deprecated: subject, predicate, object HINT: Use the "rdfnode" type instead. INFO: Target TABLE "public.t2" created based on FOREIGN TABLE "public.ft2": CREATE TABLE public.t2 AS SELECT * FROM public.ft2 WITH NO DATA; INFO: == Parameters == foreign_table: 'ft2' target_table: 'public.t2' create_table: 'true' fetch_size: 4 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 4]: 4 records inserted INFO: [4 - 8]: 4 records inserted INFO: [8 - 12]: 4 records inserted INFO: [12 - 16]: 4 records inserted INFO: [16 - 20]: 4 records inserted INFO: [20 - 24]: 4 records inserted INFO: [24 - 28]: 4 records inserted INFO: [28 - 32]: 2 records inserted SELECT * FROM public.t2 ORDER BY object::text COLLATE "C"; subject | predicate | object -----------------------------+-------------------------------------------------+---------------------------------------------------- https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | <= Tabulatorzeichen => https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | + | | <= salto de línea => + | | https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | " <= double-quotes => " https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | . <= pontos => . https://www.uni-muenster.de | http://dbpedia.org/property/verySmallNumber | 0.000000000000001 https://www.uni-muenster.de | http://dbpedia.org/property/tuition | 1500.0e0 https://www.uni-muenster.de | http://dbpedia.org/property/established | 1780-04-16 https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#time | 18:18:42 https://www.uni-muenster.de | http://dbpedia.org/property/administrativeStaff | 1924 https://www.uni-muenster.de | http://dbpedia.org/ontology/restingDate | 2024-02-29 https://www.uni-muenster.de | http://dbpedia.org/ontology/wikiPageExtracted | 2025-12-24T13:00:42Z https://www.uni-muenster.de | http://dbpedia.org/property/modified | 2025-12-24T18:30:42 https://www.uni-muenster.de | http://dbpedia.org/property/students | 49098 https://www.uni-muenster.de | http://dbpedia.org/property/academicStaff | 4956 https://www.uni-muenster.de | http://www.w3.org/2003/01/geo/wgs84_pos#lat | 51.9636 https://www.uni-muenster.de | http://www.w3.org/2003/01/geo/wgs84_pos#long | 7.6136 https://www.uni-muenster.de | http://dbpedia.org/property/budget | 803600000 https://www.uni-muenster.de | http://dbpedia.org/property/veryLargeNumber | 9999999999999999999 https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | Hello 👋 PostgreSQL 🐘 https://www.uni-muenster.de | http://dbpedia.org/property/rector | Johannes Wessels https://www.uni-muenster.de | http://dbpedia.org/property/name | Univerrrsity of Münsterrr https://www.uni-muenster.de | http://dbpedia.org/property/name | University of Münster https://www.uni-muenster.de | http://dbpedia.org/property/name | Univêrsity of Münsta https://www.uni-muenster.de | http://dbpedia.org/property/name | Westfälische Wilhelms-Universität Münster https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | explicit string literal https://www.uni-muenster.de | http://dbpedia.org/property/city | http://dbpedia.org/resource/Münster https://www.uni-muenster.de | http://dbpedia.org/property/state | http://dbpedia.org/resource/North_Rhine-Westphalia https://www.uni-muenster.de | http://dbpedia.org/ontology/internationally | true https://www.uni-muenster.de | http://www.w3.org/2000/01/rdf-schema#comment | unknown literal type (30 rows) /* timestamp data type */ CREATE FOREIGN TABLE ft3 ( subject text OPTIONS (variable '?s'), predicate text OPTIONS (variable '?p'), object timestamp OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o FILTER (?p = || ?p = )}' ); CALL rdf_fdw_clone_table( foreign_table => 'public.ft3', target_table => 'public.t3', fetch_size => 4, verbose => true, create_table => true, commit_page => false ); WARNING: the rdf_fdw FOREIGN TABLE "ft3" has columns using native PostgreSQL types which are deprecated: subject, predicate, object HINT: Use the "rdfnode" type instead. INFO: Target TABLE "public.t3" created based on FOREIGN TABLE "public.ft3": CREATE TABLE public.t3 AS SELECT * FROM public.ft3 WITH NO DATA; INFO: == Parameters == foreign_table: 'ft3' target_table: 'public.t3' create_table: 'true' fetch_size: 4 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 4]: 2 records inserted SELECT * FROM public.t3 ORDER BY object::text COLLATE "C"; subject | predicate | object -----------------------------+-----------------------------------------------+-------------------------- https://www.uni-muenster.de | http://dbpedia.org/ontology/wikiPageExtracted | Wed Dec 24 13:00:42 2025 https://www.uni-muenster.de | http://dbpedia.org/property/modified | Wed Dec 24 18:30:42 2025 (2 rows) /* date data type */ CREATE FOREIGN TABLE ft4 ( subject text OPTIONS (variable '?s'), predicate text OPTIONS (variable '?p'), object date OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o FILTER (?p = || ?p = )}' ); CALL rdf_fdw_clone_table( foreign_table => 'public.ft4', target_table => 'public.t4', fetch_size => 4, verbose => true, create_table => true, commit_page => false ); WARNING: the rdf_fdw FOREIGN TABLE "ft4" has columns using native PostgreSQL types which are deprecated: subject, predicate, object HINT: Use the "rdfnode" type instead. INFO: Target TABLE "public.t4" created based on FOREIGN TABLE "public.ft4": CREATE TABLE public.t4 AS SELECT * FROM public.ft4 WITH NO DATA; INFO: == Parameters == foreign_table: 'ft4' target_table: 'public.t4' create_table: 'true' fetch_size: 4 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 4]: 2 records inserted SELECT * FROM public.t4 ORDER BY object::text COLLATE "C"; subject | predicate | object -----------------------------+-----------------------------------------+------------ https://www.uni-muenster.de | http://dbpedia.org/ontology/restingDate | 02-29-2024 https://www.uni-muenster.de | http://dbpedia.org/property/established | 04-16-1780 (2 rows) /* numeric data type */ CREATE FOREIGN TABLE ft5 ( subject text OPTIONS (variable '?s'), predicate text OPTIONS (variable '?p'), object numeric OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o FILTER (?p = || ?p = )}' ); CALL rdf_fdw_clone_table( foreign_table => 'public.ft5', target_table => 'public.t5', fetch_size => 4, verbose => true, create_table => true, commit_page => false ); WARNING: the rdf_fdw FOREIGN TABLE "ft5" has columns using native PostgreSQL types which are deprecated: subject, predicate, object HINT: Use the "rdfnode" type instead. INFO: Target TABLE "public.t5" created based on FOREIGN TABLE "public.ft5": CREATE TABLE public.t5 AS SELECT * FROM public.ft5 WITH NO DATA; INFO: == Parameters == foreign_table: 'ft5' target_table: 'public.t5' create_table: 'true' fetch_size: 4 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 4]: 2 records inserted SELECT * FROM public.t5 ORDER BY object::text COLLATE "C"; subject | predicate | object -----------------------------+---------------------------------------------+--------------------- https://www.uni-muenster.de | http://dbpedia.org/property/verySmallNumber | 0.000000000000001 https://www.uni-muenster.de | http://dbpedia.org/property/veryLargeNumber | 9999999999999999999 (2 rows) /* int data type */ CREATE FOREIGN TABLE ft6 ( subject text OPTIONS (variable '?s'), predicate text OPTIONS (variable '?p'), object int OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o FILTER (?p = || ?p = )}' ); CALL rdf_fdw_clone_table( foreign_table => 'public.ft6', target_table => 'public.t6', fetch_size => 4, verbose => true, create_table => true, commit_page => false ); WARNING: the rdf_fdw FOREIGN TABLE "ft6" has columns using native PostgreSQL types which are deprecated: subject, predicate, object HINT: Use the "rdfnode" type instead. INFO: Target TABLE "public.t6" created based on FOREIGN TABLE "public.ft6": CREATE TABLE public.t6 AS SELECT * FROM public.ft6 WITH NO DATA; INFO: == Parameters == foreign_table: 'ft6' target_table: 'public.t6' create_table: 'true' fetch_size: 4 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 4]: 2 records inserted SELECT * FROM public.t6 ORDER BY object::text COLLATE "C"; subject | predicate | object -----------------------------+-------------------------------------------------+----------- https://www.uni-muenster.de | http://dbpedia.org/property/administrativeStaff | 1924 https://www.uni-muenster.de | http://dbpedia.org/property/budget | 803600000 (2 rows) /* * A record need not bind every variable the query selects. A column with no * binding in a given record is still a column of that record, and its value * is unknown: it has to be inserted as NULL rather than left out of the * statement, which hands the row to whatever default the target column * carries. When no column of a record is bound there is nothing to leave in * either, and the statement built from it is not a statement at all. */ CREATE FOREIGN TABLE ft_unbound ( subject rdfnode OPTIONS (variable '?subject'), absent rdfnode OPTIONS (variable '?absent') ) SERVER fuseki OPTIONS (sparql $$ SELECT ?subject ?absent WHERE { ?subject ?o . OPTIONAL { ?subject ?absent } } $$); CREATE TABLE t_unbound (subject rdfnode, absent rdfnode DEFAULT '"a default"'); CALL rdf_fdw_clone_table( foreign_table => 'public.ft_unbound', target_table => 'public.t_unbound', create_table => false, commit_page => false ); /* the absent binding must be NULL, not the column's default */ SELECT subject, absent IS NULL AS absent_is_null FROM t_unbound; subject | absent_is_null -------------------------------+---------------- | t (1 row) /* and a record in which nothing at all is bound still makes a row */ CREATE FOREIGN TABLE ft_nothing ( absent rdfnode OPTIONS (variable '?absent') ) SERVER fuseki OPTIONS (sparql $$ SELECT ?absent WHERE { ?subject ?o . OPTIONAL { ?subject ?absent } } $$); CREATE TABLE t_nothing (absent rdfnode); CALL rdf_fdw_clone_table( foreign_table => 'public.ft_nothing', target_table => 'public.t_nothing', create_table => false, commit_page => false ); SELECT count(*) AS rows_cloned, count(absent) AS bound_values FROM t_nothing; rows_cloned | bound_values -------------+-------------- 1 | 0 (1 row) DROP TABLE t_unbound; DROP TABLE t_nothing; DROP FOREIGN TABLE ft_unbound; DROP FOREIGN TABLE ft_nothing; /* * fetch_size is read from the foreign table as well as from the server, and * the table's value is the one that applies. The clone procedure takes its own * fetch_size argument, which overrides both; leaving it at its default of 0 is * what makes the option under test the one that decides the page size. * * The verbose output reports the size it settled on and one line per page, so * both the value and the paging it produces are visible. */ ALTER SERVER fuseki OPTIONS (ADD fetch_size '10'); CREATE FOREIGN TABLE ft_fetch ( subject rdfnode OPTIONS (variable '?s'), predicate rdfnode OPTIONS (variable '?p'), object rdfnode OPTIONS (variable '?o') ) SERVER fuseki OPTIONS ( sparql 'SELECT * WHERE {?s ?p ?o}', fetch_size '25' ); /* the table's 25 applies, not the server's 10 */ CALL rdf_fdw_clone_table( foreign_table => 'public.ft_fetch', target_table => 'public.t_fetch', verbose => true, create_table => true, commit_page => false ); INFO: Target TABLE "public.t_fetch" created based on FOREIGN TABLE "public.ft_fetch": CREATE TABLE public.t_fetch AS SELECT * FROM public.ft_fetch WITH NO DATA; INFO: == Parameters == foreign_table: 'ft_fetch' target_table: 'public.t_fetch' create_table: 'true' fetch_size: 25 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 25]: 25 records inserted INFO: [25 - 50]: 5 records inserted /* without the table option the server's 10 applies */ ALTER FOREIGN TABLE ft_fetch OPTIONS (DROP fetch_size); CALL rdf_fdw_clone_table( foreign_table => 'public.ft_fetch', target_table => 'public.t_fetch2', verbose => true, create_table => true, commit_page => false ); INFO: Target TABLE "public.t_fetch2" created based on FOREIGN TABLE "public.ft_fetch": CREATE TABLE public.t_fetch2 AS SELECT * FROM public.ft_fetch WITH NO DATA; INFO: == Parameters == foreign_table: 'ft_fetch' target_table: 'public.t_fetch2' create_table: 'true' fetch_size: 10 begin_offset: 0 max_records: 0 ordering_column: 'NOT SET' ordering sparql variable: '?s' sort_order: 'ASC' INFO: [0 - 10]: 10 records inserted INFO: [10 - 20]: 10 records inserted INFO: [20 - 30]: 10 records inserted /* a page size is rejected when it is not a non-negative integer */ ALTER FOREIGN TABLE ft_fetch OPTIONS (ADD fetch_size 'abc'); ERROR: invalid fetch_size: 'abc' HINT: Expected an integer between 0 and 2147483647. DROP TABLE public.t_fetch; DROP TABLE public.t_fetch2; DROP FOREIGN TABLE ft_fetch; ALTER SERVER fuseki OPTIONS (DROP fetch_size); DELETE FROM ft; DROP TABLE public.t1; DROP TABLE public.t2; DROP TABLE public.t3; DROP TABLE public.t4; DROP TABLE public.t5; DROP TABLE public.t6; DROP SERVER fuseki CASCADE; NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to foreign table ft drop cascades to user mapping for postgres on server fuseki drop cascades to foreign table ft2 drop cascades to foreign table ft3 drop cascades to foreign table ft4 drop cascades to foreign table ft5 drop cascades to foreign table ft6