File Coverage

blib/lib/RDF/RDB2RDF/Simple.pm
Criterion Covered Total %
statement 28 30 93.3
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 38 40 95.0


line stmt bran cond sub pod time code
1             package RDF::RDB2RDF::Simple;
2              
3 4     4   165 use 5.010;
  4         14  
  4         316  
4 4     4   22 use strict;
  4         7  
  4         306  
5 4     4   19 use utf8;
  4         5  
  4         20  
6              
7 4     4   129 use Carp qw[carp croak];
  4         6  
  4         442  
8 4     4   4316 use Data::UUID;
  4         9991  
  4         306  
9 4     4   29 use Digest::MD5 qw[md5_hex];
  4         7  
  4         230  
10 4     4   8929 use DBI;
  4         56078  
  4         248  
11 4     4   6597 use JSON qw[];
  4         145648  
  4         112  
12 4     4   35 use overload qw[];
  4         9  
  4         90  
13 4     4   6378 use RDF::Trine qw[statement blank literal];
  0            
  0            
14             use RDF::Trine::Namespace qw[rdf rdfs owl xsd];
15             use Scalar::Util qw[blessed];
16             use URI::Escape::Optimistic qw[uri_escape_optimistic];
17              
18             use namespace::clean;
19             use base qw[
20             RDF::RDB2RDF
21             RDF::RDB2RDF::DatatypeMapper
22             ];
23              
24             our $AUTHORITY = 'cpan:TOBYINK';
25             our $VERSION = '0.008';
26              
27             sub new
28             {
29             my ($class, %mappings) = @_;
30             my $ns = delete($mappings{-namespaces}) // +{};
31             my $base = delete($mappings{-base}) // 'http://example.com/base/';
32             while (my ($k, $v) = each %$ns)
33             {
34             $ns->{$k} = RDF::Trine::Namespace->new($v)
35             unless (blessed($v) and $v->isa('RDF::Trine::Namespace'));
36             }
37             bless {
38             mappings => \%mappings,
39             namespaces => $ns,
40             base => $base,
41             }, $class;
42             }
43              
44             sub mappings
45             {
46             my ($self) = @_;
47             return $self->{mappings};
48             }
49              
50             sub namespaces
51             {
52             my ($self) = @_;
53             my %NS;
54            
55             %NS = %{ $self->{namespaces} }
56             if (ref $self->{namespaces} eq 'HASH' or blessed($self->{namespaces}));
57            
58             %NS = (
59             owl => "$owl",
60             rdf => "$rdf",
61             rdfs => "$rdfs",
62             xsd => "$xsd",
63             ) unless %NS;
64            
65             return %NS;
66             }
67              
68             sub template
69             {
70             my ($self, $template, $data, $types, $process) = @_;
71             $process = sub { +shift } unless ref $process eq 'CODE';
72            
73             if (blessed($template) and $template->isa('RDF::Trine::Node'))
74             {
75             return $template;
76             }
77              
78             if (blessed($template) and $template->isa('RDF::RDB2RDF::R2RML::_COL_'))
79             {
80             my $col = substr($template, 1, -1);
81             return $data->{$col};
82             }
83              
84             $self->{uuid} = Data::UUID->new unless $self->{uuid};
85             $data->{'+uuid'} = $self->{uuid}->create_str;
86            
87             $template =~ s(
88             (?<!\\) \{ # opening unescaped brace
89             ( # start capturing
90             (?: \\\} | [^}] )+ # escaped closing braces and non-brace characters
91             ) # finish capturing
92             \} # closing brace
93             )
94             {
95             (my $key = $1)
96             =~ s/\\\}/\}/g;
97             my ($value, $type);
98            
99             if ($key =~ /^"(.+)"$/)
100             {
101             $value = ($data->{$1});
102             $type = ($types->{$1} // 'varchar');
103             }
104             else
105             {
106             $value = ($data->{$key} // $data->{lc $key});
107             $type = ($types->{$key} // $types->{lc $key} // '');
108             }
109            
110             return unless defined $value; # Argh! return in a regexp!
111             $process->( $self->datatyped_literal($value, $type)->literal_value );
112             }gex;
113            
114             $template =~ s< \\ ( [{}\\] ) >< $1 >xg;
115            
116             return $template;
117             }
118              
119             sub template_irisafe
120             {
121             template(@_, \&URI::Escape::Optimistic::uri_escape_optimistic);
122             }
123              
124             sub iri
125             {
126             my ($self, $iri, $graph) = @_;
127            
128             return
129             unless defined $iri;
130             return $iri
131             if blessed($iri) && $iri->isa('RDF::Trine::Node');
132             return blank()
133             if $iri eq '[]';
134            
135             if ($iri =~ /^_:(.*)$/)
136             {
137             my $ident = $1;
138             $ident =~ s/([^0-9A-Za-wyz])/sprintf('x%04X', ord($1))/eg;
139             if ($graph)
140             {
141             $ident = md5_hex("$graph").$ident;
142             }
143             return blank($ident);
144             }
145            
146             return RDF::Trine::Node::Resource->new("$iri", $self->{base});
147             }
148              
149             sub process
150             {
151             my ($self, $dbh, $model) = @_;
152             $model = RDF::Trine::Model->temporary_model unless defined $model;
153             my $callback = (ref $model eq 'CODE')
154             ? $model
155             : sub { $model->add_statement(@_) };
156            
157             my $mappings = $self->mappings;
158             TABLE: foreach my $table (keys %$mappings)
159             {
160             $self->handle_table($dbh, $callback, $table);
161             }
162              
163             return $model;
164             }
165              
166             sub _mktemplate
167             {
168             my ($self, $string) = @_;
169             $string =~ s! ( [\\{}] ) !\\$1!gx;
170             return $string;
171             }
172              
173             sub _get_types
174             {
175             my ($self, $sth, $dbh) = @_;
176            
177             my %types;
178             if (exists $sth->{pg_type})
179             {
180             # For PostgreSQL, this appears to give better results.
181             # Particularly in the case of columns which don't exist
182             # in the table itself. (e.g. aggregation, casting,
183             # functions, etc)
184             @types{ @{$sth->{NAME}} } =
185             @{ $sth->{pg_type} };
186             }
187             else
188             {eval{
189             @types{ @{$sth->{NAME}} } =
190             map {
191             /^\d+$/
192             ? scalar($dbh->type_info($_)->{TYPE_NAME})
193             : $_
194             }
195             @{ $sth->{TYPE} };
196             }}
197            
198             return \%types;
199             }
200              
201             sub handle_table
202             {
203             my ($self, $dbh, $model, $table) = @_;
204             $model = RDF::Trine::Model->temporary_model unless defined $model;
205             my $callback = (ref $model eq 'CODE')?$model:sub{$model->add_statement(@_)};
206            
207             my $mappings = $self->mappings;
208             my $tmap = $mappings->{$table};
209            
210             # ->{from}
211             my $from = $tmap->{from} || $table;
212              
213             # ->{select}
214             my $select = $tmap->{select} || '*';
215            
216             # ->{sql}
217             my $sql = "SELECT $select FROM $from";
218             $sql = $tmap->{sql} if $tmap->{sql};# =~ /^\s*SELECT/i;
219            
220             ### Re-jig mapping structure.
221             {
222             $tmap->{-maps} = [];
223             $tmap->{-jmaps} = [];
224            
225             my %c = %{$tmap->{columns}};
226             while (my ($col, $list) = each %c)
227             {
228             push @{ $tmap->{-maps} },
229             map { {%$_, column => $col}; }
230             grep { !defined $_->{join} }
231             @$list;
232             push @{ $tmap->{-jmaps} },
233             grep { defined $_->{join} }
234             @$list;
235             }
236             }
237            
238             my $sth = $dbh->prepare($sql) or return;
239             $sth->execute;
240             my $types = $self->_get_types($sth, $dbh);
241            
242             my $row_count = 0;
243             ROW: while (my $row = $sth->fetchrow_hashref)
244             {
245             $row_count++;
246             $self->handle_row($dbh, $callback, $table, $row, $types, $row_count);
247             }
248            
249             JMAP: foreach my $map (@{ $tmap->{-jmaps} })
250             {
251             my $method = lc $map->{method};
252             $method ||= 'subquery' if $map->{join} =~ /^\s*SELECT/i;
253             $method ||= 'table' if $map->{join} =~ /^[A-Za-z0-9_]+$/i;
254             croak sprintf("Cannot join to '' without method.", $map->{join})
255             unless $method eq 'subquery' || $method eq 'table';
256            
257             my $evil_sql = sprintf('SELECT * FROM %s AS "r2r_join_parent", %s AS "r2r_join_child"',
258             sprintf(($method eq 'subquery' ? '(%s)' : '"%s"'), $map->{join}),
259             ($tmap->{sql} or $tmap->{select}) ? "($sql)" : $from,
260             );
261            
262             if (@{ $map->{on} })
263             {
264             $evil_sql .= ' WHERE ' .
265             join ' AND ',
266             map { sprintf('"r2r_join_parent"."%s"="r2r_join_child"."%s"', $_->{parent}, $_->{child}) }
267             @{ $map->{on} };
268             }
269            
270             my $evil_sth = $dbh->prepare($evil_sql);
271             $sth->execute;
272             my $types = $self->_get_types($sth, $dbh);
273            
274             my $evil_row_count = 0;
275             ROW: while (my $evil_row = $evil_sth->fetchrow_hashref)
276             {
277             $evil_row_count++;
278             $self->handle_jmap($dbh, $callback, $table, $map, $evil_row, $types, $evil_row_count);
279             }
280             }
281            
282             delete $tmap->{-maps};
283             delete $tmap->{-jmaps};
284             return $callback;
285             }
286              
287             sub handle_row
288             {
289             my ($self, $dbh, $model, $table, $row, $types, $row_count) = @_;
290             $model = RDF::Trine::Model->temporary_model unless defined $model;
291             my $callback = (ref $model eq 'CODE')?$model:sub{$model->add_statement(@_)};
292            
293             my $mappings = $self->mappings;
294             my $tmap = $mappings->{$table};
295            
296             # ->{graph}
297             my $graph = undef;
298             $graph = $self->iri( $self->template_irisafe($tmap->{graph}, $row, $types) )
299             if defined $tmap->{graph};
300            
301             # ->{about}
302             my $subject = $self->_extract_subject_from_row($tmap, $row, $types);
303             return unless defined $subject;
304            
305             # ->{typeof}
306             foreach (@{ $tmap->{typeof} })
307             {
308             $_ = $self->iri($_, $graph) unless ref $_;
309             $callback->(statement($self->iri($subject, $graph), $rdf->type, $_));
310             }
311              
312             foreach (@{ $tmap->{-maps} })
313             {
314             $self->handle_map($dbh, $model, $table, $row, $types, $row_count, $_, $graph, $subject);
315             }
316             }
317              
318             sub _extract_subject_from_row
319             {
320             my ($self, $tmap, $row, $types) = @_;
321             if ($tmap->{about} and $tmap->{_about_is_template})
322             {
323             return $self->template_irisafe($tmap->{about}, $row, $types);
324             }
325             elsif ($tmap->{about} and $tmap->{about} =~ m< ^ {\" ([^}]+?) \"} $ >x)
326             {
327             return $row->{$1};
328             }
329             elsif ($tmap->{about} and $tmap->{about} =~ m< ^ { ([^}]+?) } $ >x)
330             {
331             return $row->{$1} if exists $row->{$1};
332             return $row->{lc $1};
333             }
334             return ($tmap->{about} // '[]');
335             }
336              
337             sub handle_jmap
338             {
339             my ($self, $dbh, $model, $table, $jmap, $row, $types, $row_count) = @_;
340             $model = RDF::Trine::Model->temporary_model unless defined $model;
341             my $callback = (ref $model eq 'CODE')?$model:sub{$model->add_statement(@_)};
342            
343             my $mappings = $self->mappings;
344             my $tmap = $mappings->{$table};
345            
346             # ->{graph}
347             my $graph = undef;
348             $graph = $self->iri( $self->template_irisafe($tmap->{graph}, $row, $types) )
349             if defined $tmap->{graph};
350            
351             # ->{about}
352             my $subject = $self->_extract_subject_from_row($tmap, $row, $types);
353            
354             $self->handle_map($dbh, $model, $table, $row, $types, $row_count, $jmap, $graph, $subject);
355             }
356              
357             sub handle_map
358             {
359             my ($self, $dbh, $model, $table, $row, $types, $row_count, $map, $graph, $subject) = @_;
360             state $parsers = {};
361            
362             $model = RDF::Trine::Model->temporary_model unless defined $model;
363             my $callback = (ref $model eq 'CODE')?$model:sub{$model->add_statement(@_)};
364            
365             my $mappings = $self->mappings;
366             my $tmap = $mappings->{$table};
367             my %row = %$row;
368             my $column = $map->{column};
369            
370             my ($predicate, $value, $lang, $loose);
371             if ($column =~ /^"(.+)"$/)
372             { $column = $1; $value = $row{$column} }
373             elsif (exists $row{$column})
374             { $loose = 1; $value = $row{$column} }
375             elsif (exists $row{lc $column})
376             { $loose = 1; $value = $row{lc $column} }
377            
378             if (my $lang_col = $map->{lang_col})
379             {
380             if ($lang_col =~ /^"(.+)"$/)
381             { $lang = $row{$1} }
382             elsif (defined $lang_col)
383             { $lang = $row{$lang_col} // $row{lc $lang_col} }
384             }
385             $lang //= $map->{lang};
386            
387             my $lgraph = defined $map->{graph}
388             ? $self->iri($self->template_irisafe($map->{graph}, $row, $types))
389             : $graph;
390            
391             if (defined $map->{parse} and uc $map->{parse} eq 'TURTLE')
392             {
393             return unless length $value;
394            
395             my %NS = $self->namespaces;
396             my $turtle =
397             join '',
398             (map { sprintf("\@prefix %s: <%s>.\n", $_, $NS{$_}) } keys %NS),
399             sprintf("\@base <%s>.\n", $subject->uri),
400             $value,
401             "\n";
402             return eval {
403             $parsers->{ $map->{parse} } = RDF::Trine::Parser->new($map->{parse});
404             $parsers->{ $map->{parse} }->parse(
405             $subject,
406             $turtle,
407             ($lgraph ? sub { $callback->($_[0], $lgraph) } : $callback),
408             );
409             };
410             }
411              
412             if ($map->{rev} || $map->{rel})
413             {
414             $predicate = $map->{rev} || $map->{rel};
415            
416             if ($map->{resource})
417             {
418             $value = $self->template_irisafe($map->{resource}, +{ %row, '_' => $value }, $types);
419             }
420             $value = $self->iri($value, $lgraph);
421             }
422            
423             elsif ($map->{property})
424             {
425             $predicate = $map->{property};
426            
427             if ($map->{content})
428             {
429             $value = $self->template($map->{content}, +{ %row, '_' => $value }, $types);
430             }
431            
432             if ($lang)
433             {
434             $value = literal($value, $lang);
435             }
436             else
437             {
438             if ($map->{datatype})
439             {
440             $value = literal($value, undef, $map->{datatype});
441             }
442             elsif (!defined $map->{content})
443             {
444             my $type = $types->{$column};
445             $type = $types->{lc $column} if $loose && not exists $types->{$column};
446             $value = $self->datatyped_literal($value, $type);
447             }
448             else
449             {
450             $value = literal($value);
451             }
452             }
453             }
454            
455             if (defined $predicate and defined $value)
456             {
457             if (blessed($predicate) && $predicate->isa('RDF::RDB2RDF::R2RML::_COL_')
458             or not ref $predicate)
459             {
460             $predicate = $self->template_irisafe($predicate, +{ %row, '_' => $value }, $types);
461             $predicate = $self->iri($predicate, $lgraph) ;
462             }
463            
464             my $lsubject = $self->iri($subject, $lgraph);
465             if ($map->{about})
466             {
467             $lsubject = $self->iri($self->template_irisafe($map->{about}, $row, $types), $lgraph);
468             }
469             return unless defined $lsubject;
470              
471             my $st = $map->{rev}
472             ? statement($value, $predicate, $lsubject)
473             : statement($lsubject, $predicate, $value);
474            
475             if ($lgraph)
476             {
477             $callback->($st, $lgraph);
478             }
479             else
480             {
481             $callback->($st);
482             }
483             }
484             }
485              
486             sub process_turtle
487             {
488             my ($self, $dbh, %options) = @_;
489              
490             my $rv;
491             unless ($options{no_json})
492             {
493             my $json = $self->to_json(canonical=>1, pretty=>1);
494             $json =~ s/^/# /gm;
495             $json = "# MAPPING\n#\n${json}\n";
496             $rv .= $json;
497             }
498              
499             $rv .= $self->SUPER::process_turtle($dbh, namespaces => {$self->namespaces});
500             $rv;
501             }
502              
503             sub to_hashref
504             {
505             my ($self) = @_;
506            
507             return {
508             -namespaces => $self->_export( {$self->namespaces} ),
509             %{ $self->_export( $self->mappings ) },
510             };
511             }
512              
513             *TO_JSON = \&to_hashref;
514              
515             sub to_json
516             {
517             my ($self, %opts) = (exists $_[1] and ref $_[1] eq 'HASH') ? ($_[0], %{ $_[1] }) : @_;
518             $opts{convert_blessed} = 1;
519             JSON::to_json($self, {%opts});
520             }
521              
522             sub _export
523             {
524             my ($self, $thingy) = @_;
525            
526             return undef unless defined $thingy;
527            
528             if (ref $thingy eq 'HASH' or (blessed($thingy) and $thingy->isa('RDF::Trine::NamespaceMap')))
529             {
530             my $hash = {};
531             while (my ($k, $v) = each %$thingy)
532             {
533             $hash->{$k} = ref $v ? $self->_export($v) : $v;
534             }
535             return $hash;
536             }
537              
538             if (ref $thingy eq 'ARRAY')
539             {
540             return [ map { ref $_ ? $self->_export($_) : $_ } @$thingy ];
541             }
542            
543             if (blessed($thingy) and $thingy->isa('RDF::RDB2RDF::R2RML::_COL_'))
544             {
545             return "$thingy";
546             }
547              
548             if (blessed($thingy) and $thingy->isa('RDF::Trine::Node::Resource'))
549             {
550             return $self->_mktemplate($thingy->uri);
551             }
552              
553             if (blessed($thingy) and $thingy->isa('RDF::Trine::Node::BlankNode'))
554             {
555             return '_:'.$self->_mktemplate($thingy->identifier);
556             }
557              
558             if (blessed($thingy) and $thingy->isa('RDF::Trine::Node::Literal'))
559             {
560             warn "This shouldn't happen!";
561             return $self->_mktemplate($thingy->literal_value);
562             }
563            
564             if (blessed($thingy) and $thingy->isa('RDF::Trine::Namespace'))
565             {
566             return $self->_mktemplate($thingy->uri->uri);
567             }
568            
569             warn "This shouldn't happen either!" if ref $thingy;
570             return "$thingy";
571             }
572              
573             1;
574              
575             __END__
576              
577             =encoding utf8
578              
579             =head1 NAME
580              
581             RDF::RDB2RDF::Simple - map relational database to RDF easily
582              
583             =head1 SYNOPSIS
584              
585             my $mapper = RDF::RDB2RDF->new('Simple', %mappings, -namespaces => \%ns);
586             print $mapper->process_turtle($dbh);
587              
588             =head1 DESCRIPTION
589              
590             This module makes it reasonably easy to dump a relational SQL database as
591             an RDF graph.
592              
593             =head2 Constructor
594              
595             =over
596              
597             =item * C<< RDF::RDB2RDF::Simple->new(%mappings [, -namespaces=>\%ns]) >>
598              
599             =item * C<< RDF::RDB2RDF->new('Simple', %mappings [, -namespaces=>\%ns]) >>
600              
601             The constructor takes a hash of mappings. (See MAPPINGS below.) You may also
602             pass a reference to a set of namespaces. This can be a hashref, or an
603             L<RDF::Trine::NamespaceMap>.
604              
605             =back
606              
607             =head2 Methods
608              
609             =over
610              
611             =item * C<< process($dbh [, $destination]) >>
612              
613             Given a database handle, produces RDF data. Can optionally be passed a
614             destination for triples: either an existing model to add data to, or a
615             reference to a callback function.
616              
617             Returns a L<RDF::Trine::Model>.
618              
619             =item * C<< process_turtle($dbh, %options) >>
620              
621             As per C<process>, but returns a string in Turtle format.
622              
623             The mapping is included as a JSON comment at the top of the Turtle. Passing
624             C<< no_json => 1 >> can disable that feature.
625              
626             Returns a string.
627              
628             =item * C<< to_hashref >>
629              
630             Creates a hashref of the mappings and namespaces, which can later be fed
631             back to the constructor to re-produce this object.
632              
633             Returns a hashref.
634              
635             =item * C<< to_json(%options) >>
636              
637             Produces the JSON equivalent of C<to_hashref>. Any valid options for the
638             L<JSON> module's C<to_json> function can be passed.
639              
640             Returns a string.
641              
642             =item * C<< namespaces >>
643              
644             The namespaces known about by the object.
645              
646             Returns a hash.
647              
648             =item * C<< mappings >>
649              
650             The mappings.
651              
652             Returns a hashref.
653              
654             =back
655              
656             =head1 MAPPINGS
657              
658             It's best just to show you...
659              
660             use RDF::Trine::Namespace qw[rdf rdfs owl xsd];
661             my $foaf = RDF::Trine::Namespace->new('http://xmlns.com/foaf/0.1/');
662             my $bibo = RDF::Trine::Namespace->new('http://purl.org/ontology/bibo/');
663             my $dc = RDF::Trine::Namespace->new('http://purl.org/dc/terms/');
664             my $skos = RDF::Trine::Namespace->new('http://www.w3.org/2004/02/skos/core#');
665              
666             my %simple_mapping = (
667            
668             -namespaces => {
669             bibo => "$bibo",
670             dc => "$dc",
671             foaf => "$foaf",
672             rdfs => "$rdfs",
673             skos => "$skos",
674             },
675            
676             books => {
677             about => 'http://example.net/id/book/{book_id}',
678             typeof => [$bibo->Book],
679             columns => {
680             title => [{property => $rdfs->label, lang=>'en'},
681             {property => $dc->title, lang=>'en'}],
682             turtle => [{parse => 'Turtle'}],
683             },
684             },
685            
686             authors => {
687             select => "*, forename||' '||surname AS fullname",
688             about => 'http://example.net/id/author/{author_id}',
689             typeof => [$foaf->Person],
690             columns => {
691             forename => [{property => $foaf->givenName}],
692             surname => [{property => $foaf->familyName}],
693             fullname => [{property => $rdfs->label},
694             {property => $foaf->name}],
695             turtle => [{parse => 'Turtle'}],
696             },
697             },
698            
699             topics => {
700             about => 'http://example.net/id/topic/{topic_id}',
701             typeof => [$skos->Concept],
702             columns => {
703             label => [{property => $rdfs->label, lang=>'en'},
704             {property => $skos->prefLabel, lang=>'en'}],
705             turtle => [{parse => 'Turtle'}],
706             },
707             },
708            
709             book_authors => {
710             about => 'http://example.net/id/book/{book_id}',
711             columns => {
712             author_id=> [{rel => $dc->creator,
713             resource => 'http://example.net/id/author/{author_id}'},
714             {rel => $foaf->maker,
715             resource => 'http://example.net/id/author/{author_id}'},
716             {rev => $foaf->made,
717             resource => 'http://example.net/id/author/{author_id}'},
718             {rel => $bibo->author,
719             resource => 'http://example.net/id/author/{author_id}'}],
720             },
721             },
722            
723             book_topics => {
724             about => ['http://example.net/id/book/{book_id}'],
725             columns => {
726             topic_id => [{rel => $dc->subject,
727             resource => 'http://example.net/id/topic/{topic_id}'}],
728             },
729             },
730             );
731            
732             Looking at the "books" mapping alone for now, we see:
733              
734             about => 'http://example.net/id/book/{book_id}',
735              
736             This tells us that for each row of the "books" table in the database, generate
737             a subject URI using the template C<< http://example.net/id/book/{book_id} >>. Note
738             that column names appearing in curly braces get substituted for the relevent
739             values.
740              
741             Generating blank nodes is easy: either use a template along the lines of
742             C<< _:book{book_id} >> or simply omit the "about" line altogether.
743              
744             typeof => [$bibo->Book],
745              
746             This is a shorthand for assigning classes to the subject URI.
747              
748             columns => {
749             title => [{property => $rdfs->label, lang=>'en'},
750             {property => $dc->title, lang=>'en'}],
751              
752             This says to map the "title" column of the table to rdfs:label and dc:title.
753             These will be literals, with language tag "en".
754              
755             turtle => [{parse => 'Turtle'}],
756             },
757              
758             This last bit is somewhat weird and experimental. If you have a varchar/text
759             column in your database that includes chunks of Turtle, these can be parsed
760             into the model too. They are parsed using the current namespace map, with
761             a base URI corresponding to the URI from "about".
762              
763             In addition to the "about", "typeof" and "columns" options there are also
764             "select" and "from" options allowing you to fine tune exactly what data the
765             mapping is processing. And indeed, there is an "sql" option which overrides
766             both. An example of "select" is shown in the authors mapping above.
767              
768             Note that within:
769              
770             {property => $dc->title, lang=>'en'}
771              
772             there is a whole lot of interesting stuff going on. The object of the triple
773             here is a literal. If it were a URI, we'd do this:
774              
775             {rel => $dc->title}
776              
777             Note that these correspond with the meanings of "property" and "rel" in RDFa.
778             Like RDFa, there is also "rev" which reverses the subject and object of the
779             triple. An example can be seen in the "book_authors" mapping above for
780             foaf:made.
781              
782             For literals "lang" and "datatype" further qualify them.
783              
784             Usually, the contents of the database field are used. For example:
785              
786             columns => {
787             book_id => [{ property => $dc->identifier }],
788             },
789              
790             However, sometimes you might want to slot the data from the database into
791             a template:
792              
793             columns => {
794             book_id => [{ property => $dc->identifier,
795             content => 'urn:example:book:{book_id}' }],
796             },
797              
798             In these cases, the column mapping key becomes pretty irrelevent. The following
799             will still work fine on the same database:
800              
801             columns => {
802             foobar => [{ property => $dc->identifier,
803             content => 'urn:example:book:{book_id}' }],
804             },
805              
806             When "rel" or "rev" are used (i.e. not "property"), then "resource" should be
807             used (i.e. not "content").
808              
809             Pretty much anywhere where a URI or literal value is expected, you can either
810             give a string, or an L<RDF::Trine::Node>. In cases of strngs, they will be
811             interpolated as templates. L<RDF::Trine::Node>s are not interpolated.
812              
813             =head1 BUGS
814              
815             Please report any bugs to
816             L<http://rt.cpan.org/Dist/Display.html?Queue=RDF-RDB2RDF>.
817              
818             =head1 SEE ALSO
819              
820             L<RDF::Trine>, L<RDF::RDB2RDF>, L<RDF::RDB2RDF::R2RML>.
821              
822             L<http://www.perlrdf.org/>.
823              
824             =head1 AUTHOR
825              
826             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
827              
828             =head1 COPYRIGHT
829              
830             Copyright 2011-2013 Toby Inkster
831              
832             This library is free software; you can redistribute it and/or modify it
833             under the same terms as Perl itself.
834              
835             =head1 DISCLAIMER OF WARRANTIES
836              
837             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
838             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
839             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
840