Search code examples
perlsqliteconstraintsunique

How do you determine if a column has UNIQUE constraint set in an SQLite database?


I am writing a Perl based application that converts an SQLite database into an Excel workbook and back again. It works as is, but is missing one small piece. I do not know how to determine if the UNIQUE constraint has been set for a specific column.

To get a list of tables in a database, I use:

select name from sqlite_master 
  where type = 'table' 
  and name <> 'sqlite_sequence' order by name;

Then to get column and constraint information for each table, I do:

PRAGMA table_info($TableName);

This tells me everything I need to know except if a column has the UNIQUE constraint enabled.

In case this is not clear, here is a trivial example. Suppose I create a database table by doing this:

CREATE TABLE DATA 
(
    ID integer primary key, 
    Invoice integer unique, 
    Product varchar, 
    Comment varchar
);

I want to know how I can subsequently interrogate the SQLite database table created in this manner to determine which columns have the UNIQUE constraint set. In this case, it would be the Invoice column.

Any suggestions?


Solution

  • You need to use a the PRAGMA index_list statement.

    This subroutine determines the value of the unique attribute, given a database handle and a table and column name.

    sub column_unique {
      my ($dbh, $table, $column) = @_;
      my $sth = $dbh->prepare("PRAGMA index_list($table)");
      $sth->execute;
      my $rec = $sth->fetchall_hashref('name');
      return $rec->{$column} && $rec->{$column}{unique};
    }
    

    If the column appears in the index_list pragma for the table, the value of the unique attribute (either '0' or '1') is returned. If not then undef is returned. This allows calls like

    if (column_unique($dbh, 'DATA', 'Invoice')) { ... }
    

    Edit

    My apologies, my first answer was wrong. SQLite allows only indexes to be declared unique, and those indexes can be multi-column combinations. This revised subroutine looks at the composition of each unique index for the table and checks whether the given column is all or part of it.

    This isn't very satisfactory, as being a part of a unique index doesn't say much about the column itself, but any improvement would depend on what you want to use the information for.

    sub column_unique {
    
      my ($dbh, $table, $column) = @_;
    
      my $sth = $dbh->prepare("PRAGMA index_list($table)");
      $sth->execute;
      my $list = $sth->fetchall_hashref('name');
    
      foreach my $index (keys %$list) {
    
        next unless $list->{$index}{unique};
    
        my $sth = $dbh->prepare("PRAGMA index_info($index)");
        $sth->execute;
        my $info = $sth->fetchall_hashref('name');
    
        return 1 if $info->{$column};
      }
    
      return 0;
    }