Fix for SQLite PKs
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / SQLite.pm
1 package DBIx::Class::Schema::Loader::SQLite;
2
3 use strict;
4 use base qw/DBIx::Class::Schema::Loader::Generic/;
5 use Text::Balanced qw( extract_bracketed );
6 use Carp;
7
8 =head1 NAME
9
10 DBIx::Class::Schema::Loader::SQLite - DBIx::Class::Schema::Loader SQLite Implementation.
11
12 =head1 SYNOPSIS
13
14   use DBIx::Class::Schema::Loader;
15
16   # $loader is a DBIx::Class::Schema::Loader::SQLite
17   my $loader = DBIx::Class::Schema::Loader->new(
18     dsn       => "dbi:SQLite:dbname=/path/to/dbfile",
19     namespace => "Data",
20   );
21
22 =head1 DESCRIPTION
23
24 See L<DBIx::Class::Schema::Loader>.
25
26 =cut
27
28 sub _db_classes {
29     return qw/DBIx::Class::PK::Auto::SQLite/;
30 }
31
32 sub _relationships {
33     my $class = shift;
34     foreach my $table ( $class->tables ) {
35
36         my $dbh = $class->storage->dbh;
37         my $sth = $dbh->prepare(<<"");
38 SELECT sql FROM sqlite_master WHERE tbl_name = ?
39
40         $sth->execute($table);
41         my ($sql) = $sth->fetchrow_array;
42         $sth->finish;
43
44         # Cut "CREATE TABLE ( )" blabla...
45         $sql =~ /^[\w\s]+\((.*)\)$/si;
46         my $cols = $1;
47
48         # strip single-line comments
49         $cols =~ s/\-\-.*\n/\n/g;
50
51         # temporarily replace any commas inside parens,
52         # so we don't incorrectly split on them below
53         my $cols_no_bracketed_commas = $cols;
54         while ( my $extracted =
55             ( extract_bracketed( $cols, "()", "[^(]*" ) )[0] )
56         {
57             my $replacement = $extracted;
58             $replacement              =~ s/,/--comma--/g;
59             $replacement              =~ s/^\(//;
60             $replacement              =~ s/\)$//;
61             $cols_no_bracketed_commas =~ s/$extracted/$replacement/m;
62         }
63
64         # Split column definitions
65         for my $col ( split /,/, $cols_no_bracketed_commas ) {
66
67             # put the paren-bracketed commas back, to help
68             # find multi-col fks below
69             $col =~ s/\-\-comma\-\-/,/g;
70
71             # CDBI doesn't have built-in support multi-col fks, so ignore them
72             next if $col =~ s/^\s*FOREIGN\s+KEY\s*//i && $col =~ /^\([^,)]+,/;
73
74             # Strip punctuations around key and table names
75             $col =~ s/[()\[\]'"]/ /g;
76             $col =~ s/^\s+//gs;
77
78             # Grab reference
79             if ( $col =~ /^(\w+).*REFERENCES\s+(\w+)\s*(\w+)?/i ) {
80                 chomp $col;
81                 warn qq/\# Found foreign key definition "$col"\n\n/
82                   if $class->debug_loader;
83                 eval { $class->_belongs_to_many( $table, $1, $2, $3 ) };
84                 warn qq/\# belongs_to_many failed "$@"\n\n/
85                   if $@ && $class->debug_loader;
86             }
87         }
88     }
89 }
90
91 sub _tables {
92     my $class = shift;
93     my $dbh = $class->storage->dbh;
94     my $sth  = $dbh->prepare("SELECT * FROM sqlite_master");
95     $sth->execute;
96     my @tables;
97     while ( my $row = $sth->fetchrow_hashref ) {
98         next unless lc( $row->{type} ) eq 'table';
99         push @tables, $row->{tbl_name};
100     }
101     return @tables;
102 }
103
104 sub _table_info {
105     my ( $class, $table ) = @_;
106
107     # find all columns.
108     my $dbh = $class->storage->dbh;
109     my $sth = $dbh->prepare("PRAGMA table_info('$table')");
110     $sth->execute();
111     my @columns;
112     while ( my $row = $sth->fetchrow_hashref ) {
113         push @columns, $row->{name};
114     }
115     $sth->finish;
116
117     # find primary key. so complex ;-(
118     $sth = $dbh->prepare(<<'SQL');
119 SELECT sql FROM sqlite_master WHERE tbl_name = ?
120 SQL
121     $sth->execute($table);
122     my ($sql) = $sth->fetchrow_array;
123     $sth->finish;
124     my ($primary) = $sql =~ m/
125     (?:\(|\,) # either a ( to start the definition or a , for next
126     \s*       # maybe some whitespace
127     (\w+)     # the col name
128     [^,]*     # anything but the end or a ',' for next column
129     PRIMARY\sKEY/sxi;
130     my @pks;
131
132     if ($primary) {
133         @pks = ($primary);
134     }
135     else {
136         my ($pks) = $sql =~ m/PRIMARY\s+KEY\s*\(\s*([^)]+)\s*\)/i;
137         @pks = split( m/\s*\,\s*/, $pks ) if $pks;
138     }
139     return ( \@columns, \@pks );
140 }
141
142 =head1 SEE ALSO
143
144 L<DBIx::Schema::Class::Loader>
145
146 =cut
147
148 1;