root/branches/release-41/lib/MT/ObjectDriver/DDL/SQLite.pm @ 2705

Revision 2705, 6.3 kB (checked in by bchoate, 17 months ago)

Added support for determining autoincrement/nullable columns for sqlite's DDL module.

  • Property svn:keywords set to Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::ObjectDriver::DDL::SQLite;
8
9use strict;
10use warnings;
11use base qw( MT::ObjectDriver::DDL );
12
13sub index_defs {
14    my $ddl = shift;
15    my ($class) = @_;
16    my $driver = $class->driver;
17    my $dbh = $driver->r_handle;
18    my $field_prefix = $class->datasource;
19    my $table_name = $class->table_name;
20    my $sth = $dbh->prepare(<<SQL)
21SELECT name, sql
22FROM sqlite_master
23WHERE type = "index"
24AND tbl_name="$table_name"
25SQL
26        or return undef;
27    $sth->execute or return undef;
28
29    my $defs = {};
30    while (my $row = $sth->fetchrow_hashref) {
31        my $key = $row->{'name'};
32        next unless $key =~ m/^(mt_)?\Q$field_prefix\E_/;
33        next if $key =~ m/(.+autoindex)/;
34        my $sql = $row->{'sql'}
35            or next;
36
37        $key =~ s/^mt_\Q$field_prefix\E_//;
38        my $cols = [];
39        my $is_unique = 0;
40        my $idx_columns;
41        if ( $sql =~ m/CREATE( UNIQUE)? INDEX (?:.+?) ON $table_name \((.+?)\)/i ) {
42            $is_unique = $1 ? 'unique' eq lc($1) : 0;
43            $idx_columns = $2;
44            for my $col ( split ',', $idx_columns ) {
45                $col =~ s/^\Q$field_prefix\E_//;
46                push @$cols, $col;
47            }
48        }
49        unless ( $is_unique ) {
50            # Check constraints to identify unique index
51            my $sth_tbl = $dbh->prepare(<<TBLSQL);
52SELECT name, sql FROM sqlite_master
53WHERE type = "table" AND name = "$table_name"
54TBLSQL
55            $sth_tbl->execute or next;
56            my $rows_tbl = $sth_tbl->fetchall_hashref('name');
57            $sth_tbl->finish;
58            my $sql_tbl = $rows_tbl->{$table_name}->{'sql'}
59                or next;
60            my $idx_name = $row->{'name'};
61            if ( $sql_tbl =~ m/CONSTRAINT\s+$idx_name\s+UNIQUE\s+\(\s*$idx_columns\s*\)/im ) {
62                $is_unique = 1;
63            }
64        }
65
66        if ( $is_unique ) {
67            $defs->{$key} = { 'unique' => 1, 'columns' => $cols };
68        }
69        else {
70            if ((@$cols == 1) && ($key eq $cols->[0])) {
71                $defs->{$key} = 1;
72            } else {
73                $defs->{$key} = { 'columns' => $cols };
74            }
75        }
76
77    }
78    $sth->finish;
79    return undef unless %$defs;
80
81    return $defs;
82}
83
84sub column_defs {
85    my $ddl = shift; 
86    my ($class) = @_;
87
88    my $driver = $class->driver;
89    my $dbh = $driver->r_handle;
90    my $table_name = $class->table_name;
91    my $field_prefix = $class->datasource;
92
93    return undef unless $dbh;
94
95    # Disable RaiseError if set, since the table we're about to describe
96    # may not actually exist (in which case, the return value is undef,
97    # signalling an nonexistent table to the caller).
98    local $dbh->{RaiseError} = 1;
99    my $sth = $dbh->prepare('PRAGMA table_info("' . $table_name . '")')
100        or return undef;
101    $sth->execute or return undef;
102    my $defs = {};
103    while (my $row = $sth->fetchrow_hashref) {
104        my $colname = lc $row->{name};
105        $colname =~ s/^\Q$field_prefix\E_//i;
106        my $coltype = $ddl->db2type($row->{type});
107        if ($row->{type} =~ m/\((\d+)\)/) {
108            $defs->{$colname}{size} = $1;
109        }
110        $defs->{$colname}{type} = $coltype;
111        if ($colname =~ m/_id$/) {
112            $defs->{$colname}{key} = 1;
113        }
114        if ( ($coltype eq 'integer') && $row->{pk} ) {
115            # with sqlite, integer primary keys auto increment. always.
116            $defs->{$colname}{key} = 1;
117            $defs->{$colname}{auto} = 1;
118        }
119        $defs->{$colname}{not_null} = 1
120            if $row->{notnull};
121        $defs->{$colname}{default} = $row->{dflt_value}
122            if defined $row->{dflt_value};
123    }
124    $sth->finish;
125    return undef unless %$defs;
126    return $defs;
127}
128
129sub db2type {
130    my $ddl = shift;
131    my ($db_type) = @_;
132    $db_type =~ s/\(\d+\)//g;
133    if ($db_type eq 'varchar') {
134        $db_type = 'string';
135    }
136    return $db_type;
137}
138
139sub type2db {
140    my $ddl = shift;
141    my ($def) = @_;
142    return undef if !defined $def;
143    my $type = (ref($def) eq 'HASH') ? $def->{type} : $def;
144    $type = $def->{type};
145    if ($type eq 'string') {
146        return 'varchar(' . $def->{size} . ')';
147    } elsif ($type eq 'smallint' ) {
148        return 'smallint';
149    } elsif ($type eq 'bigint' ) {
150        return 'bigint';
151    } elsif ($type eq 'boolean') {
152        return 'boolean';
153    } elsif ($type eq 'datetime') {
154        return 'datetime';
155    } elsif ($type eq 'timestamp') {
156        return 'timestamp';
157    } elsif ($type eq 'integer') {
158        return 'integer';
159    } elsif ($type eq 'blob') {
160        return 'blob';
161    } elsif ($type eq 'text') {
162        return 'text';
163    } elsif ($type eq 'float') {
164        return 'float';
165    }
166    Carp::croak("undefined type: ". $type);
167}
168
169sub can_add_constraint { 0 }
170
171sub unique_constraint_sql {
172    my $ddl = shift;
173    my ($class) = @_;
174
175    my $table_name = $class->table_name;
176    my $props = $class->properties;
177    my $field_prefix = $class->datasource;
178    my $indexes = $props->{indexes};
179
180    my @stmts;
181    if ($indexes) {
182        # FIXME: Handle possible future primary key tuple case
183        my $pk = $props->{primary_key};
184        foreach my $name (keys %$indexes) {
185            next if $pk && $name eq $pk;
186            if (ref $indexes->{$name} eq 'HASH') {
187                my $idx_info = $indexes->{$name};
188                next unless exists($idx_info->{unique}) && $idx_info->{unique};
189                my $column_list = $idx_info->{columns} || [ $name ];
190                my $columns = '';
191                foreach my $col (@$column_list) {
192                    $columns .= ',' unless $columns eq '';
193                    $columns .= $field_prefix . '_' . $col;
194                }
195                if ($columns) {
196                    push @stmts, "CONSTRAINT ${table_name}_$name UNIQUE ($columns)";
197                }
198            }
199        }
200    }
201    if (@stmts) {
202        return ',' . join("\n", @stmts);
203    }
204    return q();
205}
206
207sub drop_index_sql {
208    my $ddl = shift;
209    my ($class, $key) = @_;
210    my $table_name = $class->table_name;
211
212    my $props = $class->properties;
213    my $indexes = $props->{indexes};
214    return q() unless exists($indexes->{$key});
215
216    return "DROP INDEX ${table_name}_$key";
217}
218
2191;
Note: See TracBrowser for help on using the browser.