From: David Jack Olrik Date: Sat, 3 Mar 2007 17:58:21 +0000 (+0000) Subject: Merge 'trunk' into 'DBIx-Class-current' X-Git-Tag: v0.08010~150^2~93 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=90545b68b06c2d2d288d593462539fe45fff48a6;hp=32be057ce33f8a211402ecfbd714bc2bea4c8d73;p=dbsrgits%2FDBIx-Class.git Merge 'trunk' into 'DBIx-Class-current' r27233@ip-37-237 (orig r3005): castaway | 2007-01-05 10:25:11 +0100 add inflatecolumn fixes to Changes r27496@ip-37-237 (orig r3007): castaway | 2007-01-09 22:20:55 +0100 todoify collapse test r27497@ip-37-237 (orig r3008): castaway | 2007-01-09 22:42:59 +0100 Up version to 0.07004 r27498@ip-37-237 (orig r3009): castaway | 2007-01-09 22:44:39 +0100 0.07004 r27499@ip-37-237 (orig r3010): castaway | 2007-01-10 18:54:19 +0100 Skip .orig files in dist r27500@ip-37-237 (orig r3011): castaway | 2007-01-10 20:37:23 +0100 oops, remove .orig files etc r27546@ip-37-237 (orig r3053): zarquon | 2007-01-23 11:15:05 +0100 minor doc improvement to clarify how to do joins r27720@ip-37-237 (orig r3058): zarquon | 2007-01-24 22:48:07 +0100 explained a cryptic error message r27732@ip-37-237 (orig r3064): castaway | 2007-01-29 17:28:12 +0100 Make POD a little more obvious about component order --This line, and those below, will be ignored-- M DateTime.pm r27738@ip-37-237 (orig r3070): jester | 2007-02-01 00:59:06 +0100 Doc cleanup in Ordered.pm r27740@ip-37-237 (orig r3072): castaway | 2007-02-02 20:48:53 +0100 POD updates r27741@ip-37-237 (orig r3073): ash | 2007-02-03 22:33:19 +0100 Made ->update not change the hashref passed in (with test.) r27743@ip-37-237 (orig r3075): ash | 2007-02-05 13:29:52 +0100 Backed out my r3073, and doc'd the fact that it takes a hashref that might be changed r27744@ip-37-237 (orig r3076): castaway | 2007-02-05 14:56:35 +0100 Add _accessor example, thanks to grinktt3n r27745@ip-37-237 (orig r3077): castaway | 2007-02-05 15:23:22 +0100 Fix overload example r27747@ip-37-237 (orig r3079): grink1tt3n | 2007-02-06 20:40:00 +0100 Added; FAQ-fetch-a-formatted-column.txt FAQ-store-JSON-in-a-column.txt r27762@ip-37-237 (orig r3094): castaway | 2007-02-08 09:25:54 +0100 Argh! Fix spurious example r27765@ip-37-237 (orig r3097): castaway | 2007-02-13 15:00:21 +0100 Improve resultset_attributes docs r27766@ip-37-237 (orig r3098): grink1tt3n | 2007-02-14 08:16:40 +0100 Fleshed out the JSON inflated column example with a corresponding YAML example. Added a cautionary warning against overuse. Added a link to InflateColumn. r27767@ip-37-237 (orig r3099): grink1tt3n | 2007-02-14 09:39:26 +0100 Added how to '.. fetch a single (or topmost) row?' to the FAQ r27768@ip-37-237 (orig r3100): zarquon | 2007-02-14 09:44:18 +0100 improved docs for as attrib r27769@ip-37-237 (orig r3101): zarquon | 2007-02-14 11:05:51 +0100 pointed out ambiguity of as attribs r27967@ip-37-237 (orig r3107): castaway | 2007-03-03 13:15:03 +0100 Fix to $filename from Carl Vincent --- diff --git a/Build.PL b/Build.PL index 2ba9d22..a919ccc 100644 --- a/Build.PL +++ b/Build.PL @@ -1,32 +1,3 @@ -use strict; -use Module::Build; - -my %arguments = ( - create_makefile_pl => 'passthrough', - license => 'perl', - module_name => 'DBIx::Class', - requires => { - 'Cwd' => 3.19, - 'Data::Page' => 2.00, - 'Scalar::Util' => 0, - 'SQL::Abstract' => 1.20, - 'SQL::Abstract::Limit' => 0.101, - 'Class::C3' => 0.13, - 'Storable' => 0, - 'Class::Data::Accessor' => 0.01, - 'Carp::Clan' => 0, - 'DBI' => 1.40, - 'Module::Find' => 0, - 'Class::Inspector' => 0, - }, - build_requires => { - 'DBD::SQLite' => 1.11, - }, - create_makefile_pl => 'passthrough', - create_readme => 1, - test_files => [ glob('t/*.t'), glob('t/*/*.t') ], - script_files => [ glob('script/*') ], -); - -Module::Build->new(%arguments)->create_build_script; - +# Dear Distribution Packager. This use of require is intentional. +# Module::Install detects Build.PL usage and acts accordingly. +require 'Makefile.PL'; diff --git a/Changes b/Changes index eb87d1b..bcc8a45 100644 --- a/Changes +++ b/Changes @@ -22,7 +22,6 @@ Revision history for DBIx::Class - fix quote tests for recent versions of SQLite - added reference implementation of Manual::Example - backported column_info_from_storage accessor from -current, but - defaults true instead of false in 0.07xxx - fixed inflate_datetime.t tests/stringify under older Test::More - minor fixes for many-to-many relationship helpers - cleared up Relationship docs, and fixed some typos diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index fa93767..eaa6bc0 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -25,6 +25,7 @@ \.tmp$ \.old$ \.bak$ +\..*?\.sw[po]$ \#$ \b\.# diff --git a/Makefile.PL b/Makefile.PL index 192903a..b9eeb22 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,31 +1,31 @@ -# Note: this file was auto-generated by Module::Build::Compat version 0.03 - - unless (eval "use Module::Build::Compat 0.02; 1" ) { - print "This module requires Module::Build to install itself.\n"; - - require ExtUtils::MakeMaker; - my $yn = ExtUtils::MakeMaker::prompt - (' Install Module::Build now from CPAN?', 'y'); - - unless ($yn =~ /^y/i) { - die " *** Cannot install without Module::Build. Exiting ...\n"; - } - - require Cwd; - require File::Spec; - require CPAN; - - # Save this 'cause CPAN will chdir all over the place. - my $cwd = Cwd::cwd(); - - CPAN::Shell->install('Module::Build::Compat'); - CPAN::Shell->expand("Module", "Module::Build::Compat")->uptodate - or die "Couldn't install Module::Build, giving up.\n"; - - chdir $cwd or die "Cannot chdir() back to $cwd: $!"; - } - eval "use Module::Build::Compat 0.02; 1" or die $@; - - Module::Build::Compat->run_build_pl(args => \@ARGV); - require Module::Build; - Module::Build::Compat->write_makefile(build_class => 'Module::Build'); +use inc::Module::Install 0.64; + +name 'DBIx-Class'; +all_from 'lib/DBIx/Class.pm'; +perl_version '5.006001'; + +requires 'Cwd' => 3.19; +requires 'Data::Page' => 2.00; +requires 'Scalar::Util' => 0; +requires 'SQL::Abstract' => 1.20; +requires 'SQL::Abstract::Limit' => 0.101; +requires 'Class::C3' => 0.13; +requires 'Storable' => 0; +requires 'Carp::Clan' => 0; +requires 'DBI' => 1.40; +requires 'Module::Find' => 0; +requires 'Class::Inspector' => 0; +requires 'Class::Accessor::Grouped' => 0.03; + +# Perl 5.8.0 doesn't have utf8::is_utf8() +requires 'Encode' => 0 if ($] <= 5.008000); + +build_requires 'DBD::SQLite' => 1.11; + +install_script 'script/dbicadmin'; + +tests "t/*.t t/*/*.t"; + +auto_install; + +WriteAll; diff --git a/lib/DBIx/Class.pm b/lib/DBIx/Class.pm index bc103a3..3ccd2af 100644 --- a/lib/DBIx/Class.pm +++ b/lib/DBIx/Class.pm @@ -4,9 +4,14 @@ use strict; use warnings; use vars qw($VERSION); -use base qw/DBIx::Class::Componentised Class::Data::Accessor/; +use base qw/DBIx::Class::Componentised Class::Accessor::Grouped/; + +sub mk_classdata { + my $self = shift; + $self->mk_group_accessors('inherited', $_[0]); + $self->set_inherited(@_) if @_ > 1; +} -sub mk_classdata { shift->mk_classaccessor(@_); } sub component_base_class { 'DBIx::Class' } # Always remember to do all digits for the version even if they're 0 @@ -180,6 +185,8 @@ andyg: Andy Grundman ank: Andres Kievsky +ash: Ash Berlin + blblack: Brandon L. Black bluefeet: Aran Deltac @@ -210,6 +217,8 @@ konobi: Scott McWhirter LTJake: Brian Cassidy +ned: Neil de Carteret + nigel: Nigel Metheringham ningu: David Kamholz @@ -234,6 +243,8 @@ Todd Lipcon typester: Daisuke Murase +victori: Victor Igumnov + wdh: Will Hawes willert: Sebastian Willert diff --git a/lib/DBIx/Class/AccessorGroup.pm b/lib/DBIx/Class/AccessorGroup.pm deleted file mode 100644 index 56bcf1b..0000000 --- a/lib/DBIx/Class/AccessorGroup.pm +++ /dev/null @@ -1,342 +0,0 @@ -package DBIx::Class::AccessorGroup; - -use strict; -use warnings; - -use Carp::Clan qw/^DBIx::Class/; - -=head1 NAME - -DBIx::Class::AccessorGroup - Lets you build groups of accessors - -=head1 SYNOPSIS - -=head1 DESCRIPTION - -This class lets you build groups of accessors that will call different -getters and setters. - -=head1 METHODS - -=head2 mk_group_accessors - -=over 4 - -=item Arguments: $group, @fieldspec - -Returns: none - -=back - -Creates a set of accessors in a given group. - -$group is the name of the accessor group for the generated accessors; they -will call get_$group($field) on get and set_$group($field, $value) on set. - -@fieldspec is a list of field/accessor names; if a fieldspec is a scalar -this is used as both field and accessor name, if a listref it is expected to -be of the form [ $accessor, $field ]. - -=cut - -sub mk_group_accessors { - my ($self, $group, @fields) = @_; - - $self->_mk_group_accessors('make_group_accessor', $group, @fields); - return; -} - - -{ - no strict 'refs'; - no warnings 'redefine'; - - sub _mk_group_accessors { - my($self, $maker, $group, @fields) = @_; - my $class = ref $self || $self; - - # So we don't have to do lots of lookups inside the loop. - $maker = $self->can($maker) unless ref $maker; - - foreach my $field (@fields) { - if( $field eq 'DESTROY' ) { - carp("Having a data accessor named DESTROY in ". - "'$class' is unwise."); - } - - my $name = $field; - - ($name, $field) = @$field if ref $field; - - my $accessor = $self->$maker($group, $field); - my $alias = "_${name}_accessor"; - - #warn "$class $group $field $alias"; - - *{$class."\:\:$name"} = $accessor; - #unless defined &{$class."\:\:$field"} - - *{$class."\:\:$alias"} = $accessor; - #unless defined &{$class."\:\:$alias"} - } - } -} - -=head2 mk_group_ro_accessors - -=over 4 - -=item Arguments: $group, @fieldspec - -Returns: none - -=back - -Creates a set of read only accessors in a given group. Identical to - but accessors will throw an error if passed a value -rather than setting the value. - -=cut - -sub mk_group_ro_accessors { - my($self, $group, @fields) = @_; - - $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields); -} - -=head2 mk_group_wo_accessors - -=over 4 - -=item Arguments: $group, @fieldspec - -Returns: none - -=back - -Creates a set of write only accessors in a given group. Identical to - but accessors will throw an error if not passed a -value rather than getting the value. - -=cut - -sub mk_group_wo_accessors { - my($self, $group, @fields) = @_; - - $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields); -} - -=head2 make_group_accessor - -=over 4 - -=item Arguments: $group, $field - -Returns: $sub (\CODE) - -=back - -Returns a single accessor in a given group; called by mk_group_accessors -for each entry in @fieldspec. - -=cut - -sub make_group_accessor { - my ($class, $group, $field) = @_; - - my $set = "set_$group"; - my $get = "get_$group"; - - # Build a closure around $field. - return sub { - my $self = shift; - - if(@_) { - return $self->$set($field, @_); - } - else { - return $self->$get($field); - } - }; -} - -=head2 make_group_ro_accessor - -=over 4 - -=item Arguments: $group, $field - -Returns: $sub (\CODE) - -=back - -Returns a single read-only accessor in a given group; called by -mk_group_ro_accessors for each entry in @fieldspec. - -=cut - -sub make_group_ro_accessor { - my($class, $group, $field) = @_; - - my $get = "get_$group"; - - return sub { - my $self = shift; - - if(@_) { - my $caller = caller; - croak("'$caller' cannot alter the value of '$field' on ". - "objects of class '$class'"); - } - else { - return $self->$get($field); - } - }; -} - -=head2 make_group_wo_accessor - -=over 4 - -=item Arguments: $group, $field - -Returns: $sub (\CODE) - -=back - -Returns a single write-only accessor in a given group; called by -mk_group_wo_accessors for each entry in @fieldspec. - -=cut - -sub make_group_wo_accessor { - my($class, $group, $field) = @_; - - my $set = "set_$group"; - - return sub { - my $self = shift; - - unless (@_) { - my $caller = caller; - croak("'$caller' cannot access the value of '$field' on ". - "objects of class '$class'"); - } - else { - return $self->$set($field, @_); - } - }; -} - -=head2 get_simple - -=over 4 - -=item Arguments: $field - -Returns: $value - -=back - -Simple getter for hash-based objects which returns the value for the field -name passed as an argument. - -=cut - -sub get_simple { - my ($self, $get) = @_; - return $self->{$get}; -} - -=head2 set_simple - -=over 4 - -=item Arguments: $field, $new_value - -Returns: $new_value - -=back - -Simple setter for hash-based objects which sets and then returns the value -for the field name passed as an argument. - -=cut - -sub set_simple { - my ($self, $set, $val) = @_; - return $self->{$set} = $val; -} - -=head2 get_component_class - -=over 4 - -=item Arguments: $name - -Returns: $component_class - -=back - -Returns the class name for a component; returns an object key if called on -an object, or attempts to return classdata referenced by _$name if called -on a class. - -=cut - -sub get_component_class { - my ($self, $get) = @_; - if (ref $self) { - return $self->{$get}; - } else { - $get = "_$get"; - return $self->can($get) ? $self->$get : undef; - } -} - -=head2 set_component_class - -=over 4 - -=item Arguments: $name, $new_component_class - -Returns: $new_component_class - -=back - -Sets a component class name; attempts to require the class before setting -but does not error if unable to do so. Sets an object key of the given name -if called or an object or classdata called _$name if called on a class. - -=cut - -sub set_component_class { - my ($self, $set, $val) = @_; - eval "require $val"; - if ($@) { - my $val_path = $val; - $val_path =~ s{::}{/}g; - carp $@ unless $@ =~ /^Can't locate $val_path\.pm/; - } - if (ref $self) { - return $self->{$set} = $val; - } else { - $set = "_$set"; - return $self->can($set) ? - $self->$set($val) : - $self->mk_classdata($set => $val); - } -} - -1; - -=head1 AUTHORS - -Matt S. Trout - -=head1 LICENSE - -You may distribute this code under the same terms as Perl itself. - -=cut - diff --git a/lib/DBIx/Class/Componentised.pm b/lib/DBIx/Class/Componentised.pm index 2b3bf83..ecfe177 100644 --- a/lib/DBIx/Class/Componentised.pm +++ b/lib/DBIx/Class/Componentised.pm @@ -6,7 +6,7 @@ use warnings; use Class::C3; use Class::Inspector; -use Carp::Clan qw/DBIx::Class/; +use Carp::Clan qw/^DBIx::Class/; sub inject_base { my ($class, $target, @to_inject) = @_; diff --git a/lib/DBIx/Class/Core.pm b/lib/DBIx/Class/Core.pm index 7c48181..92dd74c 100644 --- a/lib/DBIx/Class/Core.pm +++ b/lib/DBIx/Class/Core.pm @@ -7,14 +7,12 @@ no warnings 'qw'; use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/ - Serialize::Storable Relationship InflateColumn PK::Auto PK Row - ResultSourceProxy::Table - AccessorGroup/); + ResultSourceProxy::Table/); 1; @@ -50,8 +48,6 @@ The core modules currently are: =item L -=item L - =back =head1 AUTHORS diff --git a/lib/DBIx/Class/DB.pm b/lib/DBIx/Class/DB.pm index 007c82a..9d80916 100644 --- a/lib/DBIx/Class/DB.pm +++ b/lib/DBIx/Class/DB.pm @@ -8,6 +8,11 @@ use DBIx::Class::Schema; use DBIx::Class::Storage::DBI; use DBIx::Class::ClassResolver::PassThrough; use DBI; +use Scalar::Util; + +unless ($INC{"DBIx/Class/CDBICompat.pm"}) { + warn "IMPORTANT: DBIx::Class::DB is DEPRECATED AND *WILL* BE REMOVED. DO NOT USE.\n"; +} __PACKAGE__->load_components(qw/ResultSetProxy/); @@ -23,29 +28,15 @@ sub storage { shift->schema_instance(@_)->storage; } DBIx::Class::DB - (DEPRECATED) classdata schema component -=head1 SYNOPSIS - - package MyDB; - - use base qw/DBIx::Class/; - __PACKAGE__->load_components('DB'); - - __PACKAGE__->connection('dbi:...', 'user', 'pass', \%attrs); - - package MyDB::MyTable; - - use base qw/MyDB/; - __PACKAGE__->load_components('Core'); # just load this in MyDB if it will - # always be there - - ... - =head1 DESCRIPTION This class is designed to support the Class::DBI connection-as-classdata style for DBIx::Class. You are *strongly* recommended to use a DBIx::Class::Schema instead; DBIx::Class::DB will not undergo new development and will be moved -to being a CDBICompat-only component before 1.0. +to being a CDBICompat-only component before 1.0. In order to discourage further +use, documentation has been removed as of 0.08000 + +=begin HIDE_BECAUSE_THIS_CLASS_IS_DEPRECATED =head1 METHODS @@ -150,13 +141,43 @@ native L system. =cut sub resultset_instance { - my $class = ref $_[0] || $_[0]; - my $source = $class->result_source_instance; + $_[0]->result_source_instance->resultset +} + +sub result_source_instance { + my $class = shift; + $class = ref $class || $class; + + __PACKAGE__->mk_classdata(qw/_result_source_instance/) + unless __PACKAGE__->can('_result_source_instance'); + + + return $class->_result_source_instance(@_) if @_; + + my $source = $class->_result_source_instance; + return {} unless Scalar::Util::blessed($source); + if ($source->result_class ne $class) { - $source = $source->new($source); - $source->result_class($class); + # Remove old source instance so we dont get deep recursion + #$DB::single = 1; + # Need to set it to a non-undef value so that it doesn't just fallback to + # a parent class's _result_source_instance + #$class->_result_source_instance({}); + #$class->table($class); + #$source = $class->_result_source_instance; + + $DB::single = 1; + $source = $source->new({ + %$source, + source_name => $class, + result_class => $class + } ); + $class->_result_source_instance($source); + if (my $coderef = $class->can('schema_instance')) { + $coderef->($class)->register_class($class, $class); + } } - return $source->resultset; + return $source; } =head2 resolve_class @@ -177,6 +198,8 @@ Alias for L Alias for L +=end HIDE_BECAUSE_THIS_CLASS_IS_DEPRECATED + =head1 AUTHORS Matt S. Trout diff --git a/lib/DBIx/Class/InflateColumn.pm b/lib/DBIx/Class/InflateColumn.pm index 84f86b8..721894f 100644 --- a/lib/DBIx/Class/InflateColumn.pm +++ b/lib/DBIx/Class/InflateColumn.pm @@ -164,93 +164,6 @@ sub store_inflated_column { return $self->{_inflated_column}{$col} = $inflated; } -=head2 get_column - -Gets a column value in the same way as L. If there -is an inflated value stored that has not yet been deflated, it is deflated -when the method is invoked. - -=cut - -sub get_column { - my ($self, $col) = @_; - if (exists $self->{_inflated_column}{$col} - && !exists $self->{_column_data}{$col}) { - $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col})); - } - return $self->next::method($col); -} - -=head2 get_columns - -Returns the get_column info for all columns as a hash, -just like L. Handles inflation just -like L. - -=cut - -sub get_columns { - my $self = shift; - if (exists $self->{_inflated_column}) { - foreach my $col (keys %{$self->{_inflated_column}}) { - $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col})) - unless exists $self->{_column_data}{$col}; - } - } - return $self->next::method; -} - -=head2 has_column_loaded - -Like L, but also returns true if there -is an inflated value stored. - -=cut - -sub has_column_loaded { - my ($self, $col) = @_; - return 1 if exists $self->{_inflated_column}{$col}; - return $self->next::method($col); -} - -=head2 update - -Updates a row in the same way as L, handling -inflation and deflation of columns appropriately. - -=cut - -sub update { - my ($class, $attrs, @rest) = @_; - foreach my $key (keys %{$attrs||{}}) { - if (ref $attrs->{$key} && $class->has_column($key) - && exists $class->column_info($key)->{_inflate_info}) { - $class->set_inflated_column($key, delete $attrs->{$key}); - } - } - return $class->next::method($attrs, @rest); -} - -=head2 new - -Creates a row in the same way as L, handling -inflation and deflation of columns appropriately. - -=cut - -sub new { - my ($class, $attrs, @rest) = @_; - my $inflated; - foreach my $key (keys %{$attrs||{}}) { - $inflated->{$key} = delete $attrs->{$key} - if ref $attrs->{$key} && $class->has_column($key) - && exists $class->column_info($key)->{_inflate_info}; - } - my $obj = $class->next::method($attrs, @rest); - $obj->{_inflated_column} = $inflated if $inflated; - return $obj; -} - =head1 SEE ALSO =over 4 diff --git a/lib/DBIx/Class/InflateColumn/File.pm b/lib/DBIx/Class/InflateColumn/File.pm new file mode 100644 index 0000000..b7bda60 --- /dev/null +++ b/lib/DBIx/Class/InflateColumn/File.pm @@ -0,0 +1,201 @@ +package DBIx::Class::InflateColumn::File; + +use strict; +use warnings; +use base 'DBIx::Class'; +use File::Path; +use File::Copy; +use IO::File; + +__PACKAGE__->load_components(qw/InflateColumn/); + + +sub register_column { + my ($self, $column, $info, @rest) = @_; + $self->next::method($column, $info, @rest); + return unless defined($info->{is_file_column}); + $self->inflate_column( + $column => + { + inflate => $self->_inflate_file_column, + deflate => sub { + my ( $file, @column_names ) = $self->_load_file_column_information; + $self->_save_file_column( $file, $self, @column_names ); + }, + } + ); +} + + +sub delete { + my ( $self, @rest ) = @_; + + my @column_names = $self->columns; + for (@column_names) { + if ( $self->column_info($_)->{is_file_column} ) { + my $path = + File::Spec->catdir( $self->column_info($_)->{file_column_path}, + $self->id ); + rmtree( [$path], 0, 0 ); + } + } + + my $ret = $self->next::method(@rest); + + return $ret; +} + +sub _inflate_file_column { + my $self = shift; + + my @column_names = $self->columns; + for(@column_names) { + if ( $self->column_info($_)->{is_file_column} ) { + # make sure everything checks out + unless (defined $self->$_) { + # if something is wrong set it to undef + $self->$_(undef); + next; + } + my $fs_file = + File::Spec->catfile( $self->column_info($_)->{file_column_path}, + $self->id, $self->$_ ); + $self->$_({handle => new IO::File($fs_file, "r"), filename => $self->$_}); + } + } +} + +sub _load_file_column_information { + my $self = shift; + + my $file; + my @column_names; + + @column_names = $self->columns; + for (@column_names) { + if ( $self->column_info($_)->{is_file_column} ) { + # make sure everything checks out + unless ((defined $self->$_) || + (defined $self->$_->{filename} && defined $self->$_->{handle})) { + # if something is wrong set it to undef + $self->$_(undef); + next; + } + $file->{$_} = $self->$_; + $self->$_( $self->$_->{filename} ); + } + } + + return ( $file, @column_names ); +} + +sub _save_file_column { + my ( $self, $file, $ret, @column_names ) = @_; + + for (@column_names) { + if ( $ret->column_info($_)->{is_file_column} ) { + next unless (defined $ret->$_); + my $file_path = + File::Spec->catdir( $ret->column_info($_)->{file_column_path}, + $ret->id ); + mkpath [$file_path]; + + my $outfile = + File::Spec->catfile( $file_path, $file->{$_}->{filename} ); + File::Copy::copy( $file->{$_}->{handle}, $outfile ); + + $self->_file_column_callback($file->{$_},$ret,$_); + } + } +} + +=head1 METHODS + +=cut + + +=head2 _file_column_callback ($file,$ret,$target) + +method made to be overridden for callback purposes. + +=cut + +sub _file_column_callback { + my ($self,$file,$ret,$target) = @_; +} + +=head1 NAME + +DBIx::Class::InflateColumn::File - map files from the Database to the filesystem. + +=head1 DESCRIPTION + +InflateColumn::File + +=head1 SYNOPSIS + +In your L table class: + + __PACKAGE__->load_components( "PK::Auto", "InflateColumn::File", "Core" ); + + # define your columns + __PACKAGE__->add_columns( + "id", + { + data_type => "integer", + is_auto_increment => 1, + is_nullable => 0, + size => 4, + }, + "filename", + { + data_type => "varchar", + is_file_column => 1, + file_column_path =>'/tmp/uploaded_files', + # or for a Catalyst application + # file_column_path => MyApp->path_to('root','static','files'), + default_value => undef, + is_nullable => 1, + size => 255, + }, + ); + + +In your L class: + +FileColumn requires a hash that contains L as handle and the file's name as name. + + my $entry = $c->model('MyAppDB::Articles')->create({ + subject => 'blah', + filename => { + handle => $c->req->upload('myupload')->fh, + filename => $c->req->upload('myupload')->basename + }, + body => '....' + }); + $c->stash->{entry}=$entry; + + +And Place the following in your TT template + + Article Subject: [% entry.subject %] + Uploaded File: + File + Body: [% entry.body %] + +The file will be stored on the filesystem for later retrieval. +Calling delete on your resultset will delete the file from the filesystem. +Retrevial of the record automatically inflates the column back to the set hash with the IO::File handle and filename. + +=head1 AUTHOR + +Victor Igumnov + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index e95a95c..bd92f43 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -494,7 +494,7 @@ To order C<< $book->pages >> by descending page_number. =head2 Transactions As of version 0.04001, there is improved transaction support in -L and L. Here is an +L and L. Here is an example of the recommended way to use it: my $genus = $schema->resultset('Genus')->find(12); @@ -912,7 +912,7 @@ method. =head2 Profiling -When you enable L's debugging it prints the SQL +When you enable L's debugging it prints the SQL executed as well as notifications of query completion and transaction begin/commit. If you'd like to profile the SQL you can subclass the L class and write your own profiling @@ -1107,42 +1107,18 @@ B test.pl DBIx::Class is not built for speed, it's built for convenience and ease of use, but sometimes you just need to get the data, and skip the -fancy objects. Luckily this is also fairly easy using -C: - - # Define a class which just returns the results as a hashref: - package My::HashRefInflator; - - ## $me is the hashref of cols/data from the immediate resultsource - ## $prefetch is a deep hashref of all the data from the prefetched - ## related sources. - - sub mk_hash { - my ($me, $rest) = @_; - - return { %$me, - map { ($_ => mk_hash(@{$rest->{$_}})) } keys %$rest - }; - } - - sub inflate_result { - my ($self, $source, $me, $prefetch) = @_; - return mk_hash($me, $prefetch); - } - - # Change the object inflation to a hashref for just this resultset: - $rs->result_class('My::HashRefInflator'); - - my $datahashref = $rs->next; - foreach my $col (keys %$datahashref) { - if(!ref($datahashref->{$col})) { - # It's a plain value - } - elsif(ref($datahashref->{$col} eq 'HASH')) { - # It's a related value in a hashref - } - } - +fancy objects. + +To do this simply use L. + + my $rs = $schema->resultset('CD'); + + $rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); + + my $hash_ref = $rs->find(1); + +Wasn't that easy? + =head2 Want to know if find_or_create found or created a row? Just use C instead, then check C: diff --git a/lib/DBIx/Class/Manual/DocMap.pod b/lib/DBIx/Class/Manual/DocMap.pod index 8d425bb..5820d03 100644 --- a/lib/DBIx/Class/Manual/DocMap.pod +++ b/lib/DBIx/Class/Manual/DocMap.pod @@ -64,6 +64,8 @@ DBIx::Class::Manual::DocMap - What documentation do we have? =item L - Dealing with actual data. +=item L - Basic Storage stuff. + =item L - Storage using L and L. =back diff --git a/lib/DBIx/Class/Manual/FAQ.pod b/lib/DBIx/Class/Manual/FAQ.pod index 2ff72ee..df0f773 100644 --- a/lib/DBIx/Class/Manual/FAQ.pod +++ b/lib/DBIx/Class/Manual/FAQ.pod @@ -376,7 +376,7 @@ to work around this issue. =item See the SQL statements my code is producing? -Turn on debugging! See L for details of how +Turn on debugging! See L for details of how to turn on debugging in the environment, pass your own filehandle to save debug to, or create your own callback. diff --git a/lib/DBIx/Class/Ordered.pm b/lib/DBIx/Class/Ordered.pm index 35ceba4..88e1110 100644 --- a/lib/DBIx/Class/Ordered.pm +++ b/lib/DBIx/Class/Ordered.pm @@ -17,7 +17,6 @@ Create a table for your ordered data. name TEXT NOT NULL, position INTEGER NOT NULL ); - # Optional: group_id INTEGER NOT NULL In your Schema or DB class add "Ordered" to the top of the component list. @@ -29,7 +28,6 @@ each row. package My::Item; __PACKAGE__->position_column('position'); - __PACKAGE__->grouping_column('group_id'); # optional That's it, now you can change the position of your objects. @@ -54,6 +52,10 @@ That's it, now you can change the position of your objects. $item->move_first(); $item->move_last(); $item->move_to( $position ); + $item->move_to_group( 'groupname' ); + $item->move_to_group( 'groupname', $position ); + $item->move_to_group( {group_id=>'groupname', 'other_group_id=>'othergroupname'} ); + $item->move_to_group( {group_id=>'groupname', 'other_group_id=>'othergroupname'}, $position ); =head1 DESCRIPTION @@ -127,6 +129,7 @@ is this sibling. sub first_sibling { my( $self ) = @_; return 0 if ($self->get_column($self->position_column())==1); + return ($self->result_source->resultset->search( { $self->position_column => 1, @@ -290,11 +293,72 @@ sub move_to { $self->_grouping_clause(), }); my $op = ($from_position>$to_position) ? '+' : '-'; - $rs->update({ $position_column => \"$position_column $op 1" }); + $rs->update({ $position_column => \"$position_column $op 1" }); #" Sorry, GEdit bug + $self->{_ORDERED_INTERNAL_UPDATE} = 1; $self->update({ $position_column => $to_position }); return 1; } + + +=head2 move_to_group + + $item->move_to_group( $group, $position ); + +Moves the object to the specified position of the specified +group, or to the end of the group if $position is undef. +1 is returned on success, and 0 is returned if the object is +already at the specified position of the specified group. + +$group may be specified as a single scalar if only one +grouping column is in use, or as a hashref of column => value pairs +if multiple grouping columns are in use. + +=cut + +sub move_to_group { + my( $self, $to_group, $to_position ) = @_; + + # if we're given a string, turn it into a hashref + unless (ref $to_group eq 'HASH') { + $to_group = {($self->_grouping_columns)[0] => $to_group}; + } + + my $position_column = $self->position_column; + #my @grouping_columns = $self->_grouping_columns; + + return 0 if ( ! defined($to_group) ); + return 0 if ( defined($to_position) and $to_position < 1 ); + return 0 if ( $self->_is_in_group($to_group) + and ((not defined($to_position)) + or (defined($to_position) and $self->$position_column==$to_position) + ) + ); + + # Move to end of current group and adjust siblings + $self->move_last; + + $self->set_columns($to_group); + my $new_group_count = $self->result_source->resultset->search({$self->_grouping_clause()})->count(); + if (!defined($to_position) or $to_position > $new_group_count) { + $self->{_ORDERED_INTERNAL_UPDATE} = 1; + $self->update({ $position_column => $new_group_count + 1 }); + } + else { + my @between = ($to_position, $new_group_count); + + my $rs = $self->result_source->resultset->search({ + $position_column => { -between => [ @between ] }, + $self->_grouping_clause(), + }); + $rs->update({ $position_column => \"$position_column + 1" }); #" + $self->{_ORDERED_INTERNAL_UPDATE} = 1; + $self->update({ $position_column => $to_position }); + } + + return 1; +} + =head2 insert Overrides the DBIC insert() method by providing a default @@ -311,6 +375,53 @@ sub insert { return $self->next::method( @_ ); } +=head2 update + +Overrides the DBIC update() method by checking for a change +to the position and/or group columns. Movement within a +group or to another group is handled by repositioning +the appropriate siblings. Position defaults to the end +of a new group if it has been changed to undef. + +=cut + +sub update { + my $self = shift; + + if ($self->{_ORDERED_INTERNAL_UPDATE}) { + delete $self->{_ORDERED_INTERNAL_UPDATE}; + return $self->next::method( @_ ); + } + + $self->set_columns($_[0]) if @_ > 0; + my %changes = $self->get_dirty_columns; + $self->discard_changes; + + my $pos_col = $self->position_column; + + # if any of our grouping columns have been changed + if (grep {$_} map {exists $changes{$_}} $self->_grouping_columns ) { + + # create new_group by taking the current group and inserting changes + my $new_group = {$self->_grouping_clause}; + foreach my $col (keys %$new_group) { + if (exists $changes{$col}) { + $new_group->{$col} = $changes{$col}; + delete $changes{$col}; # don't want to pass this on to next::method + } + } + + $self->move_to_group( + $new_group, + exists($changes{$pos_col}) ? delete($changes{$pos_col}) : $self->$pos_col + ); + } + elsif (exists $changes{$pos_col}) { + $self->move_to(delete $changes{$pos_col}); + } + return $self->next::method( \%changes ); +} + =head2 delete Overrides the DBIC delete() method by first moving the object @@ -337,16 +448,52 @@ by the collection column. If the collection column is not defined then this will return an empty list. =cut - sub _grouping_clause { my( $self ) = @_; + return map { $_ => $self->get_column($_) } $self->_grouping_columns(); +} + + + +=head2 _get_grouping_columns + +Returns a list of the column names used for grouping, regardless of whether +they were specified as an arrayref or a single string, and returns () +if there is no grouping. + +=cut +sub _grouping_columns { + my( $self ) = @_; my $col = $self->grouping_column(); - if ($col) { - return ( $col => $self->get_column($col) ); + if (ref $col eq 'ARRAY') { + return @$col; + } elsif ($col) { + return ( $col ); + } else { + return (); } - return (); } + + +=head2 _is_in_group($other) + + $item->_is_in_group( {user => 'fred', list => 'work'} ) + +Returns true if the object is in the group represented by hashref $other +=cut +sub _is_in_group { + my ($self, $other) = @_; + my $current = {$self->_grouping_clause}; + return 0 unless (ref $other eq 'HASH') and (keys %$current == keys %$other); + for my $key (keys %$current) { + return 0 unless exists $other->{$key}; + return 0 if $current->{$key} ne $other->{$key}; + } + return 1; +} + + 1; __END__ diff --git a/lib/DBIx/Class/Relationship/Accessor.pm b/lib/DBIx/Class/Relationship/Accessor.pm index b77ce00..b20eb16 100644 --- a/lib/DBIx/Class/Relationship/Accessor.pm +++ b/lib/DBIx/Class/Relationship/Accessor.pm @@ -62,39 +62,4 @@ sub add_relationship_accessor { } } -sub new { - my ($class, $attrs, @rest) = @_; - my ($related, $info); - foreach my $key (keys %{$attrs||{}}) { - next unless $info = $class->relationship_info($key); - $related->{$key} = delete $attrs->{$key} - if ref $attrs->{$key} - && $info->{attrs}{accessor} - && $info->{attrs}{accessor} eq 'single'; - } - my $obj = $class->next::method($attrs, @rest); - if ($related) { - $obj->{_relationship_data} = $related; - foreach my $rel (keys %$related) { - $obj->set_from_related($rel, $related->{$rel}); - } - } - return $obj; -} - -sub update { - my ($obj, $attrs, @rest) = @_; - my $info; - foreach my $key (keys %{$attrs||{}}) { - next unless $info = $obj->relationship_info($key); - if (ref $attrs->{$key} && $info->{attrs}{accessor} - && $info->{attrs}{accessor} eq 'single') { - my $rel = delete $attrs->{$key}; - $obj->set_from_related($key => $rel); - $obj->{_relationship_data}{$key} = $rel; - } - } - return $obj->next::method($attrs, @rest); -} - 1; diff --git a/lib/DBIx/Class/ResultClass/HashRefInflator.pm b/lib/DBIx/Class/ResultClass/HashRefInflator.pm new file mode 100644 index 0000000..ca6741e --- /dev/null +++ b/lib/DBIx/Class/ResultClass/HashRefInflator.pm @@ -0,0 +1,76 @@ +package DBIx::Class::ResultClass::HashRefInflator; + +=head1 NAME + +DBIx::Class::ResultClass::HashRefInflator + +=head1 SYNOPSIS + + my $rs = $schema->resultset('CD'); + + $rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); + +=head1 DESCRIPTION + +DBIx::Class is not built for speed: it's built for convenience and +ease of use. But sometimes you just need to get the data, and skip the +fancy objects. That is what this class provides. + +There are two ways of using this class. + +=over + +=item * + +Specify C<< $rs->result_class >> on a specific resultset to affect only that +resultser (and any chained off of it); or + +=item * + +Specify C<< __PACKAGE__->result_class >> on your source object to force all +uses of that result source to be inflated to hash-refs - this approach is not +recomended + +=back + +=head1 METHODS + +=head2 inflate_result + +Inflates the result and prefetched data into a hash-ref using L. + +=cut + +sub inflate_result { + my ($self, $source, $me, $prefetch) = @_; + + return mk_hash($me, $prefetch); +} + +=head2 mk_hash + +This does all the work of inflating the (pre)fetched data. + +=cut + +sub mk_hash { + my ($me, $rest) = @_; + + # $me is the hashref of cols/data from the immediate resultsource + # $rest is a deep hashref of all the data from the prefetched + # related sources. + + # to avoid emtpy has_many rels contain one empty hashref + return if (not keys %$me); + + return { %$me, + map { + ( $_ => + ref($rest->{$_}[0]) eq 'ARRAY' ? [ map { mk_hash(@$_) } @{$rest->{$_}} ] + : mk_hash( @{$rest->{$_}} ) + ) + } keys %$rest + }; +} + +1; diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 6c1066c..a56285c 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -10,10 +10,10 @@ use Carp::Clan qw/^DBIx::Class/; use Data::Page; use Storable; use DBIx::Class::ResultSetColumn; +use DBIx::Class::ResultSourceHandle; use base qw/DBIx::Class/; -__PACKAGE__->load_components(qw/AccessorGroup/); -__PACKAGE__->mk_group_accessors('simple' => qw/result_source result_class/); +__PACKAGE__->mk_group_accessors('simple' => qw/result_class _source_handle/); =head1 NAME @@ -85,7 +85,8 @@ sub new { return $class->new_result(@_) if ref $class; my ($source, $attrs) = @_; - #weaken $source; + $source = $source->handle + unless $source->isa('DBIx::Class::ResultSourceHandle'); $attrs = { %{$attrs||{}} }; if ($attrs->{page}) { @@ -97,8 +98,8 @@ sub new { $attrs->{alias} ||= 'me'; my $self = { - result_source => $source, - result_class => $attrs->{result_class} || $source->result_class, + _source_handle => $source, + result_class => $attrs->{result_class} || $source->resolve->result_class, cond => $attrs->{where}, count => undef, pager => undef, @@ -239,7 +240,7 @@ sub search_rs { : $having); } - my $rs = (ref $self)->new($self->result_source, $new_attrs); + my $rs = (ref $self)->new($self->_source_handle, $new_attrs); if ($rows) { $rs->set_cache($rows); } @@ -743,7 +744,7 @@ sub next { sub _construct_object { my ($self, @row) = @_; my $info = $self->_collapse_result($self->{_attrs}{as}, \@row); - my @new = $self->result_class->inflate_result($self->result_source, @$info); + my @new = $self->result_class->inflate_result($self->_source_handle, @$info); @new = $self->{_attrs}{record_filter}->(@new) if exists $self->{_attrs}{record_filter}; return @new; @@ -919,7 +920,7 @@ sub _count { # Separated out so pager can get the full count # offset, order by and page are not needed to count. record_filter is cdbi delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/; - my $tmp_rs = (ref $self)->new($self->result_source, $attrs); + my $tmp_rs = (ref $self)->new($self->_source_handle, $attrs); my ($count) = $tmp_rs->cursor->next; return $count; } @@ -1110,9 +1111,9 @@ sub update { unless ref $values eq 'HASH'; my $cond = $self->_cond_for_update_delete; - + return $self->result_source->storage->update( - $self->result_source->from, $values, $cond + $self->result_source, $values, $cond ); } @@ -1162,7 +1163,7 @@ sub delete { my $cond = $self->_cond_for_update_delete; - $self->result_source->storage->delete($self->result_source->from, $cond); + $self->result_source->storage->delete($self->result_source, $cond); return 1; } @@ -1230,7 +1231,7 @@ attribute set on the resultset (10 by default). sub page { my ($self, $page) = @_; - return (ref $self)->new($self->result_source, { %{$self->{attrs}}, page => $page }); + return (ref $self)->new($self->_source_handle, { %{$self->{attrs}}, page => $page }); } =head2 new_result @@ -1260,11 +1261,9 @@ sub new_result { my %new = ( %{ $self->_remove_alias($values, $alias) }, %{ $self->_remove_alias($collapsed_cond, $alias) }, - -result_source => $self->result_source, ); - my $obj = $self->result_class->new(\%new); - return $obj; + return $self->result_class->new(\%new,$self->_source_handle); } # _collapse_cond @@ -1558,7 +1557,7 @@ sub related_resultset { my $rel_obj = $self->result_source->relationship_info($rel); $self->throw_exception( - "search_related: result source '" . $self->result_source->name . + "search_related: result source '" . $self->_source_handle->source_moniker . "' has no such relationship $rel") unless $rel_obj; @@ -1567,7 +1566,7 @@ sub related_resultset { my $join_count = $seen->{$rel}; my $alias = ($join_count > 1 ? join('_', $rel, $join_count) : $rel); - $self->result_source->schema->resultset($rel_obj->{class})->search_rs( + $self->_source_handle->schema->resultset($rel_obj->{class})->search_rs( undef, { %{$self->{attrs}||{}}, join => undef, @@ -1608,7 +1607,7 @@ sub _resolved_attrs { return $self->{_attrs} if $self->{_attrs}; my $attrs = { %{$self->{attrs}||{}} }; - my $source = $self->{result_source}; + my $source = $self->result_source; my $alias = $attrs->{alias}; $attrs->{columns} ||= delete $attrs->{cols} if exists $attrs->{cols}; @@ -1740,6 +1739,16 @@ sub _merge_attr { } } +sub result_source { + my $self = shift; + + if (@_) { + $self->_source_handle($_[0]->handle); + } else { + $self->_source_handle->resolve; + } +} + =head2 throw_exception See L for details. @@ -1748,7 +1757,7 @@ See L for details. sub throw_exception { my $self=shift; - $self->result_source->schema->throw_exception(@_); + $self->_source_handle->schema->throw_exception(@_); } # XXX: FIXME: Attributes docs need clearing up @@ -1770,8 +1779,8 @@ Which column(s) to order the results by. This is currently passed through directly to SQL, so you can give e.g. C for a descending order on the column `year'. -Please note that if you have quoting enabled (see -L) you will need to do C<\'year DESC' > to +Please note that if you have C enabled (see +L) you will need to do C<\'year DESC' > to specify an order. (The scalar ref causes it to be passed as raw sql to the DB, so you will need to manually quote things as appropriate.) diff --git a/lib/DBIx/Class/ResultSetColumn.pm b/lib/DBIx/Class/ResultSetColumn.pm index 49b8456..876a3c1 100644 --- a/lib/DBIx/Class/ResultSetColumn.pm +++ b/lib/DBIx/Class/ResultSetColumn.pm @@ -35,12 +35,8 @@ passed as params. Used internally by L. sub new { my ($class, $rs, $column) = @_; $class = ref $class if ref $class; - - my $object_ref = { _column => $column, - _parent_resultset => $rs }; - - my $new = bless $object_ref, $class; - $new->throw_exception("column must be supplied") unless ($column); + my $new = bless { _column => $column, _parent_resultset => $rs }, $class; + $new->throw_exception("column must be supplied") unless $column; return $new; } @@ -64,7 +60,6 @@ one value. sub next { my $self = shift; - $self->{_resultset} = $self->{_parent_resultset}->search(undef, {select => [$self->{_column}], as => [$self->{_column}]}) unless ($self->{_resultset}); my ($row) = $self->{_resultset}->cursor->next; return $row; @@ -111,8 +106,7 @@ resultset (or C if there are none). =cut sub min { - my $self = shift; - return $self->func('MIN'); + return shift->func('MIN'); } =head2 max @@ -133,8 +127,7 @@ resultset (or C if there are none). =cut sub max { - my $self = shift; - return $self->func('MAX'); + return shift->func('MAX'); } =head2 sum @@ -155,8 +148,7 @@ the resultset. Use on varchar-like columns at your own risk. =cut sub sum { - my $self = shift; - return $self->func('SUM'); + return shift->func('SUM'); } =head2 func @@ -180,9 +172,7 @@ value. Produces the following SQL: =cut sub func { - my $self = shift; - my $function = shift; - + my ($self,$function) = @_; my ($row) = $self->{_parent_resultset}->search(undef, {select => {$function => $self->{_column}}, as => [$self->{_column}]})->cursor->next; return $row; } diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index dbbe1ad..54374f1 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -4,19 +4,21 @@ use strict; use warnings; use DBIx::Class::ResultSet; +use DBIx::Class::ResultSourceHandle; use Carp::Clan qw/^DBIx::Class/; use Storable; use base qw/DBIx::Class/; -__PACKAGE__->load_components(qw/AccessorGroup/); __PACKAGE__->mk_group_accessors('simple' => qw/_ordered_columns _columns _primaries _unique_constraints name resultset_attributes - schema from _relationships column_info_from_storage source_name/); + schema from _relationships column_info_from_storage source_info/); -__PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class +__PACKAGE__->mk_group_accessors('inherited' => qw/resultset_class result_class/); +__PACKAGE__->mk_group_ro_accessors('simple' => qw/source_name/); + =head1 NAME DBIx::Class::ResultSource - Result source object @@ -46,9 +48,7 @@ sub new { my ($class, $attrs) = @_; $class = ref $class if ref $class; - my $new = { %{$attrs || {}}, _resultset => undef }; - bless $new, $class; - + my $new = bless { %{$attrs || {}} }, $class; $new->{resultset_class} ||= 'DBIx::Class::ResultSet'; $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} }; $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}]; @@ -56,14 +56,22 @@ sub new { $new->{_relationships} = { %{$new->{_relationships}||{}} }; $new->{name} ||= "!!NAME NOT SET!!"; $new->{_columns_info_loaded} ||= 0; - if(!defined $new->column_info_from_storage) { - $new->{column_info_from_storage} = 1 - } return $new; } =pod +=head2 source_info + +Stores a hashref of per-source metadata. No specific key names +have yet been standardized, the examples below are purely hypothetical +and don't actually accomplish anything on their own: + + __PACKAGE__->source_info({ + "_tablespace" => 'fast_disk_array_3', + "_engine" => 'InnoDB', + }); + =head2 add_columns $table->add_columns(qw/col1 col2 col3/); @@ -223,13 +231,10 @@ sub column_info { =head2 column_info_from_storage -Enables or disables the on-demand automatic loading of the above -column metadata from storage as neccesary. Defaults to true in the -current release, but will default to false in future releases starting -with 0.08000. This is *deprecated*, and should not be used. It will -be removed before 1.0. +Enables the on-demand automatic loading of the above column +metadata from storage as neccesary. This is *deprecated*, and +should not be used. It will be removed before 1.0. - __PACKAGE__->column_info_from_storage(0); __PACKAGE__->column_info_from_storage(1); =head2 columns @@ -965,12 +970,6 @@ sub resultset { 'call it on the schema instead.' ) if scalar @_; - # disabled until we can figure out a way to do it without consistency issues - # - #return $self->{_resultset} - # if ref $self->{_resultset} eq $self->resultset_class; - #return $self->{_resultset} = - return $self->resultset_class->new( $self, $self->{resultset_attributes} ); @@ -996,6 +995,20 @@ its class name. # from your schema... $schema->resultset('Books')->find(1); +=head2 handle + +Obtain a new handle to this source. Returns an instance of a +L. + +=cut + +sub handle { + return new DBIx::Class::ResultSourceHandle({ + schema => $_[0]->schema, + source_moniker => $_[0]->source_name + }); +} + =head2 throw_exception See L. diff --git a/lib/DBIx/Class/ResultSource/Table.pm b/lib/DBIx/Class/ResultSource/Table.pm index 1e56359..cf263d1 100644 --- a/lib/DBIx/Class/ResultSource/Table.pm +++ b/lib/DBIx/Class/ResultSource/Table.pm @@ -5,8 +5,6 @@ use warnings; use DBIx::Class::ResultSet; -use Carp qw/croak/; - use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/ResultSource/); diff --git a/lib/DBIx/Class/ResultSourceHandle.pm b/lib/DBIx/Class/ResultSourceHandle.pm new file mode 100644 index 0000000..60118b8 --- /dev/null +++ b/lib/DBIx/Class/ResultSourceHandle.pm @@ -0,0 +1,76 @@ +package DBIx::Class::ResultSourceHandle; + +use strict; +use warnings; +use Storable; + +use base qw/DBIx::Class/; + +use overload + q/""/ => sub { __PACKAGE__ . ":" . shift->source_moniker; }, + fallback => 1; + +__PACKAGE__->mk_group_accessors('simple' => qw/schema source_moniker/); + +=head1 NAME + +DBIx::Class::ResultSourceHandle + +=head1 DESCRIPTION + +This module removes fixed link between Rows/ResultSets and the actual source +objects, which gets round the following problems + +=over 4 + +=item * + +Needing to keep C<$schema> in scope, since any objects/result_sets +will have a C<$schema> object through their source handle + +=item * + +Large output when using Data::Dump(er) since this class can be set to +stringify to almost nothing + +=item * + +Closer to being aboe to do a Serialize::Storable that doesn't require class-based connections + +=back + +=head1 METHODS + +=head2 new + +=cut + +sub new { + my ($class, $data) = @_; + + $class = ref $class if ref $class; + + bless $data, $class; +} + +=head2 resolve + +Resolve the moniker into the actual ResultSource object + +=cut + +sub resolve { return $_[0]->schema->source($_[0]->source_moniker) } + +sub STORABLE_freeze { + my ($self, $cloning) = @_; + my $to_serialize = { %$self }; + delete $to_serialize->{schema}; + return (Storable::freeze($to_serialize)); +} + +sub STORABLE_thaw { + my ($self, $cloning,$ice) = @_; + %$self = %{ Storable::thaw($ice) }; +} + +1; diff --git a/lib/DBIx/Class/ResultSourceProxy.pm b/lib/DBIx/Class/ResultSourceProxy.pm index 1ae920e..696c9a5 100644 --- a/lib/DBIx/Class/ResultSourceProxy.pm +++ b/lib/DBIx/Class/ResultSourceProxy.pm @@ -5,11 +5,28 @@ use strict; use warnings; use base qw/DBIx::Class/; +use Scalar::Util qw/blessed/; +use Carp::Clan qw/^DBIx::Class/; sub iterator_class { shift->result_source_instance->resultset_class(@_) } sub resultset_class { shift->result_source_instance->resultset_class(@_) } sub result_class { shift->result_source_instance->result_class(@_) } -sub source_name { shift->result_source_instance->source_name(@_) } +sub source_info { shift->result_source_instance->source_info(@_) } + +sub set_inherited_ro_instance { + my $self = shift; + + croak "Cannot set @{[shift]} on an instance" if blessed $self; + + return $self->set_inherited(@_); +} + +sub get_inherited_ro_instance { + return shift->get_inherited(@_); +} + +__PACKAGE__->mk_group_accessors('inherited_ro_instance' => 'source_name'); + sub resultset_attributes { shift->result_source_instance->resultset_attributes(@_); diff --git a/lib/DBIx/Class/ResultSourceProxy/Table.pm b/lib/DBIx/Class/ResultSourceProxy/Table.pm index f70f2bc..ce78cb8 100644 --- a/lib/DBIx/Class/ResultSourceProxy/Table.pm +++ b/lib/DBIx/Class/ResultSourceProxy/Table.pm @@ -4,10 +4,10 @@ use strict; use warnings; use base qw/DBIx::Class::ResultSourceProxy/; -__PACKAGE__->load_components(qw/AccessorGroup/); -__PACKAGE__->mk_group_accessors('component_class' => 'table_class'); -__PACKAGE__->table_class('DBIx::Class::ResultSource::Table'); +use DBIx::Class::ResultSource::Table; + +__PACKAGE__->mk_classdata(table_class => 'DBIx::Class::ResultSource::Table'); __PACKAGE__->mk_classdata('table_alias'); # FIXME: Doesn't actually do # anything yet! @@ -53,7 +53,12 @@ sub table { source_name => undef, }); } - $class->mk_classdata('result_source_instance' => $table); + + $class->mk_classdata('result_source_instance') + unless $class->can('result_source_instance'); + + $class->result_source_instance($table); + if ($class->can('schema_instance')) { $class =~ m/([^:]+)$/; $class->schema_instance->register_class($class, $class); diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index e6e5ebc..a26d34c 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -6,9 +6,7 @@ use warnings; use base qw/DBIx::Class/; use Carp::Clan qw/^DBIx::Class/; -__PACKAGE__->load_components(qw/AccessorGroup/); - -__PACKAGE__->mk_group_accessors('simple' => 'result_source'); +__PACKAGE__->mk_group_accessors('simple' => qw/_source_handle/); =head1 NAME @@ -32,23 +30,46 @@ Creates a new row object from column => value mappings passed as a hash ref =cut sub new { - my ($class, $attrs) = @_; + my ($class, $attrs, $source) = @_; $class = ref $class if ref $class; my $new = { _column_data => {} }; bless $new, $class; + $new->_source_handle($source) if $source; + if ($attrs) { $new->throw_exception("attrs must be a hashref") unless ref($attrs) eq 'HASH'; + + my ($related,$inflated); + foreach my $key (keys %$attrs) { + if (ref $attrs->{$key}) { + my $info = $class->relationship_info($key); + if ($info && $info->{attrs}{accessor} + && $info->{attrs}{accessor} eq 'single') + { + $new->set_from_related($key, $attrs->{$key}); + $related->{$key} = $attrs->{$key}; + next; + } + elsif ($class->has_column($key) + && exists $class->column_info($key)->{_inflate_info}) + { + $inflated->{$key} = $attrs->{$key}; + next; + } + } + $new->throw_exception("No such column $key on $class") + unless $class->has_column($key); + $new->store_column($key => $attrs->{$key}); + } if (my $source = delete $attrs->{-result_source}) { $new->result_source($source); } - foreach my $k (keys %$attrs) { - $new->throw_exception("No such column $k on $class") - unless $class->has_column($k); - $new->store_column($k => $attrs->{$k}); - } + + $new->{_relationship_data} = $related if $related; + $new->{_inflated_column} = $inflated if $inflated; } return $new; @@ -69,13 +90,13 @@ L). sub insert { my ($self) = @_; return $self if $self->in_storage; - $self->{result_source} ||= $self->result_source_instance + my $source = $self->result_source; + $source ||= $self->result_source($self->result_source_instance) if $self->can('result_source_instance'); - my $source = $self->{result_source}; $self->throw_exception("No result_source set on this object; can't insert") unless $source; - #use Data::Dumper; warn Dumper($self); - $source->storage->insert($source->from, { $self->get_columns }); + + $source->storage->insert($source, { $self->get_columns }); $self->in_storage(1); $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; @@ -118,11 +139,33 @@ sub update { my $ident_cond = $self->ident_condition; $self->throw_exception("Cannot safely update a row in a PK-less table") if ! keys %$ident_cond; - $self->set_columns($upd) if $upd; + + if ($upd) { + foreach my $key (keys %$upd) { + if (ref $upd->{$key}) { + my $info = $self->relationship_info($key); + if ($info && $info->{attrs}{accessor} + && $info->{attrs}{accessor} eq 'single') + { + my $rel = delete $upd->{$key}; + $self->set_from_related($key => $rel); + $self->{_relationship_data}{$key} = $rel; + } + elsif ($self->has_column($key) + && exists $self->column_info($key)->{_inflate_info}) + { + $self->set_inflated_column($key, delete $upd->{$key}); + } + } + } + $self->set_columns($upd); + } my %to_update = $self->get_dirty_columns; return $self unless keys %to_update; my $rows = $self->result_source->storage->update( - $self->result_source->from, \%to_update, $self->{_orig_ident} || $ident_cond); + $self->result_source, \%to_update, + $self->{_orig_ident} || $ident_cond + ); if ($rows == 0) { $self->throw_exception( "Can't update ${self}: row not found" ); } elsif ($rows > 1) { @@ -139,8 +182,8 @@ sub update { $obj->delete Deletes the object from the database. The object is still perfectly -usable, but C<-Ein_storage()> will now return 0 and the object must -reinserted using C<-Einsert()> before C<-E(update()> can be used +usable, but C<< ->in_storage() >> will now return 0 and the object must +reinserted using C<< ->insert() >> before C<< ->update() >> can be used on it. If you delete an object in a class with a C relationship, all the related objects will be deleted as well. To turn this behavior off, pass C 0> in the C<$attr> @@ -161,7 +204,7 @@ sub delete { unless exists $self->{_column_data}{$column}; } $self->result_source->storage->delete( - $self->result_source->from, $ident_cond); + $self->result_source, $ident_cond); $self->in_storage(undef); } else { $self->throw_exception("Can't do class delete without a ResultSource instance") @@ -177,9 +220,10 @@ sub delete { my $val = $obj->get_column($col); -Gets a column value from a row object. Currently, does not do -any queries; the column must have already been fetched from -the database and stored in the object. +Gets a column value from a row object. Does not do any queries; the column +must have already been fetched from the database and stored in the object. If +there is an inflated value stored that has not yet been deflated, it is deflated +when the method is invoked. =cut @@ -187,6 +231,10 @@ sub get_column { my ($self, $column) = @_; $self->throw_exception( "Can't fetch data as class method" ) unless ref $self; return $self->{_column_data}{$column} if exists $self->{_column_data}{$column}; + if (exists $self->{_inflated_column}{$column}) { + return $self->store_column($column, + $self->_deflated_column($column, $self->{_inflated_column}{$column})); + } $self->throw_exception( "No such column '${column}'" ) unless $self->has_column($column); return undef; } @@ -205,6 +253,7 @@ database (or set locally). sub has_column_loaded { my ($self, $column) = @_; $self->throw_exception( "Can't call has_column data as class method" ) unless ref $self; + return 1 if exists $self->{_inflated_column}{$column}; return exists $self->{_column_data}{$column}; } @@ -218,6 +267,12 @@ Does C, for all column values at once. sub get_columns { my $self = shift; + if (exists $self->{_inflated_column}) { + foreach my $col (keys %{$self->{_inflated_column}}) { + $self->store_column($col, $self->_deflated_column($col, $self->{_inflated_column}{$col})) + unless exists $self->{_column_data}{$col}; + } + } return %{$self->{_column_data}}; } @@ -334,9 +389,17 @@ Called by ResultSet to inflate a result from storage sub inflate_result { my ($class, $source, $me, $prefetch) = @_; - #use Data::Dumper; print Dumper(@_); + + my ($source_handle) = $source; + + if ($source->isa('DBIx::Class::ResultSourceHandle')) { + $source = $source_handle->resolve + } else { + $source_handle = $source->handle + } + my $new = { - result_source => $source, + _source_handle => $source_handle, _column_data => $me, _in_storage => 1 }; @@ -436,6 +499,18 @@ sub is_column_changed { Accessor to the ResultSource this object was created from +=cut + +sub result_source { + my $self = shift; + + if (@_) { + $self->_source_handle($_[0]->handle); + } else { + $self->_source_handle->resolve; + } +} + =head2 register_column $column_info = { .... }; diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index 3ab14fd..58a59db 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -5,6 +5,8 @@ use warnings; use Carp::Clan qw/^DBIx::Class/; use Scalar::Util qw/weaken/; +use File::Spec; +require Module::Find; use base qw/DBIx::Class/; @@ -12,6 +14,7 @@ __PACKAGE__->mk_classdata('class_mappings' => {}); __PACKAGE__->mk_classdata('source_registrations' => {}); __PACKAGE__->mk_classdata('storage_type' => '::DBI'); __PACKAGE__->mk_classdata('storage'); +__PACKAGE__->mk_classdata('exception_action'); =head1 NAME @@ -91,10 +94,15 @@ moniker. sub register_source { my ($self, $moniker, $source) = @_; + + %$source = %{ $source->new( { %$source, source_name => $moniker }) }; + my %reg = %{$self->source_registrations}; $reg{$moniker} = $source; $self->source_registrations(\%reg); + $source->schema($self); + weaken($source->{schema}) if ref($self); if ($source->result_class) { my %map = %{$self->class_mappings}; @@ -103,6 +111,19 @@ sub register_source { } } +sub _unregister_source { + my ($self, $moniker) = @_; + my %reg = %{$self->source_registrations}; + + my $source = delete $reg{$moniker}; + $self->source_registrations(\%reg); + if ($source->result_class) { + my %map = %{$self->class_mappings}; + delete $map{$source->result_class}; + $self->class_mappings(\%map); + } +} + =head2 class =over 4 @@ -253,10 +274,6 @@ sub load_classes { } } } else { - eval "require Module::Find;"; - $class->throw_exception( - "No arguments to load_classes and couldn't load Module::Find ($@)" - ) if $@; my @comp = map { substr $_, length "${class}::" } Module::Find::findallmod($class); $comps_for{$class} = \@comp; @@ -277,9 +294,10 @@ sub load_classes { } } $class->ensure_class_loaded($comp_class); - $comp_class->source_name($comp) unless $comp_class->source_name; - push(@to_register, [ $comp_class->source_name, $comp_class ]); + $comp = $comp_class->source_name || $comp; +# $DB::single = 1; + push(@to_register, [ $comp, $comp_class ]); } } } @@ -291,7 +309,165 @@ sub load_classes { } } -=head2 compose_connection +=head2 load_namespaces + +=over 4 + +=item Arguments: %options? + +=back + +This is an alternative to L above which assumes an alternative +layout for automatic class loading. It assumes that all result +classes are underneath a sub-namespace of the schema called C, any +corresponding ResultSet classes are underneath a sub-namespace of the schema +called C. + +Both of the sub-namespaces are configurable if you don't like the defaults, +via the options C and C. + +If (and only if) you specify the option C, any found +Result classes for which we do not find a corresponding +ResultSet class will have their C set to +C. + +C takes care of calling C for you where +neccessary if you didn't do it for yourself. + +All of the namespace and classname options to this method are relative to +the schema classname by default. To specify a fully-qualified name, prefix +it with a literal C<+>. + +Examples: + + # load My::Schema::Result::CD, My::Schema::Result::Artist, + # My::Schema::ResultSet::CD, etc... + My::Schema->load_namespaces; + + # Override everything to use ugly names. + # In this example, if there is a My::Schema::Res::Foo, but no matching + # My::Schema::RSets::Foo, then Foo will have its + # resultset_class set to My::Schema::RSetBase + My::Schema->load_namespaces( + result_namespace => 'Res', + resultset_namespace => 'RSets', + default_resultset_class => 'RSetBase', + ); + + # Put things in other namespaces + My::Schema->load_namespaces( + result_namespace => '+Some::Place::Results', + resultset_namespace => '+Another::Place::RSets', + ); + +If you'd like to use multiple namespaces of each type, simply use an arrayref +of namespaces for that option. In the case that the same result +(or resultset) class exists in multiple namespaces, the latter entries in +your list of namespaces will override earlier ones. + + My::Schema->load_namespaces( + # My::Schema::Results_C::Foo takes precedence over My::Schema::Results_B::Foo : + result_namespace => [ 'Results_A', 'Results_B', 'Results_C' ], + resultset_namespace => [ '+Some::Place::RSets', 'RSets' ], + ); + +=cut + +# Pre-pends our classname to the given relative classname or +# class namespace, unless there is a '+' prefix, which will +# be stripped. +sub _expand_relative_name { + my ($class, $name) = @_; + return if !$name; + $name = $class . '::' . $name if ! ($name =~ s/^\+//); + return $name; +} + +# returns a hash of $shortname => $fullname for every package +# found in the given namespaces ($shortname is with the $fullname's +# namespace stripped off) +sub _map_namespaces { + my ($class, @namespaces) = @_; + + my @results_hash; + foreach my $namespace (@namespaces) { + push( + @results_hash, + map { (substr($_, length "${namespace}::"), $_) } + Module::Find::findallmod($namespace) + ); + } + + @results_hash; +} + +sub load_namespaces { + my ($class, %args) = @_; + + my $result_namespace = delete $args{result_namespace} || 'Result'; + my $resultset_namespace = delete $args{resultset_namespace} || 'ResultSet'; + my $default_resultset_class = delete $args{default_resultset_class}; + + $class->throw_exception('load_namespaces: unknown option(s): ' + . join(q{,}, map { qq{'$_'} } keys %args)) + if scalar keys %args; + + $default_resultset_class + = $class->_expand_relative_name($default_resultset_class); + + for my $arg ($result_namespace, $resultset_namespace) { + $arg = [ $arg ] if !ref($arg) && $arg; + + $class->throw_exception('load_namespaces: namespace arguments must be ' + . 'a simple string or an arrayref') + if ref($arg) ne 'ARRAY'; + + $_ = $class->_expand_relative_name($_) for (@$arg); + } + + my %results = $class->_map_namespaces(@$result_namespace); + my %resultsets = $class->_map_namespaces(@$resultset_namespace); + + my @to_register; + { + no warnings 'redefine'; + local *Class::C3::reinitialize = sub { }; + use warnings 'redefine'; + + foreach my $result (keys %results) { + my $result_class = $results{$result}; + $class->ensure_class_loaded($result_class); + $result_class->source_name($result) unless $result_class->source_name; + + my $rs_class = delete $resultsets{$result}; + my $rs_set = $result_class->resultset_class; + if($rs_set && $rs_set ne 'DBIx::Class::ResultSet') { + if($rs_class && $rs_class ne $rs_set) { + warn "We found ResultSet class '$rs_class' for '$result', but it seems " + . "that you had already set '$result' to use '$rs_set' instead"; + } + } + elsif($rs_class ||= $default_resultset_class) { + $class->ensure_class_loaded($rs_class); + $result_class->resultset_class($rs_class); + } + + push(@to_register, [ $result_class->source_name, $result_class ]); + } + } + + foreach (sort keys %resultsets) { + warn "load_namespaces found ResultSet class $_ with no " + . 'corresponding Result class'; + } + + Class::C3->reinitialize; + $class->register_class(@$_) for (@to_register); + + return; +} + +=head2 compose_connection (DEPRECATED) =over 4 @@ -301,6 +477,12 @@ sub load_classes { =back +DEPRECATED. You probably wanted compose_namespace. + +Actually, you probably just wanted to call connect. + +=for hidden due to deprecation + Calls L to the target namespace, calls L with @db_info on the new schema, then injects the L component and a @@ -315,43 +497,50 @@ more information. =cut -sub compose_connection { - my ($self, $target, @info) = @_; - my $base = 'DBIx::Class::ResultSetProxy'; - eval "require ${base};"; - $self->throw_exception - ("No arguments to load_classes and couldn't load ${base} ($@)") - if $@; - - if ($self eq $target) { - # Pathological case, largely caused by the docs on early C::M::DBIC::Plain - foreach my $moniker ($self->sources) { - my $source = $self->source($moniker); +{ + my $warn; + + sub compose_connection { + my ($self, $target, @info) = @_; + + warn "compose_connection deprecated as of 0.08000" unless $warn++; + + my $base = 'DBIx::Class::ResultSetProxy'; + eval "require ${base};"; + $self->throw_exception + ("No arguments to load_classes and couldn't load ${base} ($@)") + if $@; + + if ($self eq $target) { + # Pathological case, largely caused by the docs on early C::M::DBIC::Plain + foreach my $moniker ($self->sources) { + my $source = $self->source($moniker); + my $class = $source->result_class; + $self->inject_base($class, $base); + $class->mk_classdata(resultset_instance => $source->resultset); + $class->mk_classdata(class_resolver => $self); + } + $self->connection(@info); + return $self; + } + + my $schema = $self->compose_namespace($target, $base); + { + no strict 'refs'; + *{"${target}::schema"} = sub { $schema }; + } + + $schema->connection(@info); + foreach my $moniker ($schema->sources) { + my $source = $schema->source($moniker); my $class = $source->result_class; - $self->inject_base($class, $base); + #warn "$moniker $class $source ".$source->storage; + $class->mk_classdata(result_source_instance => $source); $class->mk_classdata(resultset_instance => $source->resultset); - $class->mk_classdata(class_resolver => $self); + $class->mk_classdata(class_resolver => $schema); } - $self->connection(@info); - return $self; + return $schema; } - - my $schema = $self->compose_namespace($target, $base); - { - no strict 'refs'; - *{"${target}::schema"} = sub { $schema }; - } - - $schema->connection(@info); - foreach my $moniker ($schema->sources) { - my $source = $schema->source($moniker); - my $class = $source->result_class; - #warn "$moniker $class $source ".$source->storage; - $class->mk_classdata(result_source_instance => $source); - $class->mk_classdata(resultset_instance => $source->resultset); - $class->mk_classdata(class_resolver => $schema); - } - return $schema; } =head2 compose_namespace @@ -470,8 +659,10 @@ C<::DBI::Sybase::MSSQL>. Instantiates a new Storage object of type L and passes the arguments to -$storage->connect_info. Sets the connection in-place on the schema. See -L for more information. +$storage->connect_info. Sets the connection in-place on the schema. + +See L for DBI-specific syntax, +or L in general. =cut @@ -485,9 +676,10 @@ sub connection { $self->throw_exception( "No arguments to load_classes and couldn't load ${storage_class} ($@)" ) if $@; - my $storage = $storage_class->new; + my $storage = $storage_class->new($self); $storage->connect_info(\@info); $self->storage($storage); + $self->on_connect() if($self->can('on_connect')); return $self; } @@ -509,138 +701,83 @@ information. sub connect { shift->clone->connection(@_) } -=head2 txn_begin - -Begins a transaction (does nothing if AutoCommit is off). Equivalent to -calling $schema->storage->txn_begin. See -L for more information. +=head2 txn_do -=cut +=over 4 -sub txn_begin { shift->storage->txn_begin } +=item Arguments: C<$coderef>, @coderef_args? -=head2 txn_commit +=item Return Value: The return value of $coderef -Commits the current transaction. Equivalent to calling -$schema->storage->txn_commit. See L -for more information. +=back -=cut +Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically, +returning its result (if any). Equivalent to calling $schema->storage->txn_do. +See L for more information. -sub txn_commit { shift->storage->txn_commit } +This interface is preferred over using the individual methods L, +L, and L below. -=head2 txn_rollback +=cut -Rolls back the current transaction. Equivalent to calling -$schema->storage->txn_rollback. See -L for more information. +sub txn_do { + my $self = shift; -=cut + $self->storage or $self->throw_exception + ('txn_do called on $schema without storage'); -sub txn_rollback { shift->storage->txn_rollback } + $self->storage->txn_do(@_); +} -=head2 txn_do +=head2 txn_begin -=over 4 +Begins a transaction (does nothing if AutoCommit is off). Equivalent to +calling $schema->storage->txn_begin. See +L for more information. -=item Arguments: C<$coderef>, @coderef_args? +=cut -=item Return Value: The return value of $coderef +sub txn_begin { + my $self = shift; -=back + $self->storage or $self->throw_exception + ('txn_begin called on $schema without storage'); -Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically, -returning its result (if any). If an exception is caught, a rollback is issued -and the exception is rethrown. If the rollback fails, (i.e. throws an -exception) an exception is thrown that includes a "Rollback failed" message. + $self->storage->txn_begin; +} -For example, +=head2 txn_commit - my $author_rs = $schema->resultset('Author')->find(1); - my @titles = qw/Night Day It/; +Commits the current transaction. Equivalent to calling +$schema->storage->txn_commit. See L +for more information. - my $coderef = sub { - # If any one of these fails, the entire transaction fails - $author_rs->create_related('books', { - title => $_ - }) foreach (@titles); +=cut - return $author->books; - }; +sub txn_commit { + my $self = shift; - my $rs; - eval { - $rs = $schema->txn_do($coderef); - }; + $self->storage or $self->throw_exception + ('txn_commit called on $schema without storage'); - if ($@) { # Transaction failed - die "something terrible has happened!" # - if ($@ =~ /Rollback failed/); # Rollback failed + $self->storage->txn_commit; +} - deal_with_failed_transaction(); - } +=head2 txn_rollback -In a nested transaction (calling txn_do() from within a txn_do() coderef) only -the outermost transaction will issue a L on -the Schema's storage, and txn_do() can be called in void, scalar and list -context and it will behave as expected. +Rolls back the current transaction. Equivalent to calling +$schema->storage->txn_rollback. See +L for more information. =cut -sub txn_do { - my ($self, $coderef, @args) = @_; +sub txn_rollback { + my $self = shift; $self->storage or $self->throw_exception - ('txn_do called on $schema without storage'); - ref $coderef eq 'CODE' or $self->throw_exception - ('$coderef must be a CODE reference'); - - my (@return_values, $return_value); - - $self->txn_begin; # If this throws an exception, no rollback is needed - - my $wantarray = wantarray; # Need to save this since the context - # inside the eval{} block is independent - # of the context that called txn_do() - eval { - - # Need to differentiate between scalar/list context to allow for - # returning a list in scalar context to get the size of the list - if ($wantarray) { - # list context - @return_values = $coderef->(@args); - } elsif (defined $wantarray) { - # scalar context - $return_value = $coderef->(@args); - } else { - # void context - $coderef->(@args); - } - $self->txn_commit; - }; + ('txn_rollback called on $schema without storage'); - if ($@) { - my $error = $@; - - eval { - $self->txn_rollback; - }; - - if ($@) { - my $rollback_error = $@; - my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION"; - $self->throw_exception($error) # propagate nested rollback - if $rollback_error =~ /$exception_class/; - - $self->throw_exception( - "Transaction aborted: $error. Rollback failed: ${rollback_error}" - ); - } else { - $self->throw_exception($error); # txn failed but rollback succeeded - } - } - - return $wantarray ? @return_values : $return_value; + $self->storage->txn_rollback; } =head2 clone @@ -666,6 +803,7 @@ sub clone { my $new = $source->new($source); $clone->register_source($moniker => $new); } + $clone->storage->set_schema($clone) if $clone->storage; return $clone; } @@ -681,7 +819,12 @@ Pass this method a resultsource name, and an arrayref of arrayrefs. The arrayrefs should contain a list of column names, followed by one or many sets of matching data for the given columns. -Each set of data is inserted into the database using +In void context, C in L is used +to insert the data, as this is a fast method. However, insert_bulk currently +assumes that your datasets all contain the same type of values, using scalar +references in a column in one row, and not in another will probably not work. + +Otherwise, each set of data is inserted into the database using L, and a arrayref of the resulting row objects is returned. @@ -700,15 +843,50 @@ sub populate { my ($self, $name, $data) = @_; my $rs = $self->resultset($name); my @names = @{shift(@$data)}; - my @created; - foreach my $item (@$data) { - my %create; - @create{@names} = @$item; - push(@created, $rs->create(\%create)); + if(defined wantarray) { + my @created; + foreach my $item (@$data) { + my %create; + @create{@names} = @$item; + push(@created, $rs->create(\%create)); + } + return @created; } - return @created; + $self->storage->insert_bulk($self->source($name), \@names, $data); } +=head2 exception_action + +=over 4 + +=item Arguments: $code_reference + +=back + +If C is set for this class/object, L +will prefer to call this code reference with the exception as an argument, +rather than its normal action. + +Your subroutine should probably just wrap the error in the exception +object/class of your choosing and rethrow. If, against all sage advice, +you'd like your C to suppress a particular exception +completely, simply have it return true. + +Example: + + package My::Schema; + use base qw/DBIx::Class::Schema/; + use My::ExceptionClass; + __PACKAGE__->exception_action(sub { My::ExceptionClass->throw(@_) }); + __PACKAGE__->load_classes; + + # or: + my $schema_obj = My::Schema->connect( .... ); + $schema_obj->exception_action(sub { My::ExceptionClass->throw(@_) }); + + # suppress all exceptions, like a moron: + $schema_obj->exception_action(sub { 1 }); + =head2 throw_exception =over 4 @@ -718,13 +896,14 @@ sub populate { =back Throws an exception. Defaults to using L to report errors from -user's perspective. +user's perspective. See L for details on overriding +this method's behavior. =cut sub throw_exception { - my ($self) = shift; - croak @_; + my $self = shift; + croak @_ if !$self->exception_action || !$self->exception_action->(@_); } =head2 deploy (EXPERIMENTAL) @@ -738,12 +917,17 @@ sub throw_exception { Attempts to deploy the schema to the current storage using L. Note that this feature is currently EXPERIMENTAL and may not work correctly -across all databases, or fully handle complex relationships. +across all databases, or fully handle complex relationships. Saying that, it +has been used successfully by many people, including the core dev team. See L for a list of values for C<$sqlt_args>. The most common value for this would be C<< { add_drop_table => 1, } >> to have the SQL produced include a DROP TABLE statement for each table created. +Additionally, the DBIx::Class parser accepts a C parameter as a hash +ref or an array ref, containing a list of source to deploy. If present, then +only the sources listed will get deployed. + =cut sub deploy { @@ -756,16 +940,41 @@ sub deploy { =over 4 -=item Arguments: \@databases, $version, $directory, $sqlt_args +=item Arguments: \@databases, $version, $directory, $preversion, $sqlt_args =back Creates an SQL file based on the Schema, for each of the specified -database types, in the given directory. +database types, in the given directory. Given a previous version number, +this will also create a file containing the ALTER TABLE statements to +transform the previous schema into the current one. Note that these +statements may contain DROP TABLE or DROP COLUMN statements that can +potentially destroy data. + +The file names are created using the C method below, please +override this method in your schema if you would like a different file +name format. For the ALTER file, the same format is used, replacing +$version in the name with "$preversion-$version". + +If no arguments are passed, then the following default values are used: + +=over 4 + +=item databases - ['MySQL', 'SQLite', 'PostgreSQL'] + +=item version - $schema->VERSION + +=item directory - './' + +=item preversion - + +=back Note that this feature is currently EXPERIMENTAL and may not work correctly across all databases, or fully handle complex relationships. +WARNING: Please check all SQL files created, before applying them. + =cut sub create_ddl_dir { @@ -777,19 +986,30 @@ sub create_ddl_dir { =head2 ddl_filename (EXPERIMENTAL) - my $filename = $table->ddl_filename($type, $dir, $version) +=over 4 + +=item Arguments: $directory, $database-type, $version, $preversion + +=back + + my $filename = $table->ddl_filename($type, $dir, $version, $preversion) + +This method is called by C to compose a file name out of +the supplied directory, database type and version number. The default file +name format is: C<$dir$schema-$version-$type.sql>. -Creates a filename for a SQL file based on the table class name. Not -intended for direct end user use. +You may override this method in your schema if you wish to use a different +format. =cut sub ddl_filename { - my ($self, $type, $dir, $version) = @_; + my ($self, $type, $dir, $version, $pversion) = @_; my $filename = ref($self); $filename =~ s/::/-/g; - $filename = "$dir$filename-$version-$type.sql"; + $filename = File::Spec->catfile($dir, "$filename-$version-$type.sql"); + $filename =~ s/$version/$pversion-$version/ if($pversion); return $filename; } diff --git a/lib/DBIx/Class/Schema/Versioned.pm b/lib/DBIx/Class/Schema/Versioned.pm new file mode 100644 index 0000000..91e78aa --- /dev/null +++ b/lib/DBIx/Class/Schema/Versioned.pm @@ -0,0 +1,303 @@ +package DBIx::Class::Version::Table; +use base 'DBIx::Class'; +use strict; +use warnings; + +__PACKAGE__->load_components(qw/ Core/); +__PACKAGE__->table('SchemaVersions'); + +__PACKAGE__->add_columns + ( 'Version' => { + 'data_type' => 'VARCHAR', + 'is_auto_increment' => 0, + 'default_value' => undef, + 'is_foreign_key' => 0, + 'name' => 'Version', + 'is_nullable' => 0, + 'size' => '10' + }, + 'Installed' => { + 'data_type' => 'VARCHAR', + 'is_auto_increment' => 0, + 'default_value' => undef, + 'is_foreign_key' => 0, + 'name' => 'Installed', + 'is_nullable' => 0, + 'size' => '20' + }, + ); +__PACKAGE__->set_primary_key('Version'); + +package DBIx::Class::Version; +use base 'DBIx::Class::Schema'; +use strict; +use warnings; + +__PACKAGE__->register_class('Table', 'DBIx::Class::Version::Table'); + + +# --------------------------------------------------------------------------- +package DBIx::Class::Schema::Versioned; + +use strict; +use warnings; +use base 'DBIx::Class'; +use POSIX 'strftime'; +use Data::Dumper; + +__PACKAGE__->mk_classdata('_filedata'); +__PACKAGE__->mk_classdata('upgrade_directory'); +__PACKAGE__->mk_classdata('backup_directory'); + +sub on_connect +{ + my ($self) = @_; + my $vschema = DBIx::Class::Version->connect(@{$self->storage->connect_info()}); + my $vtable = $vschema->resultset('Table'); + my $pversion; + + if(!$self->exists($vtable)) + { +# $vschema->storage->debug(1); + $vschema->storage->ensure_connected(); + $vschema->deploy(); + $pversion = 0; + } + else + { + my $psearch = $vtable->search(undef, + { select => [ + { 'max' => 'Installed' }, + ], + as => ['maxinstall'], + })->first; + $pversion = $vtable->search({ Installed => $psearch->get_column('maxinstall'), + })->first; + $pversion = $pversion->Version if($pversion); + } +# warn("Previous version: $pversion\n"); + if($pversion eq $self->VERSION) + { + warn "This version is already installed\n"; + return 1; + } + +## use IC::DT? + + if(!$pversion) + { + $vtable->create({ Version => $self->VERSION, + Installed => strftime("%Y-%m-%d %H:%M:%S", gmtime()) + }); + ## If we let the user do this, where does the Version table get updated? + warn "No previous version found, calling deploy to install this version.\n"; + $self->deploy(); + return 1; + } + + my $file = $self->ddl_filename( + $self->storage->sqlt_type, + $self->upgrade_directory, + $self->VERSION + ); + if(!$file) + { + # No upgrade path between these two versions + return 1; + } + + $file = $self->ddl_filename( + $self->storage->sqlt_type, + $self->upgrade_directory, + $self->VERSION, + $pversion, + ); +# $file =~ s/@{[ $self->VERSION ]}/"${pversion}-" . $self->VERSION/e; + if(!-f $file) + { + warn "Upgrade not possible, no upgrade file found ($file)\n"; + return; + } + + my $fh; + open $fh, "<$file" or warn("Can't open upgrade file, $file ($!)"); + my @data = split(/;\n/, join('', <$fh>)); + close($fh); + @data = grep { $_ && $_ !~ /^-- / } @data; + @data = grep { $_ !~ /^(BEGIN TRANACTION|COMMIT)/m } @data; + + $self->_filedata(\@data); + + ## Don't do this yet, do only on command? + ## If we do this later, where does the Version table get updated?? + warn "Versions out of sync. This is " . $self->VERSION . + ", your database contains version $pversion, please call upgrade on your Schema.\n"; +# $self->upgrade($pversion, $self->VERSION); +} + +sub exists +{ + my ($self, $rs) = @_; + + my $c = eval { + $rs->search({ 1, 0 })->count; + }; + return 0 if $@ || !defined $c; + + return 1; +} + +sub backup +{ + my ($self) = @_; + ## Make each ::DBI::Foo do this + $self->storage->backup($self->backup_directory()); +} + +sub upgrade +{ + my ($self) = @_; + + ## overridable sub, per default just run all the commands. + + $self->backup(); + + $self->run_upgrade(qr/create/i); + $self->run_upgrade(qr/alter table .*? add/i); + $self->run_upgrade(qr/alter table .*? (?!drop)/i); + $self->run_upgrade(qr/alter table .*? drop/i); + $self->run_upgrade(qr/drop/i); +# $self->run_upgrade(qr//i); + + my $vschema = DBIx::Class::Version->connect(@{$self->storage->connect_info()}); + my $vtable = $vschema->resultset('Table'); + $vtable->create({ Version => $self->VERSION, + Installed => strftime("%Y-%m-%d %H:%M:%S", gmtime()) + }); +} + + +sub run_upgrade +{ + my ($self, $stm) = @_; +# print "Reg: $stm\n"; + my @statements = grep { $_ =~ $stm } @{$self->_filedata}; +# print "Statements: ", join("\n", @statements), "\n"; + $self->_filedata([ grep { $_ !~ /$stm/i } @{$self->_filedata} ]); + + for (@statements) + { + $self->storage->debugfh->print("$_\n") if $self->storage->debug; +# print "Running \n>>$_<<\n"; + $self->storage->dbh->do($_) or warn "SQL was:\n $_"; + } + + return 1; +} + +1; + +=head1 NAME + +DBIx::Class::Versioning - DBIx::Class::Schema plugin for Schema upgrades + +=head1 SYNOPSIS + + package Library::Schema; + use base qw/DBIx::Class::Schema/; + # load Library::Schema::CD, Library::Schema::Book, Library::Schema::DVD + __PACKAGE__->load_classes(qw/CD Book DVD/); + + __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Versioned/); + __PACKAGE__->upgrade_directory('/path/to/upgrades/'); + __PACKAGE__->backup_directory('/path/to/backups/'); + + sub backup + { + my ($self) = @_; + # my special backup process + } + + sub upgrade + { + my ($self) = @_; + + ## overridable sub, per default just runs all the commands. + + $self->run_upgrade(qr/create/i); + $self->run_upgrade(qr/alter table .*? add/i); + $self->run_upgrade(qr/alter table .*? (?!drop)/i); + $self->run_upgrade(qr/alter table .*? drop/i); + $self->run_upgrade(qr/drop/i); + $self->run_upgrade(qr//i); + } + +=head1 DESCRIPTION + +This module is a component designed to extend L +classes, to enable them to upgrade to newer schema layouts. To use this +module, you need to have called C on your Schema to +create your upgrade files to include with your delivery. + +A table called I is created and maintained by the +module. This contains two fields, 'Version' and 'Installed', which +contain each VERSION of your Schema, and the date+time it was installed. + +If you would like to influence which levels of version change need +upgrades in your Schema, you can override the method C +in L. Return a false value if there is no upgrade +path between the two versions supplied. By default, every change in +your VERSION is regarded as needing an upgrade. + +The actual upgrade is called manually by calling C on your +schema object. Code is run at connect time to determine whether an +upgrade is needed, if so, a warning "Versions out of sync" is +produced. + +NB: At the moment, SQLite upgrading is rather spotty, as SQL::Translator::Diff +returns SQL statements that SQLite does not support. + + +=head1 METHODS + +=head2 backup + +This is an overwritable method which is called just before the upgrade, to +allow you to make a backup of the database. Per default this method attempts +to call C<< $self->storage->backup >>, to run the standard backup on each +database type. + +This method should return the name of the backup file, if appropriate. + +C is called from C, make sure you call it, if you write your +own method. + +=head2 upgrade + +This is an overwritable method used to run your upgrade. The freeform method +allows you to run your upgrade any way you please, you can call C +any number of times to run the actual SQL commands, and in between you can +sandwich your data upgrading. For example, first run all the B +commands, then migrate your data from old to new tables/formats, then +issue the DROP commands when you are finished. + +=head2 run_upgrade + + $self->run_upgrade(qr/create/i); + +Runs a set of SQL statements matching a passed in regular expression. The +idea is that this method can be called any number of times from your +C method, running whichever commands you specify via the +regex in the parameter. + +=head2 upgrade_directory + +Use this to set the directory your upgrade files are stored in. + +=head2 backup_directory + +Use this to set the directory you want your backups stored in. + +=head1 AUTHOR + +Jess Robinson diff --git a/lib/DBIx/Class/Serialize/Storable.pm b/lib/DBIx/Class/Serialize/Storable.pm deleted file mode 100644 index 7ccd2b0..0000000 --- a/lib/DBIx/Class/Serialize/Storable.pm +++ /dev/null @@ -1,68 +0,0 @@ -package DBIx::Class::Serialize::Storable; -use strict; -use warnings; -use Storable; - -sub STORABLE_freeze { - my ($self,$cloning) = @_; - my $to_serialize = { %$self }; - delete $to_serialize->{result_source}; - return (Storable::freeze($to_serialize)); -} - -sub STORABLE_thaw { - my ($self,$cloning,$serialized) = @_; - %$self = %{ Storable::thaw($serialized) }; - $self->result_source($self->result_source_instance) - if $self->can('result_source_instance'); -} - -1; - -__END__ - -=head1 NAME - - DBIx::Class::Serialize::Storable - hooks for Storable freeze/thaw - -=head1 SYNOPSIS - - # in a table class definition - __PACKAGE__->load_components(qw/Serialize::Storable/); - - # meanwhile, in a nearby piece of code - my $cd = $schema->resultset('CD')->find(12); - # if the cache uses Storable, this will work automatically - $cache->set($cd->ID, $cd); - -=head1 DESCRIPTION - -This component adds hooks for Storable so that row objects can be -serialized. It assumes that your row object class (C) is -the same as your table class, which is the normal situation. - -=head1 HOOKS - -The following hooks are defined for L - see the -documentation for L for detailed information on these -hooks. - -=head2 STORABLE_freeze - -The serializing hook, called on the object during serialization. It -can be inherited, or defined in the class itself, like any other -method. - -=head2 STORABLE_thaw - -The deserializing hook called on the object during deserialization. - -=head1 AUTHORS - -David Kamholz - -=head1 LICENSE - -You may distribute this code under the same terms as Perl itself. - -=cut diff --git a/lib/DBIx/Class/Storage.pm b/lib/DBIx/Class/Storage.pm index 735006c..9a58b94 100644 --- a/lib/DBIx/Class/Storage.pm +++ b/lib/DBIx/Class/Storage.pm @@ -1,42 +1,429 @@ -package # hide from PAUSE - DBIx::Class::Storage; +package DBIx::Class::Storage; use strict; use warnings; -sub new { die "Virtual method!" } -sub debug { die "Virtual method!" } -sub debugcb { die "Virtual method!" } -sub debugfh { die "Virtual method!" } -sub debugobj { die "Virtual method!" } -sub cursor { die "Virtual method!" } -sub disconnect { die "Virtual method!" } +use base qw/DBIx::Class/; + +use Scalar::Util qw/weaken/; +use Carp::Clan qw/^DBIx::Class/; + +__PACKAGE__->mk_group_accessors('simple' => qw/debug debugobj schema/); + +package # Hide from PAUSE + DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION; + +use overload '"' => sub { + 'DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION' +}; + +sub new { + my $class = shift; + my $self = {}; + return bless $self, $class; +} + +package DBIx::Class::Storage; + +=head1 NAME + +DBIx::Class::Storage - Generic Storage Handler + +=head1 DESCRIPTION + +A base implementation of common Storage methods. For specific +information about L-based storage, see L. + +=head1 METHODS + +=head2 new + +Arguments: $schema + +Instantiates the Storage object. + +=cut + +sub new { + my ($self, $schema) = @_; + + $self = ref $self if ref $self; + + my $new = {}; + bless $new, $self; + + $new->set_schema($schema); + $new->debugobj(new DBIx::Class::Storage::Statistics()); + + my $fh; + + my $debug_env = $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} + || $ENV{DBIC_TRACE}; + + if (defined($debug_env) && ($debug_env =~ /=(.+)$/)) { + $fh = IO::File->new($1, 'w') + or $new->throw_exception("Cannot open trace file $1"); + } else { + $fh = IO::File->new('>&STDERR'); + } + + $new->debugfh($fh); + $new->debug(1) if $debug_env; + + $new; +} + +=head2 set_schema + +Used to reset the schema class or object which owns this +storage object, such as during L. + +=cut + +sub set_schema { + my ($self, $schema) = @_; + $self->schema($schema); + weaken($self->{schema}) if ref $self->{schema}; +} + +=head2 connected + +Returns true if we have an open storage connection, false +if it is not (yet) open. + +=cut + sub connected { die "Virtual method!" } + +=head2 disconnect + +Closes any open storage connection unconditionally. + +=cut + +sub disconnect { die "Virtual method!" } + +=head2 ensure_connected + +Initiate a connection to the storage if one isn't already open. + +=cut + sub ensure_connected { die "Virtual method!" } -sub on_connect_do { die "Virtual method!" } -sub connect_info { die "Virtual method!" } -sub sql_maker { die "Virtual method!" } + +=head2 throw_exception + +Throws an exception - croaks. + +=cut + +sub throw_exception { + my $self = shift; + + $self->schema->throw_exception(@_) if $self->schema; + croak @_; +} + +=head2 txn_do + +=over 4 + +=item Arguments: C<$coderef>, @coderef_args? + +=item Return Value: The return value of $coderef + +=back + +Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically, +returning its result (if any). If an exception is caught, a rollback is issued +and the exception is rethrown. If the rollback fails, (i.e. throws an +exception) an exception is thrown that includes a "Rollback failed" message. + +For example, + + my $author_rs = $schema->resultset('Author')->find(1); + my @titles = qw/Night Day It/; + + my $coderef = sub { + # If any one of these fails, the entire transaction fails + $author_rs->create_related('books', { + title => $_ + }) foreach (@titles); + + return $author->books; + }; + + my $rs; + eval { + $rs = $schema->txn_do($coderef); + }; + + if ($@) { # Transaction failed + die "something terrible has happened!" # + if ($@ =~ /Rollback failed/); # Rollback failed + + deal_with_failed_transaction(); + } + +In a nested transaction (calling txn_do() from within a txn_do() coderef) only +the outermost transaction will issue a L, and txn_do() can be +called in void, scalar and list context and it will behave as expected. + +=cut + +sub txn_do { + my ($self, $coderef, @args) = @_; + + ref $coderef eq 'CODE' or $self->throw_exception + ('$coderef must be a CODE reference'); + + my (@return_values, $return_value); + + $self->txn_begin; # If this throws an exception, no rollback is needed + + my $wantarray = wantarray; # Need to save this since the context + # inside the eval{} block is independent + # of the context that called txn_do() + eval { + + # Need to differentiate between scalar/list context to allow for + # returning a list in scalar context to get the size of the list + if ($wantarray) { + # list context + @return_values = $coderef->(@args); + } elsif (defined $wantarray) { + # scalar context + $return_value = $coderef->(@args); + } else { + # void context + $coderef->(@args); + } + $self->txn_commit; + }; + + if ($@) { + my $error = $@; + + eval { + $self->txn_rollback; + }; + + if ($@) { + my $rollback_error = $@; + my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION"; + $self->throw_exception($error) # propagate nested rollback + if $rollback_error =~ /$exception_class/; + + $self->throw_exception( + "Transaction aborted: $error. Rollback failed: ${rollback_error}" + ); + } else { + $self->throw_exception($error); # txn failed but rollback succeeded + } + } + + return $wantarray ? @return_values : $return_value; +} + +=head2 txn_begin + +Starts a transaction. + +See the preferred L method, which allows for +an entire code block to be executed transactionally. + +=cut + sub txn_begin { die "Virtual method!" } + +=head2 txn_commit + +Issues a commit of the current transaction. + +=cut + sub txn_commit { die "Virtual method!" } + +=head2 txn_rollback + +Issues a rollback of the current transaction. A nested rollback will +throw a L exception, +which allows the rollback to propagate to the outermost transaction. + +=cut + sub txn_rollback { die "Virtual method!" } + +=head2 sql_maker + +Returns a C object - normally an object of class +C. + +=cut + +sub sql_maker { die "Virtual method!" } + +=head2 debug + +Causes trace information to be emitted on the C object. +(or C if C has not specifically been set). + +This is the equivalent to setting L in your +shell environment. + +=head2 debugfh + +Set or retrieve the filehandle used for trace/debug output. This should be +an IO::Handle compatible ojbect (only the C method is used. Initially +set to be STDERR - although see information on the +L environment variable. + +=cut + +sub debugfh { + my $self = shift; + + if ($self->debugobj->can('debugfh')) { + return $self->debugobj->debugfh(@_); + } +} + +=head2 debugobj + +Sets or retrieves the object used for metric collection. Defaults to an instance +of L that is compatible with the original +method of using a coderef as a callback. See the aforementioned Statistics +class for more information. + +=head2 debugcb + +Sets a callback to be executed each time a statement is run; takes a sub +reference. Callback is executed as $sub->($op, $info) where $op is +SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed. + +See L for a better way. + +=cut + +sub debugcb { + my $self = shift; + + if ($self->debugobj->can('callback')) { + return $self->debugobj->callback(@_); + } +} + +=head2 cursor + +The cursor class for this Storage object. + +=cut + +sub cursor { die "Virtual method!" } + +=head2 deploy + +Deploy the tables to storage (CREATE TABLE and friends in a SQL-based +Storage class). This would normally be called through +L. + +=cut + +sub deploy { die "Virtual method!" } + +=head2 connect_info + +The arguments of C are always a single array reference, +and are Storage-handler specific. + +This is normally accessed via L, which +encapsulates its argument list in an arrayref before calling +C here. + +=cut + +sub connect_info { die "Virtual method!" } + +=head2 select + +Handle a select statement. + +=cut + +sub select { die "Virtual method!" } + +=head2 insert + +Handle an insert statement. + +=cut + sub insert { die "Virtual method!" } + +=head2 update + +Handle an update statement. + +=cut + sub update { die "Virtual method!" } + +=head2 delete + +Handle a delete statement. + +=cut + sub delete { die "Virtual method!" } -sub select { die "Virtual method!" } + +=head2 select_single + +Performs a select, fetch and return of data - handles a single row +only. + +=cut + sub select_single { die "Virtual method!" } + +=head2 columns_info_for + +Returns metadata for the given source's columns. This +is *deprecated*, and will be removed before 1.0. You should +be specifying the metadata yourself if you need it. + +=cut + sub columns_info_for { die "Virtual method!" } +=head1 ENVIRONMENT VARIABLES -package DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION; +=head2 DBIC_TRACE -use overload '"' => sub { - 'DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION' -}; +If C is set then trace information +is produced (as when the L method is set). -sub new { - my $class = shift; - my $self = {}; - return bless $self, $class; -} +If the value is of the form C<1=/path/name> then the trace output is +written to the file C. + +This environment variable is checked when the storage object is first +created (when you call connect on your schema). So, run-time changes +to this environment variable will not take effect unless you also +re-connect on your schema. + +=head2 DBIX_CLASS_STORAGE_DBI_DEBUG + +Old name for DBIC_TRACE + +=head1 AUTHORS + +Matt S. Trout + +Andy Grundman + +=head1 LICENSE + +You may distribute this code under the same terms as Perl itself. + +=cut 1; diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index b5caa90..61fef77 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -3,14 +3,21 @@ package DBIx::Class::Storage::DBI; use base 'DBIx::Class::Storage'; -use strict; +use strict; use warnings; use DBI; use SQL::Abstract::Limit; use DBIx::Class::Storage::DBI::Cursor; use DBIx::Class::Storage::Statistics; use IO::File; -use Carp::Clan qw/DBIx::Class/; +use Scalar::Util 'blessed'; + +__PACKAGE__->mk_group_accessors( + 'simple' => + qw/_connect_info _dbh _sql_maker _sql_maker_opts _conn_pid _conn_tid + disable_sth_caching cursor on_connect_do transaction_depth/ +); + BEGIN { package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :( @@ -56,7 +63,6 @@ use Scalar::Util 'blessed'; sub _find_syntax { my ($self, $syntax) = @_; my $dbhname = blessed($syntax) ? $syntax->{Driver}{Name} : $syntax; -# print STDERR "Found DBH $syntax >$dbhname< ", $syntax->{Driver}->{Name}, "\n"; if(ref($self) && $dbhname && $dbhname eq 'DB2') { return 'RowNumberOver'; } @@ -287,14 +293,6 @@ sub name_sep { } # End of BEGIN block -use base qw/DBIx::Class/; - -__PACKAGE__->load_components(qw/AccessorGroup/); - -__PACKAGE__->mk_group_accessors('simple' => - qw/_connect_info _dbh _sql_maker _sql_maker_opts _conn_pid _conn_tid - debug debugobj cursor on_connect_do transaction_depth/); - =head1 NAME DBIx::Class::Storage::DBI - DBI storage handler @@ -303,49 +301,24 @@ DBIx::Class::Storage::DBI - DBI storage handler =head1 DESCRIPTION -This class represents the connection to the database +This class represents the connection to an RDBMS via L. See +L for general information. This pod only +documents DBI-specific methods and behaviors. =head1 METHODS -=head2 new - =cut sub new { - my $new = {}; - bless $new, (ref $_[0] || $_[0]); + my $new = shift->next::method(@_); $new->cursor("DBIx::Class::Storage::DBI::Cursor"); $new->transaction_depth(0); - - $new->debugobj(new DBIx::Class::Storage::Statistics()); - - my $fh; - - my $debug_env = $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} - || $ENV{DBIC_TRACE}; - - if (defined($debug_env) && ($debug_env =~ /=(.+)$/)) { - $fh = IO::File->new($1, 'w') - or $new->throw_exception("Cannot open trace file $1"); - } else { - $fh = IO::File->new('>&STDERR'); - } - $new->debugfh($fh); - $new->debug(1) if $debug_env; $new->_sql_maker_opts({}); - return $new; -} - -=head2 throw_exception + $new->{_in_dbh_do} = 0; + $new->{_dbh_gen} = 0; -Throws an exception - croaks. - -=cut - -sub throw_exception { - my ($self, $msg) = @_; - croak($msg); + $new; } =head2 connect_info @@ -372,6 +345,11 @@ This can be set to an arrayref of literal sql statements, which will be executed immediately after making the connection to the database every time we [re-]connect. +=item disable_sth_caching + +If set to a true value, this option will disable the caching of +statement handles via L. + =item limit_dialect Sets the limit dialect. This is useful for JDBC-bridge among others @@ -406,6 +384,12 @@ Every time C is invoked, any previous settings for these options will be cleared before setting the new ones, regardless of whether any options are specified in the new C. +Important note: DBIC expects the returned database handle provided by +a subref argument to have RaiseError set on it. If it doesn't, things +might not work very well, YMMV. If you don't use a subref, DBIC will +force this setting for you anyways. Setting HandleError to anything +other than simple exception object wrapper might cause problems too. + Examples: # Simple SQLite connection @@ -443,67 +427,178 @@ Examples: quote_char => q{`}, name_sep => q{@}, on_connect_do => ['SET search_path TO myschema,otherschema,public'], + disable_sth_caching => 1, }, ] ); +=cut + +sub connect_info { + my ($self, $info_arg) = @_; + + return $self->_connect_info if !$info_arg; + + # Kill sql_maker/_sql_maker_opts, so we get a fresh one with only + # the new set of options + $self->_sql_maker(undef); + $self->_sql_maker_opts({}); + + my $info = [ @$info_arg ]; # copy because we can alter it + my $last_info = $info->[-1]; + if(ref $last_info eq 'HASH') { + for my $storage_opt (qw/on_connect_do disable_sth_caching/) { + if(my $value = delete $last_info->{$storage_opt}) { + $self->$storage_opt($value); + } + } + for my $sql_maker_opt (qw/limit_dialect quote_char name_sep/) { + if(my $opt_val = delete $last_info->{$sql_maker_opt}) { + $self->_sql_maker_opts->{$sql_maker_opt} = $opt_val; + } + } + + # Get rid of any trailing empty hashref + pop(@$info) if !keys %$last_info; + } + + $self->_connect_info($info); +} + =head2 on_connect_do This method is deprecated in favor of setting via L. -=head2 debug +=head2 dbh_do -Causes SQL trace information to be emitted on the C object. -(or C if C has not specifically been set). +Arguments: $subref, @extra_coderef_args? -This is the equivalent to setting L in your -shell environment. +Execute the given subref using the new exception-based connection management. -=head2 debugfh +The first two arguments will be the storage object that C was called +on and a database handle to use. Any additional arguments will be passed +verbatim to the called subref as arguments 2 and onwards. -Set or retrieve the filehandle used for trace/debug output. This should be -an IO::Handle compatible ojbect (only the C method is used. Initially -set to be STDERR - although see information on the -L environment variable. +Using this (instead of $self->_dbh or $self->dbh) ensures correct +exception handling and reconnection (or failover in future subclasses). + +Your subref should have no side-effects outside of the database, as +there is the potential for your subref to be partially double-executed +if the database connection was stale/dysfunctional. + +Example: + + my @stuff = $schema->storage->dbh_do( + sub { + my ($storage, $dbh, @cols) = @_; + my $cols = join(q{, }, @cols); + $dbh->selectrow_array("SELECT $cols FROM foo"); + }, + @column_list + ); =cut -sub debugfh { - my $self = shift; +sub dbh_do { + my $self = shift; + my $coderef = shift; + + ref $coderef eq 'CODE' or $self->throw_exception + ('$coderef must be a CODE reference'); + + return $coderef->($self, $self->_dbh, @_) if $self->{_in_dbh_do}; + local $self->{_in_dbh_do} = 1; + + my @result; + my $want_array = wantarray; - if ($self->debugobj->can('debugfh')) { - return $self->debugobj->debugfh(@_); + eval { + $self->_verify_pid if $self->_dbh; + $self->_populate_dbh if !$self->_dbh; + if($want_array) { + @result = $coderef->($self, $self->_dbh, @_); } -} + elsif(defined $want_array) { + $result[0] = $coderef->($self, $self->_dbh, @_); + } + else { + $coderef->($self, $self->_dbh, @_); + } + }; -=head2 debugobj + my $exception = $@; + if(!$exception) { return $want_array ? @result : $result[0] } -Sets or retrieves the object used for metric collection. Defaults to an instance -of L that is campatible with the original -method of using a coderef as a callback. See the aforementioned Statistics -class for more information. + $self->throw_exception($exception) if $self->connected; -=head2 debugcb + # We were not connected - reconnect and retry, but let any + # exception fall right through this time + $self->_populate_dbh; + $coderef->($self, $self->_dbh, @_); +} -Sets a callback to be executed each time a statement is run; takes a sub -reference. Callback is executed as $sub->($op, $info) where $op is -SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed. +# This is basically a blend of dbh_do above and DBIx::Class::Storage::txn_do. +# It also informs dbh_do to bypass itself while under the direction of txn_do, +# via $self->{_in_dbh_do} (this saves some redundant eval and errorcheck, etc) +sub txn_do { + my $self = shift; + my $coderef = shift; -See L for a better way. + ref $coderef eq 'CODE' or $self->throw_exception + ('$coderef must be a CODE reference'); -=cut + local $self->{_in_dbh_do} = 1; -sub debugcb { - my $self = shift; + my @result; + my $want_array = wantarray; + + my $tried = 0; + while(1) { + eval { + $self->_verify_pid if $self->_dbh; + $self->_populate_dbh if !$self->_dbh; + + $self->txn_begin; + if($want_array) { + @result = $coderef->(@_); + } + elsif(defined $want_array) { + $result[0] = $coderef->(@_); + } + else { + $coderef->(@_); + } + $self->txn_commit; + }; - if ($self->debugobj->can('callback')) { - return $self->debugobj->callback(@_); + my $exception = $@; + if(!$exception) { return $want_array ? @result : $result[0] } + + if($tried++ > 0 || $self->connected) { + eval { $self->txn_rollback }; + my $rollback_exception = $@; + if($rollback_exception) { + my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION"; + $self->throw_exception($exception) # propagate nested rollback + if $rollback_exception =~ /$exception_class/; + + $self->throw_exception( + "Transaction aborted: ${exception}. " + . "Rollback failed: ${rollback_exception}" + ); + } + $self->throw_exception($exception) } + + # We were not connected, and was first try - reconnect and retry + # via the while loop + $self->_populate_dbh; + } } =head2 disconnect -Disconnect the L handle, performing a rollback first if the +Our C method also performs a rollback first if the database is not in C mode. =cut @@ -515,25 +610,21 @@ sub disconnect { $self->_dbh->rollback unless $self->_dbh->{AutoCommit}; $self->_dbh->disconnect; $self->_dbh(undef); + $self->{_dbh_gen}++; } } -=head2 connected - -Check if the L handle is connected. Returns true if the handle -is connected. - -=cut - -sub connected { my ($self) = @_; +sub connected { + my ($self) = @_; if(my $dbh = $self->_dbh) { if(defined $self->_conn_tid && $self->_conn_tid != threads->tid) { - return $self->_dbh(undef); + $self->_dbh(undef); + $self->{_dbh_gen}++; + return; } - elsif($self->_conn_pid != $$) { - $self->_dbh->{InactiveDestroy} = 1; - return $self->_dbh(undef); + else { + $self->_verify_pid; } return ($dbh->FETCH('Active') && $dbh->ping); } @@ -541,12 +632,19 @@ sub connected { my ($self) = @_; return 0; } -=head2 ensure_connected +# handle pid changes correctly +# NOTE: assumes $self->_dbh is a valid $dbh +sub _verify_pid { + my ($self) = @_; -Check whether the database handle is connected - if not then make a -connection. + return if $self->_conn_pid == $$; -=cut + $self->_dbh->{InactiveDestroy} = 1; + $self->_dbh(undef); + $self->{_dbh_gen}++; + + return; +} sub ensure_connected { my ($self) = @_; @@ -572,16 +670,9 @@ sub dbh { sub _sql_maker_args { my ($self) = @_; - return ( limit_dialect => $self->dbh, %{$self->_sql_maker_opts} ); + return ( bindtype=>'columns', limit_dialect => $self->dbh, %{$self->_sql_maker_opts} ); } -=head2 sql_maker - -Returns a C object - normally an object of class -C. - -=cut - sub sql_maker { my ($self) = @_; unless ($self->_sql_maker) { @@ -590,37 +681,6 @@ sub sql_maker { return $self->_sql_maker; } -sub connect_info { - my ($self, $info_arg) = @_; - - if($info_arg) { - # Kill sql_maker/_sql_maker_opts, so we get a fresh one with only - # the new set of options - $self->_sql_maker(undef); - $self->_sql_maker_opts({}); - - my $info = [ @$info_arg ]; # copy because we can alter it - my $last_info = $info->[-1]; - if(ref $last_info eq 'HASH') { - if(my $on_connect_do = delete $last_info->{on_connect_do}) { - $self->on_connect_do($on_connect_do); - } - for my $sql_maker_opt (qw/limit_dialect quote_char name_sep/) { - if(my $opt_val = delete $last_info->{$sql_maker_opt}) { - $self->_sql_maker_opts->{$sql_maker_opt} = $opt_val; - } - } - - # Get rid of any trailing empty hashref - pop(@$info) if !keys %$last_info; - } - - $self->_connect_info($info); - } - - $self->_connect_info; -} - sub _populate_dbh { my ($self) = @_; my @info = @{$self->_connect_info || []}; @@ -659,9 +719,15 @@ sub _connect { } eval { - $dbh = ref $info[0] eq 'CODE' - ? &{$info[0]} - : DBI->connect(@info); + if(ref $info[0] eq 'CODE') { + $dbh = &{$info[0]} + } + else { + $dbh = DBI->connect(@info); + $dbh->{RaiseError} = 1; + $dbh->{PrintError} = 0; + $dbh->{PrintWarn} = 0; + } }; $DBI::connect_via = $old_connect_via if $old_connect_via; @@ -673,36 +739,23 @@ sub _connect { $dbh; } -=head2 txn_begin - -Calls begin_work on the current dbh. - -See L for the txn_do() method, which allows for -an entire code block to be executed transactionally. - -=cut +sub _dbh_txn_begin { + my ($self, $dbh) = @_; + if ($dbh->{AutoCommit}) { + $self->debugobj->txn_begin() + if ($self->debug); + $dbh->begin_work; + } +} sub txn_begin { my $self = shift; - if ($self->{transaction_depth}++ == 0) { - my $dbh = $self->dbh; - if ($dbh->{AutoCommit}) { - $self->debugobj->txn_begin() - if ($self->debug); - $dbh->begin_work; - } - } + $self->dbh_do($self->can('_dbh_txn_begin')) + if $self->{transaction_depth}++ == 0; } -=head2 txn_commit - -Issues a commit against the current dbh. - -=cut - -sub txn_commit { - my $self = shift; - my $dbh = $self->dbh; +sub _dbh_txn_commit { + my ($self, $dbh) = @_; if ($self->{transaction_depth} == 0) { unless ($dbh->{AutoCommit}) { $self->debugobj->txn_commit() @@ -719,38 +772,36 @@ sub txn_commit { } } -=head2 txn_rollback - -Issues a rollback against the current dbh. A nested rollback will -throw a L exception, -which allows the rollback to propagate to the outermost transaction. - -=cut - -sub txn_rollback { +sub txn_commit { my $self = shift; + $self->dbh_do($self->can('_dbh_txn_commit')); +} - eval { - my $dbh = $self->dbh; - if ($self->{transaction_depth} == 0) { - unless ($dbh->{AutoCommit}) { - $self->debugobj->txn_rollback() - if ($self->debug); - $dbh->rollback; - } +sub _dbh_txn_rollback { + my ($self, $dbh) = @_; + if ($self->{transaction_depth} == 0) { + unless ($dbh->{AutoCommit}) { + $self->debugobj->txn_rollback() + if ($self->debug); + $dbh->rollback; + } + } + else { + if (--$self->{transaction_depth} == 0) { + $self->debugobj->txn_rollback() + if ($self->debug); + $dbh->rollback; } else { - if (--$self->{transaction_depth} == 0) { - $self->debugobj->txn_rollback() - if ($self->debug); - $dbh->rollback; - } - else { - die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new; - } + die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new; } - }; + } +} +sub txn_rollback { + my $self = shift; + + eval { $self->dbh_do($self->can('_dbh_txn_rollback')) }; if ($@) { my $error = $@; my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION"; @@ -760,12 +811,35 @@ sub txn_rollback { } } -sub _execute { +# This used to be the top-half of _execute. It was split out to make it +# easier to override in NoBindVars without duping the rest. It takes up +# all of _execute's args, and emits $sql, @bind. +sub _prep_for_execute { my ($self, $op, $extra_bind, $ident, @args) = @_; + my ($sql, @bind) = $self->sql_maker->$op($ident, @args); - unshift(@bind, @$extra_bind) if $extra_bind; + unshift(@bind, + map { ref $_ eq 'ARRAY' ? $_ : [ '!!dummy', $_ ] } @$extra_bind) + if $extra_bind; + @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args + + return ($sql, @bind); +} + +sub _execute { + my ($self, $op, $extra_bind, $ident, $bind_attributes, @args) = @_; + + if( blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) { + $ident = $ident->from(); + } + + my ($sql, @bind) = $self->sql_maker->$op($ident, @args); + unshift(@bind, + map { ref $_ eq 'ARRAY' ? $_ : [ '!!dummy', $_ ] } @$extra_bind) + if $extra_bind; if ($self->debug) { - my @debug_bind = map { defined $_ ? qq{'$_'} : q{'NULL'} } @bind; + my @debug_bind = + map { defined ($_ && $_->[1]) ? qq{'$_->[1]'} : q{'NULL'} } @bind; $self->debugobj->query_start($sql, @debug_bind); } my $sth = eval { $self->sth($sql,$op) }; @@ -775,12 +849,34 @@ sub _execute { 'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql" ); } - @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args + my $rv; if ($sth) { my $time = time(); - $rv = eval { $sth->execute(@bind) }; + $rv = eval { + my $placeholder_index = 1; + + foreach my $bound (@bind) { + my $attributes = {}; + my($column_name, @data) = @$bound; + + if( $bind_attributes ) { + $attributes = $bind_attributes->{$column_name} + if defined $bind_attributes->{$column_name}; + } + + foreach my $data (@data) + { + $data = ref $data ? ''.$data : $data; # stringify args + + $sth->bind_param($placeholder_index, $data, $attributes); + $placeholder_index++; + } + } + $sth->execute(); + }; + if ($@ || !$rv) { $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr)); } @@ -788,28 +884,119 @@ sub _execute { $self->throw_exception("'$sql' did not generate a statement."); } if ($self->debug) { - my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind; - $self->debugobj->query_end($sql, @debug_bind); + my @debug_bind = + map { defined ($_ && $_->[1]) ? qq{'$_->[1]'} : q{'NULL'} } @bind; + $self->debugobj->query_end($sql, @debug_bind); } return (wantarray ? ($rv, $sth, @bind) : $rv); } sub insert { - my ($self, $ident, $to_insert) = @_; + my ($self, $source, $to_insert) = @_; + + my $ident = $source->from; + my $bind_attributes = $self->source_bind_attributes($source); + $self->throw_exception( "Couldn't insert ".join(', ', map "$_ => $to_insert->{$_}", keys %$to_insert )." into ${ident}" - ) unless ($self->_execute('insert' => [], $ident, $to_insert)); + ) unless ($self->_execute('insert' => [], $source, $bind_attributes, $to_insert)); return $to_insert; } +## Still not quite perfect, and EXPERIMENTAL +## Currently it is assumed that all values passed will be "normal", i.e. not +## scalar refs, or at least, all the same type as the first set, the statement is +## only prepped once. +sub insert_bulk { + my ($self, $source, $cols, $data) = @_; + my %colvalues; + my $table = $source->from; + @colvalues{@$cols} = (0..$#$cols); + my ($sql, @bind) = $self->sql_maker->insert($table, \%colvalues); + + if ($self->debug) { + my @debug_bind = map { defined $_->[1] ? qq{$_->[1]} : q{'NULL'} } @bind; + $self->debugobj->query_start($sql, @debug_bind); + } + my $sth = $self->sth($sql); + +# @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args + + my $rv; + + ## This must be an arrayref, else nothing works! + + my $tuple_status = []; + + ##use Data::Dumper; + ##print STDERR Dumper( $data, $sql, [@bind] ); + + if ($sth) { + + my $time = time(); + + ## Get the bind_attributes, if any exist + my $bind_attributes = $self->source_bind_attributes($source); + + ## Bind the values and execute + $rv = eval { + + my $placeholder_index = 1; + + foreach my $bound (@bind) { + + my $attributes = {}; + my ($column_name, $data_index) = @$bound; + + if( $bind_attributes ) { + $attributes = $bind_attributes->{$column_name} + if defined $bind_attributes->{$column_name}; + } + + my @data = map { $_->[$data_index] } @$data; + + $sth->bind_param_array( $placeholder_index, [@data], $attributes ); + $placeholder_index++; + } + $sth->execute_array( {ArrayTupleStatus => $tuple_status} ); + + }; + + if ($@ || !defined $rv) { + my $errors = ''; + foreach my $tuple (@$tuple_status) { + $errors .= "\n" . $tuple->[1] if(ref $tuple); + } + $self->throw_exception("Error executing '$sql': ".($@ || $errors)); + } + } else { + $self->throw_exception("'$sql' did not generate a statement."); + } + if ($self->debug) { + my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind; + $self->debugobj->query_end($sql, @debug_bind); + } + return (wantarray ? ($rv, $sth, @bind) : $rv); +} + sub update { - return shift->_execute('update' => [], @_); + my $self = shift @_; + my $source = shift @_; + my $bind_attributes = $self->source_bind_attributes($source); + + return $self->_execute('update' => [], $source, $bind_attributes, @_); } + sub delete { - return shift->_execute('delete' => [], @_); + my $self = shift @_; + my $source = shift @_; + + my $bind_attrs = {}; ## If ever it's needed... + + return $self->_execute('delete' => [], $source, $bind_attrs, @_); } sub _select { @@ -825,7 +1012,8 @@ sub _select { ($order ? (order_by => $order) : ()) }; } - my @args = ('select', $attrs->{bind}, $ident, $select, $condition, $order); + my $bind_attrs = {}; ## Future support + my @args = ('select', $attrs->{bind}, $ident, $bind_attrs, $select, $condition, $order); if ($attrs->{software_limit} || $self->sql_maker->_default_limit_syntax eq "GenericSubQ") { $attrs->{software_limit} = 1; @@ -837,6 +1025,20 @@ sub _select { return $self->_execute(@args); } +sub source_bind_attributes { + my ($self, $source) = @_; + + my $bind_attributes; + foreach my $column ($source->columns) { + + my $data_type = $source->column_info($column)->{data_type} || ''; + $bind_attributes->{$column} = $self->bind_attribute_by_data_type($data_type) + if $data_type; + } + + return $bind_attributes; +} + =head2 select =over 4 @@ -855,19 +1057,11 @@ sub select { return $self->cursor->new($self, \@_, $attrs); } -=head2 select_single - -Performs a select, fetch and return of data - handles a single row -only. - -=cut - -# Need to call finish() to work round broken DBDs - sub select_single { my $self = shift; my ($rv, $sth, @bind) = $self->_select(@_); my @row = $sth->fetchrow_array; + # Need to call finish() to work round broken DBDs $sth->finish(); return @row; } @@ -884,32 +1078,35 @@ Returns a L sth (statement handle) for the supplied SQL. =cut -sub sth { - my ($self, $sql) = @_; - # 3 is the if_active parameter which avoids active sth re-use - return $self->dbh->prepare_cached($sql, {}, 3); -} +sub _dbh_sth { + my ($self, $dbh, $sql) = @_; -=head2 columns_info_for + # 3 is the if_active parameter which avoids active sth re-use + my $sth = $self->disable_sth_caching + ? $dbh->prepare($sql) + : $dbh->prepare_cached($sql, {}, 3); -Returns database type info for a given table column. + $self->throw_exception( + 'no sth generated via sql (' . ($@ || $dbh->errstr) . "): $sql" + ) if !$sth; -=cut + $sth; +} -sub columns_info_for { - my ($self, $table) = @_; +sub sth { + my ($self, $sql) = @_; + $self->dbh_do($self->can('_dbh_sth'), $sql); +} - my $dbh = $self->dbh; +sub _dbh_columns_info_for { + my ($self, $dbh, $table) = @_; if ($dbh->can('column_info')) { my %result; - local $dbh->{RaiseError} = 1; - local $dbh->{PrintError} = 0; eval { my ($schema,$tab) = $table =~ /^(.+?)\.(.+)$/ ? ($1,$2) : (undef,$table); my $sth = $dbh->column_info( undef,$schema, $tab, '%' ); $sth->execute(); - while ( my $info = $sth->fetchrow_hashref() ){ my %column_info; $column_info{data_type} = $info->{TYPE_NAME}; @@ -952,17 +1149,26 @@ sub columns_info_for { return \%result; } +sub columns_info_for { + my ($self, $table) = @_; + $self->dbh_do($self->can('_dbh_columns_info_for'), $table); +} + =head2 last_insert_id Return the row id of the last insert. =cut -sub last_insert_id { - my ($self, $row) = @_; - - return $self->dbh->func('last_insert_rowid'); +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; + # XXX This is a SQLite-ism as a default... is there a DBI-generic way? + $dbh->func('last_insert_rowid'); +} +sub last_insert_id { + my $self = shift; + $self->dbh_do($self->can('_dbh_last_insert_id'), @_); } =head2 sqlt_type @@ -973,11 +1179,25 @@ Returns the database driver name. sub sqlt_type { shift->dbh->{Driver}->{Name} } +=head2 bind_attribute_by_data_type + +Given a datatype from column info, returns a database specific bind attribute for +$dbh->bind_param($val,$attribute) or nothing if we will let the database planner +just handle it. + +Generally only needed for special case column types, like bytea in postgres. + +=cut + +sub bind_attribute_by_data_type { + return; +} + =head2 create_ddl_dir (EXPERIMENTAL) =over 4 -=item Arguments: $schema \@databases, $version, $directory, $sqlt_args +=item Arguments: $schema \@databases, $version, $directory, $preversion, $sqlt_args =back @@ -991,7 +1211,7 @@ across all databases, or fully handle complex relationships. sub create_ddl_dir { - my ($self, $schema, $databases, $version, $dir, $sqltargs) = @_; + my ($self, $schema, $databases, $version, $dir, $preversion, $sqltargs) = @_; if(!$dir || !-d $dir) { @@ -1004,14 +1224,18 @@ sub create_ddl_dir $sqltargs = { ( add_drop_table => 1 ), %{$sqltargs || {}} }; eval "use SQL::Translator"; - $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@; + $self->throw_exception("Can't create a ddl file without SQL::Translator: $@") if $@; - my $sqlt = SQL::Translator->new($sqltargs); + my $sqlt = SQL::Translator->new({ +# debug => 1, + add_drop_table => 1, + }); foreach my $db (@$databases) { $sqlt->reset(); $sqlt->parser('SQL::Translator::Parser::DBIx::Class'); # $sqlt->parser_args({'DBIx::Class' => $schema); + $sqlt = $self->configure_sqlt($sqlt, $db); $sqlt->data($schema); $sqlt->producer($db); @@ -1019,24 +1243,97 @@ sub create_ddl_dir my $filename = $schema->ddl_filename($db, $dir, $version); if(-e $filename) { - $self->throw_exception("$filename already exists, skipping $db"); + warn("$filename already exists, skipping $db"); next; } - open($file, ">$filename") - or $self->throw_exception("Can't open $filename for writing ($!)"); + my $output = $sqlt->translate; -#use Data::Dumper; -# print join(":", keys %{$schema->source_registrations}); -# print Dumper($sqlt->schema); if(!$output) { - $self->throw_exception("Failed to translate to $db. (" . $sqlt->error . ")"); + warn("Failed to translate to $db, skipping. (" . $sqlt->error . ")"); next; } + if(!open($file, ">$filename")) + { + $self->throw_exception("Can't open $filename for writing ($!)"); + next; + } print $file $output; close($file); + + if($preversion) + { + eval "use SQL::Translator::Diff"; + if($@) + { + warn("Can't diff versions without SQL::Translator::Diff: $@"); + next; + } + + my $prefilename = $schema->ddl_filename($db, $dir, $preversion); +# print "Previous version $prefilename\n"; + if(!-e $prefilename) + { + warn("No previous schema file found ($prefilename)"); + next; + } + #### We need to reparse the SQLite file we just wrote, so that + ## Diff doesnt get all confoosed, and Diff is *very* confused. + ## FIXME: rip Diff to pieces! +# my $target_schema = $sqlt->schema; +# unless ( $target_schema->name ) { +# $target_schema->name( $filename ); +# } + my @input; + push @input, {file => $prefilename, parser => $db}; + push @input, {file => $filename, parser => $db}; + my ( $source_schema, $source_db, $target_schema, $target_db ) = map { + my $file = $_->{'file'}; + my $parser = $_->{'parser'}; + + my $t = SQL::Translator->new; + $t->debug( 0 ); + $t->trace( 0 ); + $t->parser( $parser ) or die $t->error; + my $out = $t->translate( $file ) or die $t->error; + my $schema = $t->schema; + unless ( $schema->name ) { + $schema->name( $file ); + } + ($schema, $parser); + } @input; + + my $diff = SQL::Translator::Diff::schema_diff($source_schema, $db, + $target_schema, $db, + {} + ); + my $difffile = $schema->ddl_filename($db, $dir, $version, $preversion); + print STDERR "Diff: $difffile: $db, $dir, $version, $preversion \n"; + if(-e $difffile) + { + warn("$difffile already exists, skipping"); + next; + } + if(!open $file, ">$difffile") + { + $self->throw_exception("Can't write to $difffile ($!)"); + next; + } + print $file $diff; + close($file); + } } +} +sub configure_sqlt() { + my $self = shift; + my $tr = shift; + my $db = shift || $self->sqlt_type; + if ($db eq 'PostgreSQL') { + $tr->quote_table_names(0); + $tr->quote_field_names(0); + } + return $tr; } =head2 deployment_statements @@ -1069,6 +1366,17 @@ sub deployment_statements { $type ||= $self->sqlt_type; $version ||= $schema->VERSION || '1.x'; $dir ||= './'; + my $filename = $schema->ddl_filename($type, $dir, $version); + if(-f $filename) + { + my $file; + open($file, "<$filename") + or $self->throw_exception("Can't open $filename ($!)"); + my @rows = <$file>; + close($file); + return join('', @rows); + } + eval "use SQL::Translator"; if(!$@) { @@ -1076,36 +1384,22 @@ sub deployment_statements { $self->throw_exception($@) if $@; eval "use SQL::Translator::Producer::${type};"; $self->throw_exception($@) if $@; + + # sources needs to be a parser arg, but for simplicty allow at top level + # coming in + $sqltargs->{parser_args}{sources} = delete $sqltargs->{sources} + if exists $sqltargs->{sources}; + my $tr = SQL::Translator->new(%$sqltargs); SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema ); return "SQL::Translator::Producer::${type}"->can('produce')->($tr); } - my $filename = $schema->ddl_filename($type, $dir, $version); - if(!-f $filename) - { -# $schema->create_ddl_dir([ $type ], $version, $dir, $sqltargs); - $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy"); - return; - } - my $file; - open($file, "<$filename") - or $self->throw_exception("Can't open $filename ($!)"); - my @rows = <$file>; - close($file); + $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy"); + return; - return join('', @rows); - } -=head2 deploy - -Sends the appropriate statements to create or modify tables to the -db. This would normally be called through -L. - -=cut - sub deploy { my ($self, $schema, $type, $sqltargs, $dir) = @_; foreach my $statement ( $self->deployment_statements($schema, $type, undef, $dir, { no_comments => 1, %{ $sqltargs || {} } } ) ) { @@ -1117,7 +1411,7 @@ sub deploy { next if($_ =~ /^COMMIT/m); next if $_ =~ /^\s+$/; # skip whitespace only $self->debugobj->query_start($_) if $self->debug; - $self->dbh->do($_) or warn "SQL was:\n $_"; + $self->dbh->do($_) or warn "SQL was:\n $_"; # XXX exceptions? $self->debugobj->query_end($_) if $self->debug; } } @@ -1158,14 +1452,9 @@ sub build_datetime_parser { } sub DESTROY { - # NOTE: if there's a merge conflict here when -current is pushed - # back to trunk, take -current's version and ignore this trunk one :) my $self = shift; - - if($self->_dbh && $self->_conn_pid != $$) { - $self->_dbh->{InactiveDestroy} = 1; - } - + return if !$self->_dbh; + $self->_verify_pid; $self->_dbh(undef); } @@ -1206,25 +1495,6 @@ For setting, this method is deprecated in favor of L. =back -=head1 ENVIRONMENT VARIABLES - -=head2 DBIC_TRACE - -If C is set then SQL trace information -is produced (as when the L method is set). - -If the value is of the form C<1=/path/name> then the trace output is -written to the file C. - -This environment variable is checked when the storage object is first -created (when you call connect on your schema). So, run-time changes -to this environment variable will not take effect unless you also -re-connect on your schema. - -=head2 DBIX_CLASS_STORAGE_DBI_DEBUG - -Old name for DBIC_TRACE - =head1 AUTHORS Matt S. Trout diff --git a/lib/DBIx/Class/Storage/DBI/Cursor.pm b/lib/DBIx/Class/Storage/DBI/Cursor.pm index 770608c..c9dedf6 100644 --- a/lib/DBIx/Class/Storage/DBI/Cursor.pm +++ b/lib/DBIx/Class/Storage/DBI/Cursor.pm @@ -43,11 +43,9 @@ sub new { args => $args, pos => 0, attrs => $attrs, - pid => $$, + _dbh_gen => $storage->{_dbh_gen}, }; - $new->{tid} = threads->tid if $INC{'threads.pm'}; - return bless ($new, $class); } @@ -65,10 +63,10 @@ Advances the cursor to the next row and returns an arrayref of column values. =cut -sub next { - my ($self) = @_; +sub _dbh_next { + my ($storage, $dbh, $self) = @_; - $self->_check_forks_threads; + $self->_check_dbh_gen; if ($self->{attrs}{rows} && $self->{pos} >= $self->{attrs}{rows}) { $self->{sth}->finish if $self->{sth}->{Active}; delete $self->{sth}; @@ -76,7 +74,7 @@ sub next { } return if $self->{done}; unless ($self->{sth}) { - $self->{sth} = ($self->{storage}->_select(@{$self->{args}}))[1]; + $self->{sth} = ($storage->_select(@{$self->{args}}))[1]; if ($self->{attrs}{software_limit}) { if (my $offset = $self->{attrs}{offset}) { $self->{sth}->fetch for 1 .. $offset; @@ -93,6 +91,11 @@ sub next { return @row; } +sub next { + my ($self) = @_; + $self->{storage}->dbh_do($self->can('_dbh_next'), $self); +} + =head2 all =over 4 @@ -108,17 +111,22 @@ L. =cut -sub all { - my ($self) = @_; +sub _dbh_all { + my ($storage, $dbh, $self) = @_; - $self->_check_forks_threads; - return $self->SUPER::all if $self->{attrs}{rows}; + $self->_check_dbh_gen; $self->{sth}->finish if $self->{sth}->{Active}; delete $self->{sth}; - my ($rv, $sth) = $self->{storage}->_select(@{$self->{args}}); + my ($rv, $sth) = $storage->_select(@{$self->{args}}); return @{$sth->fetchall_arrayref}; } +sub all { + my ($self) = @_; + return $self->SUPER::all if $self->{attrs}{rows}; + $self->{storage}->dbh_do($self->can('_dbh_all'), $self); +} + =head2 reset Resets the cursor to the beginning of the L. @@ -128,8 +136,8 @@ Resets the cursor to the beginning of the L. sub reset { my ($self) = @_; - $self->_check_forks_threads; - $self->{sth}->finish if $self->{sth}->{Active}; + # No need to care about failures here + eval { $self->{sth}->finish if $self->{sth} && $self->{sth}->{Active} }; $self->_soft_reset; } @@ -137,30 +145,25 @@ sub _soft_reset { my ($self) = @_; delete $self->{sth}; - $self->{pos} = 0; delete $self->{done}; + $self->{pos} = 0; return $self; } -sub _check_forks_threads { +sub _check_dbh_gen { my ($self) = @_; - if($INC{'threads.pm'} && $self->{tid} != threads->tid) { - $self->_soft_reset; - $self->{tid} = threads->tid; - } - - if($self->{pid} != $$) { - $self->_soft_reset; - $self->{pid} = $$; + if($self->{_dbh_gen} != $self->{storage}->{_dbh_gen}) { + $self->{_dbh_gen} = $self->{storage}->{_dbh_gen}; + $self->_soft_reset; } } sub DESTROY { my ($self) = @_; - $self->_check_forks_threads; - $self->{sth}->finish if $self->{sth} && $self->{sth}->{Active}; + # None of the reasons this would die matter if we're in DESTROY anyways + eval { $self->{sth}->finish if $self->{sth} && $self->{sth}->{Active} }; } 1; diff --git a/lib/DBIx/Class/Storage/DBI/DB2.pm b/lib/DBIx/Class/Storage/DBI/DB2.pm index 8e867e0..4b5051b 100644 --- a/lib/DBIx/Class/Storage/DBI/DB2.pm +++ b/lib/DBIx/Class/Storage/DBI/DB2.pm @@ -7,18 +7,15 @@ use base qw/DBIx::Class::Storage::DBI/; # __PACKAGE__->load_components(qw/PK::Auto/); -sub last_insert_id -{ - my ($self) = @_; +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; - my $dbh = $self->_dbh; - my $sth = $dbh->prepare_cached("VALUES(IDENTITY_VAL_LOCAL())", {}, 3); + my $sth = $dbh->prepare_cached('VALUES(IDENTITY_VAL_LOCAL())', {}, 3); $sth->execute(); my @res = $sth->fetchrow_array(); return @res ? $res[0] : undef; - } sub datetime_parser_type { "DateTime::Format::DB2"; } diff --git a/lib/DBIx/Class/Storage/DBI/MSSQL.pm b/lib/DBIx/Class/Storage/DBI/MSSQL.pm index e355ce9..dcfe895 100644 --- a/lib/DBIx/Class/Storage/DBI/MSSQL.pm +++ b/lib/DBIx/Class/Storage/DBI/MSSQL.pm @@ -5,8 +5,9 @@ use warnings; use base qw/DBIx::Class::Storage::DBI/; -sub last_insert_id { - my( $id ) = $_[0]->_dbh->selectrow_array('SELECT @@IDENTITY' ); +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; + my ($id) = $dbh->selectrow_array('SELECT @@IDENTITY'); return $id; } diff --git a/lib/DBIx/Class/Storage/DBI/NoBindVars.pm b/lib/DBIx/Class/Storage/DBI/NoBindVars.pm index 2388762..2877ee2 100644 --- a/lib/DBIx/Class/Storage/DBI/NoBindVars.pm +++ b/lib/DBIx/Class/Storage/DBI/NoBindVars.pm @@ -17,58 +17,32 @@ well, as is the case with L =head1 METHODS -=head2 sth +=head2 connect_info -Uses C instead of the usual C, seeing as we can't cache very effectively without bind variables. +We can't cache very effectively without bind variables, so force the C setting to be turned on when the connect info is set. =cut -sub sth { - my ($self, $sql) = @_; - return $self->dbh->prepare($sql); +sub connect_info { + my $self = shift; + my $retval = shift->next::method(@_); + $self->disable_sth_caching(1); + $retval; } -=head2 _execute +=head2 _prep_for_execute -Manually subs in the values for the usual C placeholders before calling L on the generated SQL. +Manually subs in the values for the usual C placeholders. =cut -sub _execute { - my ($self, $op, $extra_bind, $ident, @args) = @_; - my ($sql, @bind) = $self->sql_maker->$op($ident, @args); - unshift(@bind, @$extra_bind) if $extra_bind; - if ($self->debug) { - my @debug_bind = map { defined $_ ? qq{'$_'} : q{'NULL'} } @bind; - $self->debugobj->query_start($sql, @debug_bind); - } +sub _prep_for_execute { + my $self = shift; + my ($sql, @bind) = $self->next::method(@_); $sql =~ s/\?/$self->_dbh->quote(shift(@bind))/eg; - my $sth = eval { $self->sth($sql,$op) }; - - if (!$sth || $@) { - $self->throw_exception( - 'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql" - ); - } - - my $rv; - if ($sth) { - my $time = time(); - $rv = eval { $sth->execute }; - - if ($@ || !$rv) { - $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr)); - } - } else { - $self->throw_exception("'$sql' did not generate a statement."); - } - if ($self->debug) { - my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind; - $self->debugobj->query_end($sql, @debug_bind); - } - return (wantarray ? ($rv, $sth, @bind) : $rv); + return ($sql); } =head1 AUTHORS diff --git a/lib/DBIx/Class/Storage/DBI/ODBC.pm b/lib/DBIx/Class/Storage/DBI/ODBC.pm index f33100c..42466ef 100644 --- a/lib/DBIx/Class/Storage/DBI/ODBC.pm +++ b/lib/DBIx/Class/Storage/DBI/ODBC.pm @@ -7,7 +7,7 @@ use base qw/DBIx::Class::Storage::DBI/; sub _rebless { my ($self) = @_; - my $dbh = $self->_dbh; + my $dbh = $self->dbh; my $dbtype = eval { $dbh->get_info(17) }; unless ( $@ ) { # Translate the backend name into a perl identifier diff --git a/lib/DBIx/Class/Storage/DBI/ODBC/DB2_400_SQL.pm b/lib/DBIx/Class/Storage/DBI/ODBC/DB2_400_SQL.pm index c39a622..1df4c21 100644 --- a/lib/DBIx/Class/Storage/DBI/ODBC/DB2_400_SQL.pm +++ b/lib/DBIx/Class/Storage/DBI/ODBC/DB2_400_SQL.pm @@ -4,11 +4,8 @@ use warnings; use base qw/DBIx::Class::Storage::DBI::ODBC/; -sub last_insert_id -{ - my ($self) = @_; - - my $dbh = $self->_dbh; +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; # get the schema/table separator: # '.' when SQL naming is active @@ -26,10 +23,14 @@ sub last_insert_id sub _sql_maker_opts { my ($self) = @_; - return { - limit_dialect => 'FetchFirst', - name_sep => $self->_dbh->get_info(41) - }; + $self->dbh_do(sub { + my ($self, $dbh) = @_; + + return { + limit_dialect => 'FetchFirst', + name_sep => $dbh->get_info(41) + }; + }); } 1; diff --git a/lib/DBIx/Class/Storage/DBI/Oracle.pm b/lib/DBIx/Class/Storage/DBI/Oracle.pm index 1cda2e3..77cedf3 100644 --- a/lib/DBIx/Class/Storage/DBI/Oracle.pm +++ b/lib/DBIx/Class/Storage/DBI/Oracle.pm @@ -3,33 +3,34 @@ package DBIx::Class::Storage::DBI::Oracle; use strict; use warnings; -use Carp qw/croak/; +use Carp::Clan qw/^DBIx::Class/; use base qw/DBIx::Class::Storage::DBI::MultiDistinctEmulation/; # __PACKAGE__->load_components(qw/PK::Auto/); -sub last_insert_id { - my ($self,$source,$col) = @_; +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; my $seq = ($source->column_info($col)->{sequence} ||= $self->get_autoinc_seq($source,$col)); - my $sql = "SELECT " . $seq . ".currval FROM DUAL"; - my ($id) = $self->_dbh->selectrow_array($sql); + my $sql = 'SELECT ' . $seq . '.currval FROM DUAL'; + my ($id) = $dbh->selectrow_array($sql); return $id; } -sub get_autoinc_seq { - my ($self,$source,$col) = @_; - +sub _dbh_get_autoinc_seq { + my ($self, $dbh, $source, $col) = @_; + # look up the correct sequence automatically - my $dbh = $self->_dbh; my $sql = q{ SELECT trigger_body FROM ALL_TRIGGERS t WHERE t.table_name = ? AND t.triggering_event = 'INSERT' AND t.status = 'ENABLED' }; + # trigger_body is a LONG $dbh->{LongReadLen} = 64 * 1024 if ($dbh->{LongReadLen} < 64 * 1024); + my $sth = $dbh->prepare($sql); $sth->execute( uc($source->name) ); while (my ($insert_trigger) = $sth->fetchrow_array) { @@ -38,6 +39,12 @@ sub get_autoinc_seq { croak "Unable to find a sequence INSERT trigger on table '" . $source->name . "'."; } +sub get_autoinc_seq { + my ($self, $source, $col) = @_; + + $self->dbh_do($self->can('_dbh_get_autoinc_seq'), $source, $col); +} + sub columns_info_for { my ($self, $table) = @_; diff --git a/lib/DBIx/Class/Storage/DBI/Pg.pm b/lib/DBIx/Class/Storage/DBI/Pg.pm index 5d7a0bf..ea5f6f0 100644 --- a/lib/DBIx/Class/Storage/DBI/Pg.pm +++ b/lib/DBIx/Class/Storage/DBI/Pg.pm @@ -3,7 +3,7 @@ package DBIx::Class::Storage::DBI::Pg; use strict; use warnings; -use DBD::Pg; +use DBD::Pg qw(:pg_types); use base qw/DBIx::Class::Storage::DBI/; @@ -13,31 +13,43 @@ use base qw/DBIx::Class::Storage::DBI/; warn "DBD::Pg 1.49 is strongly recommended" if ($DBD::Pg::VERSION < 1.49); +sub _dbh_last_insert_id { + my ($self, $dbh, $seq) = @_; + $dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq}); +} + sub last_insert_id { my ($self,$source,$col) = @_; my $seq = ($source->column_info($col)->{sequence} ||= $self->get_autoinc_seq($source,$col)); $self->throw_exception("could not fetch primary key for " . $source->name . ", could not " . "get autoinc sequence for $col (check that table and column specifications are correct " . "and in the correct case)") unless defined $seq; - $self->_dbh->last_insert_id(undef,undef,undef,undef, {sequence => $seq}); + $self->dbh_do($self->can('_dbh_last_insert_id'), $seq); +} + +sub _dbh_get_autoinc_seq { + my ($self, $dbh, $schema, $table, @pri) = @_; + + while (my $col = shift @pri) { + my $info = $dbh->column_info(undef,$schema,$table,$col)->fetchrow_hashref; + if(defined $info->{COLUMN_DEF} and + $info->{COLUMN_DEF} =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/) { + my $seq = $1; + # may need to strip quotes -- see if this works + return $seq =~ /\./ ? $seq : $info->{TABLE_SCHEM} . "." . $seq; + } + } + return; } sub get_autoinc_seq { my ($self,$source,$col) = @_; my @pri = $source->primary_columns; - my $dbh = $self->_dbh; my ($schema,$table) = $source->name =~ /^(.+)\.(.+)$/ ? ($1,$2) : (undef,$source->name); - while (my $col = shift @pri) { - my $info = $dbh->column_info(undef,$schema,$table,$col)->fetchrow_hashref; - if (defined $info->{COLUMN_DEF} and $info->{COLUMN_DEF} =~ - /^nextval\(+'([^']+)'::(?:text|regclass)\)/) - { - my $seq = $1; - return $seq =~ /\./ ? $seq : $info->{TABLE_SCHEM} . "." . $seq; # may need to strip quotes -- see if this works - } - } + + $self->dbh_do($self->can('_dbh_get_autoinc_seq'), $schema, $table, @pri); } sub sqlt_type { @@ -46,6 +58,21 @@ sub sqlt_type { sub datetime_parser_type { return "DateTime::Format::Pg"; } +sub bind_attribute_by_data_type { + my ($self,$data_type) = @_; + + my $bind_attributes = { + bytea => { pg_type => DBD::Pg::PG_BYTEA }, + }; + + if( defined $bind_attributes->{$data_type} ) { + return $bind_attributes->{$data_type}; + } + else { + return; + } +} + 1; =head1 NAME diff --git a/lib/DBIx/Class/Storage/DBI/SQLite.pm b/lib/DBIx/Class/Storage/DBI/SQLite.pm index 091b5e7..02a3c51 100644 --- a/lib/DBIx/Class/Storage/DBI/SQLite.pm +++ b/lib/DBIx/Class/Storage/DBI/SQLite.pm @@ -2,11 +2,47 @@ package DBIx::Class::Storage::DBI::SQLite; use strict; use warnings; +use POSIX 'strftime'; +use File::Copy; +use File::Spec; use base qw/DBIx::Class::Storage::DBI::MultiDistinctEmulation/; -sub last_insert_id { - return $_[0]->dbh->func('last_insert_rowid'); +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; + $dbh->func('last_insert_rowid'); +} + +sub backup +{ + my ($self, $dir) = @_; + $dir ||= './'; + + ## Where is the db file? + my $dsn = $self->connect_info()->[0]; + + my $dbname = $1 if($dsn =~ /dbname=([^;]+)/); + if(!$dbname) + { + $dbname = $1 if($dsn =~ /^dbi:SQLite:(.+)$/i); + } + $self->throw_exception("Cannot determine name of SQLite db file") + if(!$dbname || !-f $dbname); + +# print "Found database: $dbname\n"; +# my $dbfile = file($dbname); + my ($vol, $dbdir, $file) = File::Spec->splitpath($dbname); +# my $file = $dbfile->basename(); + $file = strftime("%y%m%d%h%M%s", localtime()) . $file; + $file = "B$file" while(-f $file); + + mkdir($dir) unless -f $dir; + my $backupfile = File::Spec->catfile($dir, $file); + + my $res = copy($dbname, $backupfile); + $self->throw_exception("Backup failed! ($!)") if(!$res); + + return $backupfile; } 1; diff --git a/lib/DBIx/Class/Storage/DBI/mysql.pm b/lib/DBIx/Class/Storage/DBI/mysql.pm index 8c14b1b..8ecdfca 100644 --- a/lib/DBIx/Class/Storage/DBI/mysql.pm +++ b/lib/DBIx/Class/Storage/DBI/mysql.pm @@ -7,8 +7,9 @@ use base qw/DBIx::Class::Storage::DBI/; # __PACKAGE__->load_components(qw/PK::Auto/); -sub last_insert_id { - return $_[0]->_dbh->{mysql_insertid}; +sub _dbh_last_insert_id { + my ($self, $dbh, $source, $col) = @_; + $dbh->{mysql_insertid}; } sub sqlt_type { diff --git a/lib/DBIx/Class/Storage/Statistics.pm b/lib/DBIx/Class/Storage/Statistics.pm index 3b9d7d6..5d0ba47 100644 --- a/lib/DBIx/Class/Storage/Statistics.pm +++ b/lib/DBIx/Class/Storage/Statistics.pm @@ -1,7 +1,9 @@ package DBIx::Class::Storage::Statistics; use strict; +use warnings; + +use base qw/Class::Accessor::Grouped/; -use base qw/DBIx::Class::AccessorGroup Class::Data::Accessor/; __PACKAGE__->mk_group_accessors(simple => qw/callback debugfh/); =head1 NAME diff --git a/lib/SQL/Translator/Parser/DBIx/Class.pm b/lib/SQL/Translator/Parser/DBIx/Class.pm index d8af4d6..edf6224 100644 --- a/lib/SQL/Translator/Parser/DBIx/Class.pm +++ b/lib/SQL/Translator/Parser/DBIx/Class.pm @@ -26,10 +26,11 @@ use base qw(Exporter); # We're working with DBIx::Class Schemas, not data streams. # ------------------------------------------------------------------- sub parse { - my ($tr, $data) = @_; - my $args = $tr->parser_args; - my $dbixschema = $args->{'DBIx::Schema'} || $data; - $dbixschema ||= $args->{'package'}; + my ($tr, $data) = @_; + my $args = $tr->parser_args; + my $dbixschema = $args->{'DBIx::Schema'} || $data; + $dbixschema ||= $args->{'package'}; + my $limit_sources = $args->{'sources'}; die 'No DBIx::Schema' unless ($dbixschema); if (!ref $dbixschema) { @@ -46,7 +47,23 @@ sub parse { my %seen_tables; - foreach my $moniker ($dbixschema->sources) + my @monikers = $dbixschema->sources; + if ($limit_sources) { + my $ref = ref $limit_sources || ''; + die "'sources' parameter must be an array or hash ref" unless $ref eq 'ARRAY' || ref eq 'HASH'; + + # limit monikers to those specified in + my $sources; + if ($ref eq 'ARRAY') { + $sources->{$_} = 1 for (@$limit_sources); + } else { + $sources = $limit_sources; + } + @monikers = grep { $sources->{$_} } @monikers; + } + + + foreach my $moniker (@monikers) { #eval "use $tableclass"; #print("Can't load $tableclass"), next if($@); @@ -91,6 +108,9 @@ sub parse { } my @rels = $source->relationships(); + + my %created_FK_rels; + foreach my $rel (@rels) { my $rel_info = $source->relationship_info($rel); @@ -120,12 +140,22 @@ sub parse { $on_update = $otherrelationship->{'attrs'}->{cascade_copy} ? 'CASCADE' : ''; } + # Make sure we dont create the same foreign key constraint twice + my $key_test = join("\x00", @keys); + #Decide if this is a foreign key based on whether the self #items are our primary columns. # If the sets are different, then we assume it's a foreign key from # us to another table. - if (!$source->compare_relationship_keys(\@keys, \@primary)) { + # OR: If is_foreign_key attr is explicity set on one the local columns + if ( ! exists $created_FK_rels{$rel_table}->{$key_test} + && + ( !$source->compare_relationship_keys(\@keys, \@primary) || + grep { $source->column_info($_)->{is_foreign_key} } @keys + ) + ) { + $created_FK_rels{$rel_table}->{$key_test} = 1; $table->add_constraint( type => 'foreign_key', name => "fk_$keys[0]", diff --git a/t/03podcoverage.t b/t/03podcoverage.t index aacbf18..2530be2 100644 --- a/t/03podcoverage.t +++ b/t/03podcoverage.t @@ -58,7 +58,7 @@ my $exceptions = { 'DBIx::Class::Relationship::ProxyMethods' => { skip => 1 }, 'DBIx::Class::ResultSetProxy' => { skip => 1 }, 'DBIx::Class::ResultSourceProxy' => { skip => 1 }, - 'DBIx::Class::Storage' => { skip => 1 }, + 'DBIx::Class::Storage::DBI' => { skip => 1 }, 'DBIx::Class::Storage::DBI::DB2' => { skip => 1 }, 'DBIx::Class::Storage::DBI::MSSQL' => { skip => 1 }, 'DBIx::Class::Storage::DBI::MultiDistinctEmulation' => { skip => 1 }, diff --git a/t/100populate.t b/t/100populate.t new file mode 100644 index 0000000..7b89395 --- /dev/null +++ b/t/100populate.t @@ -0,0 +1,10085 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; + +plan tests => 22; + +# perl -le'my $letter = 'a'; for my $i (4..10000) { $letter++; print "[ $i, \"$letter\" ]," }' > tests.txt + +my $schema = DBICTest->init_schema(); +$schema->populate('Artist', [ +[ qw/artistid name/ ], +[ 4, "b" ], +[ 5, "c" ], +[ 6, "d" ], +[ 7, "e" ], +[ 8, "f" ], +[ 9, "g" ], +[ 10, "h" ], +[ 11, "i" ], +[ 12, "j" ], +[ 13, "k" ], +[ 14, "l" ], +[ 15, "m" ], +[ 16, "n" ], +[ 17, "o" ], +[ 18, "p" ], +[ 19, "q" ], +[ 20, "r" ], +[ 21, "s" ], +[ 22, "t" ], +[ 23, "u" ], +[ 24, "v" ], +[ 25, "w" ], +[ 26, "x" ], +[ 27, "y" ], +[ 28, "z" ], +[ 29, "aa" ], +[ 30, "ab" ], +[ 31, "ac" ], +[ 32, "ad" ], +[ 33, "ae" ], +[ 34, "af" ], +[ 35, "ag" ], +[ 36, "ah" ], +[ 37, "ai" ], +[ 38, "aj" ], +[ 39, "ak" ], +[ 40, "al" ], +[ 41, "am" ], +[ 42, "an" ], +[ 43, "ao" ], +[ 44, "ap" ], +[ 45, "aq" ], +[ 46, "ar" ], +[ 47, "as" ], +[ 48, "at" ], +[ 49, "au" ], +[ 50, "av" ], +[ 51, "aw" ], +[ 52, "ax" ], +[ 53, "ay" ], +[ 54, "az" ], +[ 55, "ba" ], +[ 56, "bb" ], +[ 57, "bc" ], +[ 58, "bd" ], +[ 59, "be" ], +[ 60, "bf" ], +[ 61, "bg" ], +[ 62, "bh" ], +[ 63, "bi" ], +[ 64, "bj" ], +[ 65, "bk" ], +[ 66, "bl" ], +[ 67, "bm" ], +[ 68, "bn" ], +[ 69, "bo" ], +[ 70, "bp" ], +[ 71, "bq" ], +[ 72, "br" ], +[ 73, "bs" ], +[ 74, "bt" ], +[ 75, "bu" ], +[ 76, "bv" ], +[ 77, "bw" ], +[ 78, "bx" ], +[ 79, "by" ], +[ 80, "bz" ], +[ 81, "ca" ], +[ 82, "cb" ], +[ 83, "cc" ], +[ 84, "cd" ], +[ 85, "ce" ], +[ 86, "cf" ], +[ 87, "cg" ], +[ 88, "ch" ], +[ 89, "ci" ], +[ 90, "cj" ], +[ 91, "ck" ], +[ 92, "cl" ], +[ 93, "cm" ], +[ 94, "cn" ], +[ 95, "co" ], +[ 96, "cp" ], +[ 97, "cq" ], +[ 98, "cr" ], +[ 99, "cs" ], +[ 100, "ct" ], +[ 101, "cu" ], +[ 102, "cv" ], +[ 103, "cw" ], +[ 104, "cx" ], +[ 105, "cy" ], +[ 106, "cz" ], +[ 107, "da" ], +[ 108, "db" ], +[ 109, "dc" ], +[ 110, "dd" ], +[ 111, "de" ], +[ 112, "df" ], +[ 113, "dg" ], +[ 114, "dh" ], +[ 115, "di" ], +[ 116, "dj" ], +[ 117, "dk" ], +[ 118, "dl" ], +[ 119, "dm" ], +[ 120, "dn" ], +[ 121, "do" ], +[ 122, "dp" ], +[ 123, "dq" ], +[ 124, "dr" ], +[ 125, "ds" ], +[ 126, "dt" ], +[ 127, "du" ], +[ 128, "dv" ], +[ 129, "dw" ], +[ 130, "dx" ], +[ 131, "dy" ], +[ 132, "dz" ], +[ 133, "ea" ], +[ 134, "eb" ], +[ 135, "ec" ], +[ 136, "ed" ], +[ 137, "ee" ], +[ 138, "ef" ], +[ 139, "eg" ], +[ 140, "eh" ], +[ 141, "ei" ], +[ 142, "ej" ], +[ 143, "ek" ], +[ 144, "el" ], +[ 145, "em" ], +[ 146, "en" ], +[ 147, "eo" ], +[ 148, "ep" ], +[ 149, "eq" ], +[ 150, "er" ], +[ 151, "es" ], +[ 152, "et" ], +[ 153, "eu" ], +[ 154, "ev" ], +[ 155, "ew" ], +[ 156, "ex" ], +[ 157, "ey" ], +[ 158, "ez" ], +[ 159, "fa" ], +[ 160, "fb" ], +[ 161, "fc" ], +[ 162, "fd" ], +[ 163, "fe" ], +[ 164, "ff" ], +[ 165, "fg" ], +[ 166, "fh" ], +[ 167, "fi" ], +[ 168, "fj" ], +[ 169, "fk" ], +[ 170, "fl" ], +[ 171, "fm" ], +[ 172, "fn" ], +[ 173, "fo" ], +[ 174, "fp" ], +[ 175, "fq" ], +[ 176, "fr" ], +[ 177, "fs" ], +[ 178, "ft" ], +[ 179, "fu" ], +[ 180, "fv" ], +[ 181, "fw" ], +[ 182, "fx" ], +[ 183, "fy" ], +[ 184, "fz" ], +[ 185, "ga" ], +[ 186, "gb" ], +[ 187, "gc" ], +[ 188, "gd" ], +[ 189, "ge" ], +[ 190, "gf" ], +[ 191, "gg" ], +[ 192, "gh" ], +[ 193, "gi" ], +[ 194, "gj" ], +[ 195, "gk" ], +[ 196, "gl" ], +[ 197, "gm" ], +[ 198, "gn" ], +[ 199, "go" ], +[ 200, "gp" ], +[ 201, "gq" ], +[ 202, "gr" ], +[ 203, "gs" ], +[ 204, "gt" ], +[ 205, "gu" ], +[ 206, "gv" ], +[ 207, "gw" ], +[ 208, "gx" ], +[ 209, "gy" ], +[ 210, "gz" ], +[ 211, "ha" ], +[ 212, "hb" ], +[ 213, "hc" ], +[ 214, "hd" ], +[ 215, "he" ], +[ 216, "hf" ], +[ 217, "hg" ], +[ 218, "hh" ], +[ 219, "hi" ], +[ 220, "hj" ], +[ 221, "hk" ], +[ 222, "hl" ], +[ 223, "hm" ], +[ 224, "hn" ], +[ 225, "ho" ], +[ 226, "hp" ], +[ 227, "hq" ], +[ 228, "hr" ], +[ 229, "hs" ], +[ 230, "ht" ], +[ 231, "hu" ], +[ 232, "hv" ], +[ 233, "hw" ], +[ 234, "hx" ], +[ 235, "hy" ], +[ 236, "hz" ], +[ 237, "ia" ], +[ 238, "ib" ], +[ 239, "ic" ], +[ 240, "id" ], +[ 241, "ie" ], +[ 242, "if" ], +[ 243, "ig" ], +[ 244, "ih" ], +[ 245, "ii" ], +[ 246, "ij" ], +[ 247, "ik" ], +[ 248, "il" ], +[ 249, "im" ], +[ 250, "in" ], +[ 251, "io" ], +[ 252, "ip" ], +[ 253, "iq" ], +[ 254, "ir" ], +[ 255, "is" ], +[ 256, "it" ], +[ 257, "iu" ], +[ 258, "iv" ], +[ 259, "iw" ], +[ 260, "ix" ], +[ 261, "iy" ], +[ 262, "iz" ], +[ 263, "ja" ], +[ 264, "jb" ], +[ 265, "jc" ], +[ 266, "jd" ], +[ 267, "je" ], +[ 268, "jf" ], +[ 269, "jg" ], +[ 270, "jh" ], +[ 271, "ji" ], +[ 272, "jj" ], +[ 273, "jk" ], +[ 274, "jl" ], +[ 275, "jm" ], +[ 276, "jn" ], +[ 277, "jo" ], +[ 278, "jp" ], +[ 279, "jq" ], +[ 280, "jr" ], +[ 281, "js" ], +[ 282, "jt" ], +[ 283, "ju" ], +[ 284, "jv" ], +[ 285, "jw" ], +[ 286, "jx" ], +[ 287, "jy" ], +[ 288, "jz" ], +[ 289, "ka" ], +[ 290, "kb" ], +[ 291, "kc" ], +[ 292, "kd" ], +[ 293, "ke" ], +[ 294, "kf" ], +[ 295, "kg" ], +[ 296, "kh" ], +[ 297, "ki" ], +[ 298, "kj" ], +[ 299, "kk" ], +[ 300, "kl" ], +[ 301, "km" ], +[ 302, "kn" ], +[ 303, "ko" ], +[ 304, "kp" ], +[ 305, "kq" ], +[ 306, "kr" ], +[ 307, "ks" ], +[ 308, "kt" ], +[ 309, "ku" ], +[ 310, "kv" ], +[ 311, "kw" ], +[ 312, "kx" ], +[ 313, "ky" ], +[ 314, "kz" ], +[ 315, "la" ], +[ 316, "lb" ], +[ 317, "lc" ], +[ 318, "ld" ], +[ 319, "le" ], +[ 320, "lf" ], +[ 321, "lg" ], +[ 322, "lh" ], +[ 323, "li" ], +[ 324, "lj" ], +[ 325, "lk" ], +[ 326, "ll" ], +[ 327, "lm" ], +[ 328, "ln" ], +[ 329, "lo" ], +[ 330, "lp" ], +[ 331, "lq" ], +[ 332, "lr" ], +[ 333, "ls" ], +[ 334, "lt" ], +[ 335, "lu" ], +[ 336, "lv" ], +[ 337, "lw" ], +[ 338, "lx" ], +[ 339, "ly" ], +[ 340, "lz" ], +[ 341, "ma" ], +[ 342, "mb" ], +[ 343, "mc" ], +[ 344, "md" ], +[ 345, "me" ], +[ 346, "mf" ], +[ 347, "mg" ], +[ 348, "mh" ], +[ 349, "mi" ], +[ 350, "mj" ], +[ 351, "mk" ], +[ 352, "ml" ], +[ 353, "mm" ], +[ 354, "mn" ], +[ 355, "mo" ], +[ 356, "mp" ], +[ 357, "mq" ], +[ 358, "mr" ], +[ 359, "ms" ], +[ 360, "mt" ], +[ 361, "mu" ], +[ 362, "mv" ], +[ 363, "mw" ], +[ 364, "mx" ], +[ 365, "my" ], +[ 366, "mz" ], +[ 367, "na" ], +[ 368, "nb" ], +[ 369, "nc" ], +[ 370, "nd" ], +[ 371, "ne" ], +[ 372, "nf" ], +[ 373, "ng" ], +[ 374, "nh" ], +[ 375, "ni" ], +[ 376, "nj" ], +[ 377, "nk" ], +[ 378, "nl" ], +[ 379, "nm" ], +[ 380, "nn" ], +[ 381, "no" ], +[ 382, "np" ], +[ 383, "nq" ], +[ 384, "nr" ], +[ 385, "ns" ], +[ 386, "nt" ], +[ 387, "nu" ], +[ 388, "nv" ], +[ 389, "nw" ], +[ 390, "nx" ], +[ 391, "ny" ], +[ 392, "nz" ], +[ 393, "oa" ], +[ 394, "ob" ], +[ 395, "oc" ], +[ 396, "od" ], +[ 397, "oe" ], +[ 398, "of" ], +[ 399, "og" ], +[ 400, "oh" ], +[ 401, "oi" ], +[ 402, "oj" ], +[ 403, "ok" ], +[ 404, "ol" ], +[ 405, "om" ], +[ 406, "on" ], +[ 407, "oo" ], +[ 408, "op" ], +[ 409, "oq" ], +[ 410, "or" ], +[ 411, "os" ], +[ 412, "ot" ], +[ 413, "ou" ], +[ 414, "ov" ], +[ 415, "ow" ], +[ 416, "ox" ], +[ 417, "oy" ], +[ 418, "oz" ], +[ 419, "pa" ], +[ 420, "pb" ], +[ 421, "pc" ], +[ 422, "pd" ], +[ 423, "pe" ], +[ 424, "pf" ], +[ 425, "pg" ], +[ 426, "ph" ], +[ 427, "pi" ], +[ 428, "pj" ], +[ 429, "pk" ], +[ 430, "pl" ], +[ 431, "pm" ], +[ 432, "pn" ], +[ 433, "po" ], +[ 434, "pp" ], +[ 435, "pq" ], +[ 436, "pr" ], +[ 437, "ps" ], +[ 438, "pt" ], +[ 439, "pu" ], +[ 440, "pv" ], +[ 441, "pw" ], +[ 442, "px" ], +[ 443, "py" ], +[ 444, "pz" ], +[ 445, "qa" ], +[ 446, "qb" ], +[ 447, "qc" ], +[ 448, "qd" ], +[ 449, "qe" ], +[ 450, "qf" ], +[ 451, "qg" ], +[ 452, "qh" ], +[ 453, "qi" ], +[ 454, "qj" ], +[ 455, "qk" ], +[ 456, "ql" ], +[ 457, "qm" ], +[ 458, "qn" ], +[ 459, "qo" ], +[ 460, "qp" ], +[ 461, "qq" ], +[ 462, "qr" ], +[ 463, "qs" ], +[ 464, "qt" ], +[ 465, "qu" ], +[ 466, "qv" ], +[ 467, "qw" ], +[ 468, "qx" ], +[ 469, "qy" ], +[ 470, "qz" ], +[ 471, "ra" ], +[ 472, "rb" ], +[ 473, "rc" ], +[ 474, "rd" ], +[ 475, "re" ], +[ 476, "rf" ], +[ 477, "rg" ], +[ 478, "rh" ], +[ 479, "ri" ], +[ 480, "rj" ], +[ 481, "rk" ], +[ 482, "rl" ], +[ 483, "rm" ], +[ 484, "rn" ], +[ 485, "ro" ], +[ 486, "rp" ], +[ 487, "rq" ], +[ 488, "rr" ], +[ 489, "rs" ], +[ 490, "rt" ], +[ 491, "ru" ], +[ 492, "rv" ], +[ 493, "rw" ], +[ 494, "rx" ], +[ 495, "ry" ], +[ 496, "rz" ], +[ 497, "sa" ], +[ 498, "sb" ], +[ 499, "sc" ], +[ 500, "sd" ], +[ 501, "se" ], +[ 502, "sf" ], +[ 503, "sg" ], +[ 504, "sh" ], +[ 505, "si" ], +[ 506, "sj" ], +[ 507, "sk" ], +[ 508, "sl" ], +[ 509, "sm" ], +[ 510, "sn" ], +[ 511, "so" ], +[ 512, "sp" ], +[ 513, "sq" ], +[ 514, "sr" ], +[ 515, "ss" ], +[ 516, "st" ], +[ 517, "su" ], +[ 518, "sv" ], +[ 519, "sw" ], +[ 520, "sx" ], +[ 521, "sy" ], +[ 522, "sz" ], +[ 523, "ta" ], +[ 524, "tb" ], +[ 525, "tc" ], +[ 526, "td" ], +[ 527, "te" ], +[ 528, "tf" ], +[ 529, "tg" ], +[ 530, "th" ], +[ 531, "ti" ], +[ 532, "tj" ], +[ 533, "tk" ], +[ 534, "tl" ], +[ 535, "tm" ], +[ 536, "tn" ], +[ 537, "to" ], +[ 538, "tp" ], +[ 539, "tq" ], +[ 540, "tr" ], +[ 541, "ts" ], +[ 542, "tt" ], +[ 543, "tu" ], +[ 544, "tv" ], +[ 545, "tw" ], +[ 546, "tx" ], +[ 547, "ty" ], +[ 548, "tz" ], +[ 549, "ua" ], +[ 550, "ub" ], +[ 551, "uc" ], +[ 552, "ud" ], +[ 553, "ue" ], +[ 554, "uf" ], +[ 555, "ug" ], +[ 556, "uh" ], +[ 557, "ui" ], +[ 558, "uj" ], +[ 559, "uk" ], +[ 560, "ul" ], +[ 561, "um" ], +[ 562, "un" ], +[ 563, "uo" ], +[ 564, "up" ], +[ 565, "uq" ], +[ 566, "ur" ], +[ 567, "us" ], +[ 568, "ut" ], +[ 569, "uu" ], +[ 570, "uv" ], +[ 571, "uw" ], +[ 572, "ux" ], +[ 573, "uy" ], +[ 574, "uz" ], +[ 575, "va" ], +[ 576, "vb" ], +[ 577, "vc" ], +[ 578, "vd" ], +[ 579, "ve" ], +[ 580, "vf" ], +[ 581, "vg" ], +[ 582, "vh" ], +[ 583, "vi" ], +[ 584, "vj" ], +[ 585, "vk" ], +[ 586, "vl" ], +[ 587, "vm" ], +[ 588, "vn" ], +[ 589, "vo" ], +[ 590, "vp" ], +[ 591, "vq" ], +[ 592, "vr" ], +[ 593, "vs" ], +[ 594, "vt" ], +[ 595, "vu" ], +[ 596, "vv" ], +[ 597, "vw" ], +[ 598, "vx" ], +[ 599, "vy" ], +[ 600, "vz" ], +[ 601, "wa" ], +[ 602, "wb" ], +[ 603, "wc" ], +[ 604, "wd" ], +[ 605, "we" ], +[ 606, "wf" ], +[ 607, "wg" ], +[ 608, "wh" ], +[ 609, "wi" ], +[ 610, "wj" ], +[ 611, "wk" ], +[ 612, "wl" ], +[ 613, "wm" ], +[ 614, "wn" ], +[ 615, "wo" ], +[ 616, "wp" ], +[ 617, "wq" ], +[ 618, "wr" ], +[ 619, "ws" ], +[ 620, "wt" ], +[ 621, "wu" ], +[ 622, "wv" ], +[ 623, "ww" ], +[ 624, "wx" ], +[ 625, "wy" ], +[ 626, "wz" ], +[ 627, "xa" ], +[ 628, "xb" ], +[ 629, "xc" ], +[ 630, "xd" ], +[ 631, "xe" ], +[ 632, "xf" ], +[ 633, "xg" ], +[ 634, "xh" ], +[ 635, "xi" ], +[ 636, "xj" ], +[ 637, "xk" ], +[ 638, "xl" ], +[ 639, "xm" ], +[ 640, "xn" ], +[ 641, "xo" ], +[ 642, "xp" ], +[ 643, "xq" ], +[ 644, "xr" ], +[ 645, "xs" ], +[ 646, "xt" ], +[ 647, "xu" ], +[ 648, "xv" ], +[ 649, "xw" ], +[ 650, "xx" ], +[ 651, "xy" ], +[ 652, "xz" ], +[ 653, "ya" ], +[ 654, "yb" ], +[ 655, "yc" ], +[ 656, "yd" ], +[ 657, "ye" ], +[ 658, "yf" ], +[ 659, "yg" ], +[ 660, "yh" ], +[ 661, "yi" ], +[ 662, "yj" ], +[ 663, "yk" ], +[ 664, "yl" ], +[ 665, "ym" ], +[ 666, "yn" ], +[ 667, "yo" ], +[ 668, "yp" ], +[ 669, "yq" ], +[ 670, "yr" ], +[ 671, "ys" ], +[ 672, "yt" ], +[ 673, "yu" ], +[ 674, "yv" ], +[ 675, "yw" ], +[ 676, "yx" ], +[ 677, "yy" ], +[ 678, "yz" ], +[ 679, "za" ], +[ 680, "zb" ], +[ 681, "zc" ], +[ 682, "zd" ], +[ 683, "ze" ], +[ 684, "zf" ], +[ 685, "zg" ], +[ 686, "zh" ], +[ 687, "zi" ], +[ 688, "zj" ], +[ 689, "zk" ], +[ 690, "zl" ], +[ 691, "zm" ], +[ 692, "zn" ], +[ 693, "zo" ], +[ 694, "zp" ], +[ 695, "zq" ], +[ 696, "zr" ], +[ 697, "zs" ], +[ 698, "zt" ], +[ 699, "zu" ], +[ 700, "zv" ], +[ 701, "zw" ], +[ 702, "zx" ], +[ 703, "zy" ], +[ 704, "zz" ], +[ 705, "aaa" ], +[ 706, "aab" ], +[ 707, "aac" ], +[ 708, "aad" ], +[ 709, "aae" ], +[ 710, "aaf" ], +[ 711, "aag" ], +[ 712, "aah" ], +[ 713, "aai" ], +[ 714, "aaj" ], +[ 715, "aak" ], +[ 716, "aal" ], +[ 717, "aam" ], +[ 718, "aan" ], +[ 719, "aao" ], +[ 720, "aap" ], +[ 721, "aaq" ], +[ 722, "aar" ], +[ 723, "aas" ], +[ 724, "aat" ], +[ 725, "aau" ], +[ 726, "aav" ], +[ 727, "aaw" ], +[ 728, "aax" ], +[ 729, "aay" ], +[ 730, "aaz" ], +[ 731, "aba" ], +[ 732, "abb" ], +[ 733, "abc" ], +[ 734, "abd" ], +[ 735, "abe" ], +[ 736, "abf" ], +[ 737, "abg" ], +[ 738, "abh" ], +[ 739, "abi" ], +[ 740, "abj" ], +[ 741, "abk" ], +[ 742, "abl" ], +[ 743, "abm" ], +[ 744, "abn" ], +[ 745, "abo" ], +[ 746, "abp" ], +[ 747, "abq" ], +[ 748, "abr" ], +[ 749, "abs" ], +[ 750, "abt" ], +[ 751, "abu" ], +[ 752, "abv" ], +[ 753, "abw" ], +[ 754, "abx" ], +[ 755, "aby" ], +[ 756, "abz" ], +[ 757, "aca" ], +[ 758, "acb" ], +[ 759, "acc" ], +[ 760, "acd" ], +[ 761, "ace" ], +[ 762, "acf" ], +[ 763, "acg" ], +[ 764, "ach" ], +[ 765, "aci" ], +[ 766, "acj" ], +[ 767, "ack" ], +[ 768, "acl" ], +[ 769, "acm" ], +[ 770, "acn" ], +[ 771, "aco" ], +[ 772, "acp" ], +[ 773, "acq" ], +[ 774, "acr" ], +[ 775, "acs" ], +[ 776, "act" ], +[ 777, "acu" ], +[ 778, "acv" ], +[ 779, "acw" ], +[ 780, "acx" ], +[ 781, "acy" ], +[ 782, "acz" ], +[ 783, "ada" ], +[ 784, "adb" ], +[ 785, "adc" ], +[ 786, "add" ], +[ 787, "ade" ], +[ 788, "adf" ], +[ 789, "adg" ], +[ 790, "adh" ], +[ 791, "adi" ], +[ 792, "adj" ], +[ 793, "adk" ], +[ 794, "adl" ], +[ 795, "adm" ], +[ 796, "adn" ], +[ 797, "ado" ], +[ 798, "adp" ], +[ 799, "adq" ], +[ 800, "adr" ], +[ 801, "ads" ], +[ 802, "adt" ], +[ 803, "adu" ], +[ 804, "adv" ], +[ 805, "adw" ], +[ 806, "adx" ], +[ 807, "ady" ], +[ 808, "adz" ], +[ 809, "aea" ], +[ 810, "aeb" ], +[ 811, "aec" ], +[ 812, "aed" ], +[ 813, "aee" ], +[ 814, "aef" ], +[ 815, "aeg" ], +[ 816, "aeh" ], +[ 817, "aei" ], +[ 818, "aej" ], +[ 819, "aek" ], +[ 820, "ael" ], +[ 821, "aem" ], +[ 822, "aen" ], +[ 823, "aeo" ], +[ 824, "aep" ], +[ 825, "aeq" ], +[ 826, "aer" ], +[ 827, "aes" ], +[ 828, "aet" ], +[ 829, "aeu" ], +[ 830, "aev" ], +[ 831, "aew" ], +[ 832, "aex" ], +[ 833, "aey" ], +[ 834, "aez" ], +[ 835, "afa" ], +[ 836, "afb" ], +[ 837, "afc" ], +[ 838, "afd" ], +[ 839, "afe" ], +[ 840, "aff" ], +[ 841, "afg" ], +[ 842, "afh" ], +[ 843, "afi" ], +[ 844, "afj" ], +[ 845, "afk" ], +[ 846, "afl" ], +[ 847, "afm" ], +[ 848, "afn" ], +[ 849, "afo" ], +[ 850, "afp" ], +[ 851, "afq" ], +[ 852, "afr" ], +[ 853, "afs" ], +[ 854, "aft" ], +[ 855, "afu" ], +[ 856, "afv" ], +[ 857, "afw" ], +[ 858, "afx" ], +[ 859, "afy" ], +[ 860, "afz" ], +[ 861, "aga" ], +[ 862, "agb" ], +[ 863, "agc" ], +[ 864, "agd" ], +[ 865, "age" ], +[ 866, "agf" ], +[ 867, "agg" ], +[ 868, "agh" ], +[ 869, "agi" ], +[ 870, "agj" ], +[ 871, "agk" ], +[ 872, "agl" ], +[ 873, "agm" ], +[ 874, "agn" ], +[ 875, "ago" ], +[ 876, "agp" ], +[ 877, "agq" ], +[ 878, "agr" ], +[ 879, "ags" ], +[ 880, "agt" ], +[ 881, "agu" ], +[ 882, "agv" ], +[ 883, "agw" ], +[ 884, "agx" ], +[ 885, "agy" ], +[ 886, "agz" ], +[ 887, "aha" ], +[ 888, "ahb" ], +[ 889, "ahc" ], +[ 890, "ahd" ], +[ 891, "ahe" ], +[ 892, "ahf" ], +[ 893, "ahg" ], +[ 894, "ahh" ], +[ 895, "ahi" ], +[ 896, "ahj" ], +[ 897, "ahk" ], +[ 898, "ahl" ], +[ 899, "ahm" ], +[ 900, "ahn" ], +[ 901, "aho" ], +[ 902, "ahp" ], +[ 903, "ahq" ], +[ 904, "ahr" ], +[ 905, "ahs" ], +[ 906, "aht" ], +[ 907, "ahu" ], +[ 908, "ahv" ], +[ 909, "ahw" ], +[ 910, "ahx" ], +[ 911, "ahy" ], +[ 912, "ahz" ], +[ 913, "aia" ], +[ 914, "aib" ], +[ 915, "aic" ], +[ 916, "aid" ], +[ 917, "aie" ], +[ 918, "aif" ], +[ 919, "aig" ], +[ 920, "aih" ], +[ 921, "aii" ], +[ 922, "aij" ], +[ 923, "aik" ], +[ 924, "ail" ], +[ 925, "aim" ], +[ 926, "ain" ], +[ 927, "aio" ], +[ 928, "aip" ], +[ 929, "aiq" ], +[ 930, "air" ], +[ 931, "ais" ], +[ 932, "ait" ], +[ 933, "aiu" ], +[ 934, "aiv" ], +[ 935, "aiw" ], +[ 936, "aix" ], +[ 937, "aiy" ], +[ 938, "aiz" ], +[ 939, "aja" ], +[ 940, "ajb" ], +[ 941, "ajc" ], +[ 942, "ajd" ], +[ 943, "aje" ], +[ 944, "ajf" ], +[ 945, "ajg" ], +[ 946, "ajh" ], +[ 947, "aji" ], +[ 948, "ajj" ], +[ 949, "ajk" ], +[ 950, "ajl" ], +[ 951, "ajm" ], +[ 952, "ajn" ], +[ 953, "ajo" ], +[ 954, "ajp" ], +[ 955, "ajq" ], +[ 956, "ajr" ], +[ 957, "ajs" ], +[ 958, "ajt" ], +[ 959, "aju" ], +[ 960, "ajv" ], +[ 961, "ajw" ], +[ 962, "ajx" ], +[ 963, "ajy" ], +[ 964, "ajz" ], +[ 965, "aka" ], +[ 966, "akb" ], +[ 967, "akc" ], +[ 968, "akd" ], +[ 969, "ake" ], +[ 970, "akf" ], +[ 971, "akg" ], +[ 972, "akh" ], +[ 973, "aki" ], +[ 974, "akj" ], +[ 975, "akk" ], +[ 976, "akl" ], +[ 977, "akm" ], +[ 978, "akn" ], +[ 979, "ako" ], +[ 980, "akp" ], +[ 981, "akq" ], +[ 982, "akr" ], +[ 983, "aks" ], +[ 984, "akt" ], +[ 985, "aku" ], +[ 986, "akv" ], +[ 987, "akw" ], +[ 988, "akx" ], +[ 989, "aky" ], +[ 990, "akz" ], +[ 991, "ala" ], +[ 992, "alb" ], +[ 993, "alc" ], +[ 994, "ald" ], +[ 995, "ale" ], +[ 996, "alf" ], +[ 997, "alg" ], +[ 998, "alh" ], +[ 999, "ali" ], +[ 1000, "alj" ], +[ 1001, "alk" ], +[ 1002, "all" ], +[ 1003, "alm" ], +[ 1004, "aln" ], +[ 1005, "alo" ], +[ 1006, "alp" ], +[ 1007, "alq" ], +[ 1008, "alr" ], +[ 1009, "als" ], +[ 1010, "alt" ], +[ 1011, "alu" ], +[ 1012, "alv" ], +[ 1013, "alw" ], +[ 1014, "alx" ], +[ 1015, "aly" ], +[ 1016, "alz" ], +[ 1017, "ama" ], +[ 1018, "amb" ], +[ 1019, "amc" ], +[ 1020, "amd" ], +[ 1021, "ame" ], +[ 1022, "amf" ], +[ 1023, "amg" ], +[ 1024, "amh" ], +[ 1025, "ami" ], +[ 1026, "amj" ], +[ 1027, "amk" ], +[ 1028, "aml" ], +[ 1029, "amm" ], +[ 1030, "amn" ], +[ 1031, "amo" ], +[ 1032, "amp" ], +[ 1033, "amq" ], +[ 1034, "amr" ], +[ 1035, "ams" ], +[ 1036, "amt" ], +[ 1037, "amu" ], +[ 1038, "amv" ], +[ 1039, "amw" ], +[ 1040, "amx" ], +[ 1041, "amy" ], +[ 1042, "amz" ], +[ 1043, "ana" ], +[ 1044, "anb" ], +[ 1045, "anc" ], +[ 1046, "and" ], +[ 1047, "ane" ], +[ 1048, "anf" ], +[ 1049, "ang" ], +[ 1050, "anh" ], +[ 1051, "ani" ], +[ 1052, "anj" ], +[ 1053, "ank" ], +[ 1054, "anl" ], +[ 1055, "anm" ], +[ 1056, "ann" ], +[ 1057, "ano" ], +[ 1058, "anp" ], +[ 1059, "anq" ], +[ 1060, "anr" ], +[ 1061, "ans" ], +[ 1062, "ant" ], +[ 1063, "anu" ], +[ 1064, "anv" ], +[ 1065, "anw" ], +[ 1066, "anx" ], +[ 1067, "any" ], +[ 1068, "anz" ], +[ 1069, "aoa" ], +[ 1070, "aob" ], +[ 1071, "aoc" ], +[ 1072, "aod" ], +[ 1073, "aoe" ], +[ 1074, "aof" ], +[ 1075, "aog" ], +[ 1076, "aoh" ], +[ 1077, "aoi" ], +[ 1078, "aoj" ], +[ 1079, "aok" ], +[ 1080, "aol" ], +[ 1081, "aom" ], +[ 1082, "aon" ], +[ 1083, "aoo" ], +[ 1084, "aop" ], +[ 1085, "aoq" ], +[ 1086, "aor" ], +[ 1087, "aos" ], +[ 1088, "aot" ], +[ 1089, "aou" ], +[ 1090, "aov" ], +[ 1091, "aow" ], +[ 1092, "aox" ], +[ 1093, "aoy" ], +[ 1094, "aoz" ], +[ 1095, "apa" ], +[ 1096, "apb" ], +[ 1097, "apc" ], +[ 1098, "apd" ], +[ 1099, "ape" ], +[ 1100, "apf" ], +[ 1101, "apg" ], +[ 1102, "aph" ], +[ 1103, "api" ], +[ 1104, "apj" ], +[ 1105, "apk" ], +[ 1106, "apl" ], +[ 1107, "apm" ], +[ 1108, "apn" ], +[ 1109, "apo" ], +[ 1110, "app" ], +[ 1111, "apq" ], +[ 1112, "apr" ], +[ 1113, "aps" ], +[ 1114, "apt" ], +[ 1115, "apu" ], +[ 1116, "apv" ], +[ 1117, "apw" ], +[ 1118, "apx" ], +[ 1119, "apy" ], +[ 1120, "apz" ], +[ 1121, "aqa" ], +[ 1122, "aqb" ], +[ 1123, "aqc" ], +[ 1124, "aqd" ], +[ 1125, "aqe" ], +[ 1126, "aqf" ], +[ 1127, "aqg" ], +[ 1128, "aqh" ], +[ 1129, "aqi" ], +[ 1130, "aqj" ], +[ 1131, "aqk" ], +[ 1132, "aql" ], +[ 1133, "aqm" ], +[ 1134, "aqn" ], +[ 1135, "aqo" ], +[ 1136, "aqp" ], +[ 1137, "aqq" ], +[ 1138, "aqr" ], +[ 1139, "aqs" ], +[ 1140, "aqt" ], +[ 1141, "aqu" ], +[ 1142, "aqv" ], +[ 1143, "aqw" ], +[ 1144, "aqx" ], +[ 1145, "aqy" ], +[ 1146, "aqz" ], +[ 1147, "ara" ], +[ 1148, "arb" ], +[ 1149, "arc" ], +[ 1150, "ard" ], +[ 1151, "are" ], +[ 1152, "arf" ], +[ 1153, "arg" ], +[ 1154, "arh" ], +[ 1155, "ari" ], +[ 1156, "arj" ], +[ 1157, "ark" ], +[ 1158, "arl" ], +[ 1159, "arm" ], +[ 1160, "arn" ], +[ 1161, "aro" ], +[ 1162, "arp" ], +[ 1163, "arq" ], +[ 1164, "arr" ], +[ 1165, "ars" ], +[ 1166, "art" ], +[ 1167, "aru" ], +[ 1168, "arv" ], +[ 1169, "arw" ], +[ 1170, "arx" ], +[ 1171, "ary" ], +[ 1172, "arz" ], +[ 1173, "asa" ], +[ 1174, "asb" ], +[ 1175, "asc" ], +[ 1176, "asd" ], +[ 1177, "ase" ], +[ 1178, "asf" ], +[ 1179, "asg" ], +[ 1180, "ash" ], +[ 1181, "asi" ], +[ 1182, "asj" ], +[ 1183, "ask" ], +[ 1184, "asl" ], +[ 1185, "asm" ], +[ 1186, "asn" ], +[ 1187, "aso" ], +[ 1188, "asp" ], +[ 1189, "asq" ], +[ 1190, "asr" ], +[ 1191, "ass" ], +[ 1192, "ast" ], +[ 1193, "asu" ], +[ 1194, "asv" ], +[ 1195, "asw" ], +[ 1196, "asx" ], +[ 1197, "asy" ], +[ 1198, "asz" ], +[ 1199, "ata" ], +[ 1200, "atb" ], +[ 1201, "atc" ], +[ 1202, "atd" ], +[ 1203, "ate" ], +[ 1204, "atf" ], +[ 1205, "atg" ], +[ 1206, "ath" ], +[ 1207, "ati" ], +[ 1208, "atj" ], +[ 1209, "atk" ], +[ 1210, "atl" ], +[ 1211, "atm" ], +[ 1212, "atn" ], +[ 1213, "ato" ], +[ 1214, "atp" ], +[ 1215, "atq" ], +[ 1216, "atr" ], +[ 1217, "ats" ], +[ 1218, "att" ], +[ 1219, "atu" ], +[ 1220, "atv" ], +[ 1221, "atw" ], +[ 1222, "atx" ], +[ 1223, "aty" ], +[ 1224, "atz" ], +[ 1225, "aua" ], +[ 1226, "aub" ], +[ 1227, "auc" ], +[ 1228, "aud" ], +[ 1229, "aue" ], +[ 1230, "auf" ], +[ 1231, "aug" ], +[ 1232, "auh" ], +[ 1233, "aui" ], +[ 1234, "auj" ], +[ 1235, "auk" ], +[ 1236, "aul" ], +[ 1237, "aum" ], +[ 1238, "aun" ], +[ 1239, "auo" ], +[ 1240, "aup" ], +[ 1241, "auq" ], +[ 1242, "aur" ], +[ 1243, "aus" ], +[ 1244, "aut" ], +[ 1245, "auu" ], +[ 1246, "auv" ], +[ 1247, "auw" ], +[ 1248, "aux" ], +[ 1249, "auy" ], +[ 1250, "auz" ], +[ 1251, "ava" ], +[ 1252, "avb" ], +[ 1253, "avc" ], +[ 1254, "avd" ], +[ 1255, "ave" ], +[ 1256, "avf" ], +[ 1257, "avg" ], +[ 1258, "avh" ], +[ 1259, "avi" ], +[ 1260, "avj" ], +[ 1261, "avk" ], +[ 1262, "avl" ], +[ 1263, "avm" ], +[ 1264, "avn" ], +[ 1265, "avo" ], +[ 1266, "avp" ], +[ 1267, "avq" ], +[ 1268, "avr" ], +[ 1269, "avs" ], +[ 1270, "avt" ], +[ 1271, "avu" ], +[ 1272, "avv" ], +[ 1273, "avw" ], +[ 1274, "avx" ], +[ 1275, "avy" ], +[ 1276, "avz" ], +[ 1277, "awa" ], +[ 1278, "awb" ], +[ 1279, "awc" ], +[ 1280, "awd" ], +[ 1281, "awe" ], +[ 1282, "awf" ], +[ 1283, "awg" ], +[ 1284, "awh" ], +[ 1285, "awi" ], +[ 1286, "awj" ], +[ 1287, "awk" ], +[ 1288, "awl" ], +[ 1289, "awm" ], +[ 1290, "awn" ], +[ 1291, "awo" ], +[ 1292, "awp" ], +[ 1293, "awq" ], +[ 1294, "awr" ], +[ 1295, "aws" ], +[ 1296, "awt" ], +[ 1297, "awu" ], +[ 1298, "awv" ], +[ 1299, "aww" ], +[ 1300, "awx" ], +[ 1301, "awy" ], +[ 1302, "awz" ], +[ 1303, "axa" ], +[ 1304, "axb" ], +[ 1305, "axc" ], +[ 1306, "axd" ], +[ 1307, "axe" ], +[ 1308, "axf" ], +[ 1309, "axg" ], +[ 1310, "axh" ], +[ 1311, "axi" ], +[ 1312, "axj" ], +[ 1313, "axk" ], +[ 1314, "axl" ], +[ 1315, "axm" ], +[ 1316, "axn" ], +[ 1317, "axo" ], +[ 1318, "axp" ], +[ 1319, "axq" ], +[ 1320, "axr" ], +[ 1321, "axs" ], +[ 1322, "axt" ], +[ 1323, "axu" ], +[ 1324, "axv" ], +[ 1325, "axw" ], +[ 1326, "axx" ], +[ 1327, "axy" ], +[ 1328, "axz" ], +[ 1329, "aya" ], +[ 1330, "ayb" ], +[ 1331, "ayc" ], +[ 1332, "ayd" ], +[ 1333, "aye" ], +[ 1334, "ayf" ], +[ 1335, "ayg" ], +[ 1336, "ayh" ], +[ 1337, "ayi" ], +[ 1338, "ayj" ], +[ 1339, "ayk" ], +[ 1340, "ayl" ], +[ 1341, "aym" ], +[ 1342, "ayn" ], +[ 1343, "ayo" ], +[ 1344, "ayp" ], +[ 1345, "ayq" ], +[ 1346, "ayr" ], +[ 1347, "ays" ], +[ 1348, "ayt" ], +[ 1349, "ayu" ], +[ 1350, "ayv" ], +[ 1351, "ayw" ], +[ 1352, "ayx" ], +[ 1353, "ayy" ], +[ 1354, "ayz" ], +[ 1355, "aza" ], +[ 1356, "azb" ], +[ 1357, "azc" ], +[ 1358, "azd" ], +[ 1359, "aze" ], +[ 1360, "azf" ], +[ 1361, "azg" ], +[ 1362, "azh" ], +[ 1363, "azi" ], +[ 1364, "azj" ], +[ 1365, "azk" ], +[ 1366, "azl" ], +[ 1367, "azm" ], +[ 1368, "azn" ], +[ 1369, "azo" ], +[ 1370, "azp" ], +[ 1371, "azq" ], +[ 1372, "azr" ], +[ 1373, "azs" ], +[ 1374, "azt" ], +[ 1375, "azu" ], +[ 1376, "azv" ], +[ 1377, "azw" ], +[ 1378, "azx" ], +[ 1379, "azy" ], +[ 1380, "azz" ], +[ 1381, "baa" ], +[ 1382, "bab" ], +[ 1383, "bac" ], +[ 1384, "bad" ], +[ 1385, "bae" ], +[ 1386, "baf" ], +[ 1387, "bag" ], +[ 1388, "bah" ], +[ 1389, "bai" ], +[ 1390, "baj" ], +[ 1391, "bak" ], +[ 1392, "bal" ], +[ 1393, "bam" ], +[ 1394, "ban" ], +[ 1395, "bao" ], +[ 1396, "bap" ], +[ 1397, "baq" ], +[ 1398, "bar" ], +[ 1399, "bas" ], +[ 1400, "bat" ], +[ 1401, "bau" ], +[ 1402, "bav" ], +[ 1403, "baw" ], +[ 1404, "bax" ], +[ 1405, "bay" ], +[ 1406, "baz" ], +[ 1407, "bba" ], +[ 1408, "bbb" ], +[ 1409, "bbc" ], +[ 1410, "bbd" ], +[ 1411, "bbe" ], +[ 1412, "bbf" ], +[ 1413, "bbg" ], +[ 1414, "bbh" ], +[ 1415, "bbi" ], +[ 1416, "bbj" ], +[ 1417, "bbk" ], +[ 1418, "bbl" ], +[ 1419, "bbm" ], +[ 1420, "bbn" ], +[ 1421, "bbo" ], +[ 1422, "bbp" ], +[ 1423, "bbq" ], +[ 1424, "bbr" ], +[ 1425, "bbs" ], +[ 1426, "bbt" ], +[ 1427, "bbu" ], +[ 1428, "bbv" ], +[ 1429, "bbw" ], +[ 1430, "bbx" ], +[ 1431, "bby" ], +[ 1432, "bbz" ], +[ 1433, "bca" ], +[ 1434, "bcb" ], +[ 1435, "bcc" ], +[ 1436, "bcd" ], +[ 1437, "bce" ], +[ 1438, "bcf" ], +[ 1439, "bcg" ], +[ 1440, "bch" ], +[ 1441, "bci" ], +[ 1442, "bcj" ], +[ 1443, "bck" ], +[ 1444, "bcl" ], +[ 1445, "bcm" ], +[ 1446, "bcn" ], +[ 1447, "bco" ], +[ 1448, "bcp" ], +[ 1449, "bcq" ], +[ 1450, "bcr" ], +[ 1451, "bcs" ], +[ 1452, "bct" ], +[ 1453, "bcu" ], +[ 1454, "bcv" ], +[ 1455, "bcw" ], +[ 1456, "bcx" ], +[ 1457, "bcy" ], +[ 1458, "bcz" ], +[ 1459, "bda" ], +[ 1460, "bdb" ], +[ 1461, "bdc" ], +[ 1462, "bdd" ], +[ 1463, "bde" ], +[ 1464, "bdf" ], +[ 1465, "bdg" ], +[ 1466, "bdh" ], +[ 1467, "bdi" ], +[ 1468, "bdj" ], +[ 1469, "bdk" ], +[ 1470, "bdl" ], +[ 1471, "bdm" ], +[ 1472, "bdn" ], +[ 1473, "bdo" ], +[ 1474, "bdp" ], +[ 1475, "bdq" ], +[ 1476, "bdr" ], +[ 1477, "bds" ], +[ 1478, "bdt" ], +[ 1479, "bdu" ], +[ 1480, "bdv" ], +[ 1481, "bdw" ], +[ 1482, "bdx" ], +[ 1483, "bdy" ], +[ 1484, "bdz" ], +[ 1485, "bea" ], +[ 1486, "beb" ], +[ 1487, "bec" ], +[ 1488, "bed" ], +[ 1489, "bee" ], +[ 1490, "bef" ], +[ 1491, "beg" ], +[ 1492, "beh" ], +[ 1493, "bei" ], +[ 1494, "bej" ], +[ 1495, "bek" ], +[ 1496, "bel" ], +[ 1497, "bem" ], +[ 1498, "ben" ], +[ 1499, "beo" ], +[ 1500, "bep" ], +[ 1501, "beq" ], +[ 1502, "ber" ], +[ 1503, "bes" ], +[ 1504, "bet" ], +[ 1505, "beu" ], +[ 1506, "bev" ], +[ 1507, "bew" ], +[ 1508, "bex" ], +[ 1509, "bey" ], +[ 1510, "bez" ], +[ 1511, "bfa" ], +[ 1512, "bfb" ], +[ 1513, "bfc" ], +[ 1514, "bfd" ], +[ 1515, "bfe" ], +[ 1516, "bff" ], +[ 1517, "bfg" ], +[ 1518, "bfh" ], +[ 1519, "bfi" ], +[ 1520, "bfj" ], +[ 1521, "bfk" ], +[ 1522, "bfl" ], +[ 1523, "bfm" ], +[ 1524, "bfn" ], +[ 1525, "bfo" ], +[ 1526, "bfp" ], +[ 1527, "bfq" ], +[ 1528, "bfr" ], +[ 1529, "bfs" ], +[ 1530, "bft" ], +[ 1531, "bfu" ], +[ 1532, "bfv" ], +[ 1533, "bfw" ], +[ 1534, "bfx" ], +[ 1535, "bfy" ], +[ 1536, "bfz" ], +[ 1537, "bga" ], +[ 1538, "bgb" ], +[ 1539, "bgc" ], +[ 1540, "bgd" ], +[ 1541, "bge" ], +[ 1542, "bgf" ], +[ 1543, "bgg" ], +[ 1544, "bgh" ], +[ 1545, "bgi" ], +[ 1546, "bgj" ], +[ 1547, "bgk" ], +[ 1548, "bgl" ], +[ 1549, "bgm" ], +[ 1550, "bgn" ], +[ 1551, "bgo" ], +[ 1552, "bgp" ], +[ 1553, "bgq" ], +[ 1554, "bgr" ], +[ 1555, "bgs" ], +[ 1556, "bgt" ], +[ 1557, "bgu" ], +[ 1558, "bgv" ], +[ 1559, "bgw" ], +[ 1560, "bgx" ], +[ 1561, "bgy" ], +[ 1562, "bgz" ], +[ 1563, "bha" ], +[ 1564, "bhb" ], +[ 1565, "bhc" ], +[ 1566, "bhd" ], +[ 1567, "bhe" ], +[ 1568, "bhf" ], +[ 1569, "bhg" ], +[ 1570, "bhh" ], +[ 1571, "bhi" ], +[ 1572, "bhj" ], +[ 1573, "bhk" ], +[ 1574, "bhl" ], +[ 1575, "bhm" ], +[ 1576, "bhn" ], +[ 1577, "bho" ], +[ 1578, "bhp" ], +[ 1579, "bhq" ], +[ 1580, "bhr" ], +[ 1581, "bhs" ], +[ 1582, "bht" ], +[ 1583, "bhu" ], +[ 1584, "bhv" ], +[ 1585, "bhw" ], +[ 1586, "bhx" ], +[ 1587, "bhy" ], +[ 1588, "bhz" ], +[ 1589, "bia" ], +[ 1590, "bib" ], +[ 1591, "bic" ], +[ 1592, "bid" ], +[ 1593, "bie" ], +[ 1594, "bif" ], +[ 1595, "big" ], +[ 1596, "bih" ], +[ 1597, "bii" ], +[ 1598, "bij" ], +[ 1599, "bik" ], +[ 1600, "bil" ], +[ 1601, "bim" ], +[ 1602, "bin" ], +[ 1603, "bio" ], +[ 1604, "bip" ], +[ 1605, "biq" ], +[ 1606, "bir" ], +[ 1607, "bis" ], +[ 1608, "bit" ], +[ 1609, "biu" ], +[ 1610, "biv" ], +[ 1611, "biw" ], +[ 1612, "bix" ], +[ 1613, "biy" ], +[ 1614, "biz" ], +[ 1615, "bja" ], +[ 1616, "bjb" ], +[ 1617, "bjc" ], +[ 1618, "bjd" ], +[ 1619, "bje" ], +[ 1620, "bjf" ], +[ 1621, "bjg" ], +[ 1622, "bjh" ], +[ 1623, "bji" ], +[ 1624, "bjj" ], +[ 1625, "bjk" ], +[ 1626, "bjl" ], +[ 1627, "bjm" ], +[ 1628, "bjn" ], +[ 1629, "bjo" ], +[ 1630, "bjp" ], +[ 1631, "bjq" ], +[ 1632, "bjr" ], +[ 1633, "bjs" ], +[ 1634, "bjt" ], +[ 1635, "bju" ], +[ 1636, "bjv" ], +[ 1637, "bjw" ], +[ 1638, "bjx" ], +[ 1639, "bjy" ], +[ 1640, "bjz" ], +[ 1641, "bka" ], +[ 1642, "bkb" ], +[ 1643, "bkc" ], +[ 1644, "bkd" ], +[ 1645, "bke" ], +[ 1646, "bkf" ], +[ 1647, "bkg" ], +[ 1648, "bkh" ], +[ 1649, "bki" ], +[ 1650, "bkj" ], +[ 1651, "bkk" ], +[ 1652, "bkl" ], +[ 1653, "bkm" ], +[ 1654, "bkn" ], +[ 1655, "bko" ], +[ 1656, "bkp" ], +[ 1657, "bkq" ], +[ 1658, "bkr" ], +[ 1659, "bks" ], +[ 1660, "bkt" ], +[ 1661, "bku" ], +[ 1662, "bkv" ], +[ 1663, "bkw" ], +[ 1664, "bkx" ], +[ 1665, "bky" ], +[ 1666, "bkz" ], +[ 1667, "bla" ], +[ 1668, "blb" ], +[ 1669, "blc" ], +[ 1670, "bld" ], +[ 1671, "ble" ], +[ 1672, "blf" ], +[ 1673, "blg" ], +[ 1674, "blh" ], +[ 1675, "bli" ], +[ 1676, "blj" ], +[ 1677, "blk" ], +[ 1678, "bll" ], +[ 1679, "blm" ], +[ 1680, "bln" ], +[ 1681, "blo" ], +[ 1682, "blp" ], +[ 1683, "blq" ], +[ 1684, "blr" ], +[ 1685, "bls" ], +[ 1686, "blt" ], +[ 1687, "blu" ], +[ 1688, "blv" ], +[ 1689, "blw" ], +[ 1690, "blx" ], +[ 1691, "bly" ], +[ 1692, "blz" ], +[ 1693, "bma" ], +[ 1694, "bmb" ], +[ 1695, "bmc" ], +[ 1696, "bmd" ], +[ 1697, "bme" ], +[ 1698, "bmf" ], +[ 1699, "bmg" ], +[ 1700, "bmh" ], +[ 1701, "bmi" ], +[ 1702, "bmj" ], +[ 1703, "bmk" ], +[ 1704, "bml" ], +[ 1705, "bmm" ], +[ 1706, "bmn" ], +[ 1707, "bmo" ], +[ 1708, "bmp" ], +[ 1709, "bmq" ], +[ 1710, "bmr" ], +[ 1711, "bms" ], +[ 1712, "bmt" ], +[ 1713, "bmu" ], +[ 1714, "bmv" ], +[ 1715, "bmw" ], +[ 1716, "bmx" ], +[ 1717, "bmy" ], +[ 1718, "bmz" ], +[ 1719, "bna" ], +[ 1720, "bnb" ], +[ 1721, "bnc" ], +[ 1722, "bnd" ], +[ 1723, "bne" ], +[ 1724, "bnf" ], +[ 1725, "bng" ], +[ 1726, "bnh" ], +[ 1727, "bni" ], +[ 1728, "bnj" ], +[ 1729, "bnk" ], +[ 1730, "bnl" ], +[ 1731, "bnm" ], +[ 1732, "bnn" ], +[ 1733, "bno" ], +[ 1734, "bnp" ], +[ 1735, "bnq" ], +[ 1736, "bnr" ], +[ 1737, "bns" ], +[ 1738, "bnt" ], +[ 1739, "bnu" ], +[ 1740, "bnv" ], +[ 1741, "bnw" ], +[ 1742, "bnx" ], +[ 1743, "bny" ], +[ 1744, "bnz" ], +[ 1745, "boa" ], +[ 1746, "bob" ], +[ 1747, "boc" ], +[ 1748, "bod" ], +[ 1749, "boe" ], +[ 1750, "bof" ], +[ 1751, "bog" ], +[ 1752, "boh" ], +[ 1753, "boi" ], +[ 1754, "boj" ], +[ 1755, "bok" ], +[ 1756, "bol" ], +[ 1757, "bom" ], +[ 1758, "bon" ], +[ 1759, "boo" ], +[ 1760, "bop" ], +[ 1761, "boq" ], +[ 1762, "bor" ], +[ 1763, "bos" ], +[ 1764, "bot" ], +[ 1765, "bou" ], +[ 1766, "bov" ], +[ 1767, "bow" ], +[ 1768, "box" ], +[ 1769, "boy" ], +[ 1770, "boz" ], +[ 1771, "bpa" ], +[ 1772, "bpb" ], +[ 1773, "bpc" ], +[ 1774, "bpd" ], +[ 1775, "bpe" ], +[ 1776, "bpf" ], +[ 1777, "bpg" ], +[ 1778, "bph" ], +[ 1779, "bpi" ], +[ 1780, "bpj" ], +[ 1781, "bpk" ], +[ 1782, "bpl" ], +[ 1783, "bpm" ], +[ 1784, "bpn" ], +[ 1785, "bpo" ], +[ 1786, "bpp" ], +[ 1787, "bpq" ], +[ 1788, "bpr" ], +[ 1789, "bps" ], +[ 1790, "bpt" ], +[ 1791, "bpu" ], +[ 1792, "bpv" ], +[ 1793, "bpw" ], +[ 1794, "bpx" ], +[ 1795, "bpy" ], +[ 1796, "bpz" ], +[ 1797, "bqa" ], +[ 1798, "bqb" ], +[ 1799, "bqc" ], +[ 1800, "bqd" ], +[ 1801, "bqe" ], +[ 1802, "bqf" ], +[ 1803, "bqg" ], +[ 1804, "bqh" ], +[ 1805, "bqi" ], +[ 1806, "bqj" ], +[ 1807, "bqk" ], +[ 1808, "bql" ], +[ 1809, "bqm" ], +[ 1810, "bqn" ], +[ 1811, "bqo" ], +[ 1812, "bqp" ], +[ 1813, "bqq" ], +[ 1814, "bqr" ], +[ 1815, "bqs" ], +[ 1816, "bqt" ], +[ 1817, "bqu" ], +[ 1818, "bqv" ], +[ 1819, "bqw" ], +[ 1820, "bqx" ], +[ 1821, "bqy" ], +[ 1822, "bqz" ], +[ 1823, "bra" ], +[ 1824, "brb" ], +[ 1825, "brc" ], +[ 1826, "brd" ], +[ 1827, "bre" ], +[ 1828, "brf" ], +[ 1829, "brg" ], +[ 1830, "brh" ], +[ 1831, "bri" ], +[ 1832, "brj" ], +[ 1833, "brk" ], +[ 1834, "brl" ], +[ 1835, "brm" ], +[ 1836, "brn" ], +[ 1837, "bro" ], +[ 1838, "brp" ], +[ 1839, "brq" ], +[ 1840, "brr" ], +[ 1841, "brs" ], +[ 1842, "brt" ], +[ 1843, "bru" ], +[ 1844, "brv" ], +[ 1845, "brw" ], +[ 1846, "brx" ], +[ 1847, "bry" ], +[ 1848, "brz" ], +[ 1849, "bsa" ], +[ 1850, "bsb" ], +[ 1851, "bsc" ], +[ 1852, "bsd" ], +[ 1853, "bse" ], +[ 1854, "bsf" ], +[ 1855, "bsg" ], +[ 1856, "bsh" ], +[ 1857, "bsi" ], +[ 1858, "bsj" ], +[ 1859, "bsk" ], +[ 1860, "bsl" ], +[ 1861, "bsm" ], +[ 1862, "bsn" ], +[ 1863, "bso" ], +[ 1864, "bsp" ], +[ 1865, "bsq" ], +[ 1866, "bsr" ], +[ 1867, "bss" ], +[ 1868, "bst" ], +[ 1869, "bsu" ], +[ 1870, "bsv" ], +[ 1871, "bsw" ], +[ 1872, "bsx" ], +[ 1873, "bsy" ], +[ 1874, "bsz" ], +[ 1875, "bta" ], +[ 1876, "btb" ], +[ 1877, "btc" ], +[ 1878, "btd" ], +[ 1879, "bte" ], +[ 1880, "btf" ], +[ 1881, "btg" ], +[ 1882, "bth" ], +[ 1883, "bti" ], +[ 1884, "btj" ], +[ 1885, "btk" ], +[ 1886, "btl" ], +[ 1887, "btm" ], +[ 1888, "btn" ], +[ 1889, "bto" ], +[ 1890, "btp" ], +[ 1891, "btq" ], +[ 1892, "btr" ], +[ 1893, "bts" ], +[ 1894, "btt" ], +[ 1895, "btu" ], +[ 1896, "btv" ], +[ 1897, "btw" ], +[ 1898, "btx" ], +[ 1899, "bty" ], +[ 1900, "btz" ], +[ 1901, "bua" ], +[ 1902, "bub" ], +[ 1903, "buc" ], +[ 1904, "bud" ], +[ 1905, "bue" ], +[ 1906, "buf" ], +[ 1907, "bug" ], +[ 1908, "buh" ], +[ 1909, "bui" ], +[ 1910, "buj" ], +[ 1911, "buk" ], +[ 1912, "bul" ], +[ 1913, "bum" ], +[ 1914, "bun" ], +[ 1915, "buo" ], +[ 1916, "bup" ], +[ 1917, "buq" ], +[ 1918, "bur" ], +[ 1919, "bus" ], +[ 1920, "but" ], +[ 1921, "buu" ], +[ 1922, "buv" ], +[ 1923, "buw" ], +[ 1924, "bux" ], +[ 1925, "buy" ], +[ 1926, "buz" ], +[ 1927, "bva" ], +[ 1928, "bvb" ], +[ 1929, "bvc" ], +[ 1930, "bvd" ], +[ 1931, "bve" ], +[ 1932, "bvf" ], +[ 1933, "bvg" ], +[ 1934, "bvh" ], +[ 1935, "bvi" ], +[ 1936, "bvj" ], +[ 1937, "bvk" ], +[ 1938, "bvl" ], +[ 1939, "bvm" ], +[ 1940, "bvn" ], +[ 1941, "bvo" ], +[ 1942, "bvp" ], +[ 1943, "bvq" ], +[ 1944, "bvr" ], +[ 1945, "bvs" ], +[ 1946, "bvt" ], +[ 1947, "bvu" ], +[ 1948, "bvv" ], +[ 1949, "bvw" ], +[ 1950, "bvx" ], +[ 1951, "bvy" ], +[ 1952, "bvz" ], +[ 1953, "bwa" ], +[ 1954, "bwb" ], +[ 1955, "bwc" ], +[ 1956, "bwd" ], +[ 1957, "bwe" ], +[ 1958, "bwf" ], +[ 1959, "bwg" ], +[ 1960, "bwh" ], +[ 1961, "bwi" ], +[ 1962, "bwj" ], +[ 1963, "bwk" ], +[ 1964, "bwl" ], +[ 1965, "bwm" ], +[ 1966, "bwn" ], +[ 1967, "bwo" ], +[ 1968, "bwp" ], +[ 1969, "bwq" ], +[ 1970, "bwr" ], +[ 1971, "bws" ], +[ 1972, "bwt" ], +[ 1973, "bwu" ], +[ 1974, "bwv" ], +[ 1975, "bww" ], +[ 1976, "bwx" ], +[ 1977, "bwy" ], +[ 1978, "bwz" ], +[ 1979, "bxa" ], +[ 1980, "bxb" ], +[ 1981, "bxc" ], +[ 1982, "bxd" ], +[ 1983, "bxe" ], +[ 1984, "bxf" ], +[ 1985, "bxg" ], +[ 1986, "bxh" ], +[ 1987, "bxi" ], +[ 1988, "bxj" ], +[ 1989, "bxk" ], +[ 1990, "bxl" ], +[ 1991, "bxm" ], +[ 1992, "bxn" ], +[ 1993, "bxo" ], +[ 1994, "bxp" ], +[ 1995, "bxq" ], +[ 1996, "bxr" ], +[ 1997, "bxs" ], +[ 1998, "bxt" ], +[ 1999, "bxu" ], +[ 2000, "bxv" ], +[ 2001, "bxw" ], +[ 2002, "bxx" ], +[ 2003, "bxy" ], +[ 2004, "bxz" ], +[ 2005, "bya" ], +[ 2006, "byb" ], +[ 2007, "byc" ], +[ 2008, "byd" ], +[ 2009, "bye" ], +[ 2010, "byf" ], +[ 2011, "byg" ], +[ 2012, "byh" ], +[ 2013, "byi" ], +[ 2014, "byj" ], +[ 2015, "byk" ], +[ 2016, "byl" ], +[ 2017, "bym" ], +[ 2018, "byn" ], +[ 2019, "byo" ], +[ 2020, "byp" ], +[ 2021, "byq" ], +[ 2022, "byr" ], +[ 2023, "bys" ], +[ 2024, "byt" ], +[ 2025, "byu" ], +[ 2026, "byv" ], +[ 2027, "byw" ], +[ 2028, "byx" ], +[ 2029, "byy" ], +[ 2030, "byz" ], +[ 2031, "bza" ], +[ 2032, "bzb" ], +[ 2033, "bzc" ], +[ 2034, "bzd" ], +[ 2035, "bze" ], +[ 2036, "bzf" ], +[ 2037, "bzg" ], +[ 2038, "bzh" ], +[ 2039, "bzi" ], +[ 2040, "bzj" ], +[ 2041, "bzk" ], +[ 2042, "bzl" ], +[ 2043, "bzm" ], +[ 2044, "bzn" ], +[ 2045, "bzo" ], +[ 2046, "bzp" ], +[ 2047, "bzq" ], +[ 2048, "bzr" ], +[ 2049, "bzs" ], +[ 2050, "bzt" ], +[ 2051, "bzu" ], +[ 2052, "bzv" ], +[ 2053, "bzw" ], +[ 2054, "bzx" ], +[ 2055, "bzy" ], +[ 2056, "bzz" ], +[ 2057, "caa" ], +[ 2058, "cab" ], +[ 2059, "cac" ], +[ 2060, "cad" ], +[ 2061, "cae" ], +[ 2062, "caf" ], +[ 2063, "cag" ], +[ 2064, "cah" ], +[ 2065, "cai" ], +[ 2066, "caj" ], +[ 2067, "cak" ], +[ 2068, "cal" ], +[ 2069, "cam" ], +[ 2070, "can" ], +[ 2071, "cao" ], +[ 2072, "cap" ], +[ 2073, "caq" ], +[ 2074, "car" ], +[ 2075, "cas" ], +[ 2076, "cat" ], +[ 2077, "cau" ], +[ 2078, "cav" ], +[ 2079, "caw" ], +[ 2080, "cax" ], +[ 2081, "cay" ], +[ 2082, "caz" ], +[ 2083, "cba" ], +[ 2084, "cbb" ], +[ 2085, "cbc" ], +[ 2086, "cbd" ], +[ 2087, "cbe" ], +[ 2088, "cbf" ], +[ 2089, "cbg" ], +[ 2090, "cbh" ], +[ 2091, "cbi" ], +[ 2092, "cbj" ], +[ 2093, "cbk" ], +[ 2094, "cbl" ], +[ 2095, "cbm" ], +[ 2096, "cbn" ], +[ 2097, "cbo" ], +[ 2098, "cbp" ], +[ 2099, "cbq" ], +[ 2100, "cbr" ], +[ 2101, "cbs" ], +[ 2102, "cbt" ], +[ 2103, "cbu" ], +[ 2104, "cbv" ], +[ 2105, "cbw" ], +[ 2106, "cbx" ], +[ 2107, "cby" ], +[ 2108, "cbz" ], +[ 2109, "cca" ], +[ 2110, "ccb" ], +[ 2111, "ccc" ], +[ 2112, "ccd" ], +[ 2113, "cce" ], +[ 2114, "ccf" ], +[ 2115, "ccg" ], +[ 2116, "cch" ], +[ 2117, "cci" ], +[ 2118, "ccj" ], +[ 2119, "cck" ], +[ 2120, "ccl" ], +[ 2121, "ccm" ], +[ 2122, "ccn" ], +[ 2123, "cco" ], +[ 2124, "ccp" ], +[ 2125, "ccq" ], +[ 2126, "ccr" ], +[ 2127, "ccs" ], +[ 2128, "cct" ], +[ 2129, "ccu" ], +[ 2130, "ccv" ], +[ 2131, "ccw" ], +[ 2132, "ccx" ], +[ 2133, "ccy" ], +[ 2134, "ccz" ], +[ 2135, "cda" ], +[ 2136, "cdb" ], +[ 2137, "cdc" ], +[ 2138, "cdd" ], +[ 2139, "cde" ], +[ 2140, "cdf" ], +[ 2141, "cdg" ], +[ 2142, "cdh" ], +[ 2143, "cdi" ], +[ 2144, "cdj" ], +[ 2145, "cdk" ], +[ 2146, "cdl" ], +[ 2147, "cdm" ], +[ 2148, "cdn" ], +[ 2149, "cdo" ], +[ 2150, "cdp" ], +[ 2151, "cdq" ], +[ 2152, "cdr" ], +[ 2153, "cds" ], +[ 2154, "cdt" ], +[ 2155, "cdu" ], +[ 2156, "cdv" ], +[ 2157, "cdw" ], +[ 2158, "cdx" ], +[ 2159, "cdy" ], +[ 2160, "cdz" ], +[ 2161, "cea" ], +[ 2162, "ceb" ], +[ 2163, "cec" ], +[ 2164, "ced" ], +[ 2165, "cee" ], +[ 2166, "cef" ], +[ 2167, "ceg" ], +[ 2168, "ceh" ], +[ 2169, "cei" ], +[ 2170, "cej" ], +[ 2171, "cek" ], +[ 2172, "cel" ], +[ 2173, "cem" ], +[ 2174, "cen" ], +[ 2175, "ceo" ], +[ 2176, "cep" ], +[ 2177, "ceq" ], +[ 2178, "cer" ], +[ 2179, "ces" ], +[ 2180, "cet" ], +[ 2181, "ceu" ], +[ 2182, "cev" ], +[ 2183, "cew" ], +[ 2184, "cex" ], +[ 2185, "cey" ], +[ 2186, "cez" ], +[ 2187, "cfa" ], +[ 2188, "cfb" ], +[ 2189, "cfc" ], +[ 2190, "cfd" ], +[ 2191, "cfe" ], +[ 2192, "cff" ], +[ 2193, "cfg" ], +[ 2194, "cfh" ], +[ 2195, "cfi" ], +[ 2196, "cfj" ], +[ 2197, "cfk" ], +[ 2198, "cfl" ], +[ 2199, "cfm" ], +[ 2200, "cfn" ], +[ 2201, "cfo" ], +[ 2202, "cfp" ], +[ 2203, "cfq" ], +[ 2204, "cfr" ], +[ 2205, "cfs" ], +[ 2206, "cft" ], +[ 2207, "cfu" ], +[ 2208, "cfv" ], +[ 2209, "cfw" ], +[ 2210, "cfx" ], +[ 2211, "cfy" ], +[ 2212, "cfz" ], +[ 2213, "cga" ], +[ 2214, "cgb" ], +[ 2215, "cgc" ], +[ 2216, "cgd" ], +[ 2217, "cge" ], +[ 2218, "cgf" ], +[ 2219, "cgg" ], +[ 2220, "cgh" ], +[ 2221, "cgi" ], +[ 2222, "cgj" ], +[ 2223, "cgk" ], +[ 2224, "cgl" ], +[ 2225, "cgm" ], +[ 2226, "cgn" ], +[ 2227, "cgo" ], +[ 2228, "cgp" ], +[ 2229, "cgq" ], +[ 2230, "cgr" ], +[ 2231, "cgs" ], +[ 2232, "cgt" ], +[ 2233, "cgu" ], +[ 2234, "cgv" ], +[ 2235, "cgw" ], +[ 2236, "cgx" ], +[ 2237, "cgy" ], +[ 2238, "cgz" ], +[ 2239, "cha" ], +[ 2240, "chb" ], +[ 2241, "chc" ], +[ 2242, "chd" ], +[ 2243, "che" ], +[ 2244, "chf" ], +[ 2245, "chg" ], +[ 2246, "chh" ], +[ 2247, "chi" ], +[ 2248, "chj" ], +[ 2249, "chk" ], +[ 2250, "chl" ], +[ 2251, "chm" ], +[ 2252, "chn" ], +[ 2253, "cho" ], +[ 2254, "chp" ], +[ 2255, "chq" ], +[ 2256, "chr" ], +[ 2257, "chs" ], +[ 2258, "cht" ], +[ 2259, "chu" ], +[ 2260, "chv" ], +[ 2261, "chw" ], +[ 2262, "chx" ], +[ 2263, "chy" ], +[ 2264, "chz" ], +[ 2265, "cia" ], +[ 2266, "cib" ], +[ 2267, "cic" ], +[ 2268, "cid" ], +[ 2269, "cie" ], +[ 2270, "cif" ], +[ 2271, "cig" ], +[ 2272, "cih" ], +[ 2273, "cii" ], +[ 2274, "cij" ], +[ 2275, "cik" ], +[ 2276, "cil" ], +[ 2277, "cim" ], +[ 2278, "cin" ], +[ 2279, "cio" ], +[ 2280, "cip" ], +[ 2281, "ciq" ], +[ 2282, "cir" ], +[ 2283, "cis" ], +[ 2284, "cit" ], +[ 2285, "ciu" ], +[ 2286, "civ" ], +[ 2287, "ciw" ], +[ 2288, "cix" ], +[ 2289, "ciy" ], +[ 2290, "ciz" ], +[ 2291, "cja" ], +[ 2292, "cjb" ], +[ 2293, "cjc" ], +[ 2294, "cjd" ], +[ 2295, "cje" ], +[ 2296, "cjf" ], +[ 2297, "cjg" ], +[ 2298, "cjh" ], +[ 2299, "cji" ], +[ 2300, "cjj" ], +[ 2301, "cjk" ], +[ 2302, "cjl" ], +[ 2303, "cjm" ], +[ 2304, "cjn" ], +[ 2305, "cjo" ], +[ 2306, "cjp" ], +[ 2307, "cjq" ], +[ 2308, "cjr" ], +[ 2309, "cjs" ], +[ 2310, "cjt" ], +[ 2311, "cju" ], +[ 2312, "cjv" ], +[ 2313, "cjw" ], +[ 2314, "cjx" ], +[ 2315, "cjy" ], +[ 2316, "cjz" ], +[ 2317, "cka" ], +[ 2318, "ckb" ], +[ 2319, "ckc" ], +[ 2320, "ckd" ], +[ 2321, "cke" ], +[ 2322, "ckf" ], +[ 2323, "ckg" ], +[ 2324, "ckh" ], +[ 2325, "cki" ], +[ 2326, "ckj" ], +[ 2327, "ckk" ], +[ 2328, "ckl" ], +[ 2329, "ckm" ], +[ 2330, "ckn" ], +[ 2331, "cko" ], +[ 2332, "ckp" ], +[ 2333, "ckq" ], +[ 2334, "ckr" ], +[ 2335, "cks" ], +[ 2336, "ckt" ], +[ 2337, "cku" ], +[ 2338, "ckv" ], +[ 2339, "ckw" ], +[ 2340, "ckx" ], +[ 2341, "cky" ], +[ 2342, "ckz" ], +[ 2343, "cla" ], +[ 2344, "clb" ], +[ 2345, "clc" ], +[ 2346, "cld" ], +[ 2347, "cle" ], +[ 2348, "clf" ], +[ 2349, "clg" ], +[ 2350, "clh" ], +[ 2351, "cli" ], +[ 2352, "clj" ], +[ 2353, "clk" ], +[ 2354, "cll" ], +[ 2355, "clm" ], +[ 2356, "cln" ], +[ 2357, "clo" ], +[ 2358, "clp" ], +[ 2359, "clq" ], +[ 2360, "clr" ], +[ 2361, "cls" ], +[ 2362, "clt" ], +[ 2363, "clu" ], +[ 2364, "clv" ], +[ 2365, "clw" ], +[ 2366, "clx" ], +[ 2367, "cly" ], +[ 2368, "clz" ], +[ 2369, "cma" ], +[ 2370, "cmb" ], +[ 2371, "cmc" ], +[ 2372, "cmd" ], +[ 2373, "cme" ], +[ 2374, "cmf" ], +[ 2375, "cmg" ], +[ 2376, "cmh" ], +[ 2377, "cmi" ], +[ 2378, "cmj" ], +[ 2379, "cmk" ], +[ 2380, "cml" ], +[ 2381, "cmm" ], +[ 2382, "cmn" ], +[ 2383, "cmo" ], +[ 2384, "cmp" ], +[ 2385, "cmq" ], +[ 2386, "cmr" ], +[ 2387, "cms" ], +[ 2388, "cmt" ], +[ 2389, "cmu" ], +[ 2390, "cmv" ], +[ 2391, "cmw" ], +[ 2392, "cmx" ], +[ 2393, "cmy" ], +[ 2394, "cmz" ], +[ 2395, "cna" ], +[ 2396, "cnb" ], +[ 2397, "cnc" ], +[ 2398, "cnd" ], +[ 2399, "cne" ], +[ 2400, "cnf" ], +[ 2401, "cng" ], +[ 2402, "cnh" ], +[ 2403, "cni" ], +[ 2404, "cnj" ], +[ 2405, "cnk" ], +[ 2406, "cnl" ], +[ 2407, "cnm" ], +[ 2408, "cnn" ], +[ 2409, "cno" ], +[ 2410, "cnp" ], +[ 2411, "cnq" ], +[ 2412, "cnr" ], +[ 2413, "cns" ], +[ 2414, "cnt" ], +[ 2415, "cnu" ], +[ 2416, "cnv" ], +[ 2417, "cnw" ], +[ 2418, "cnx" ], +[ 2419, "cny" ], +[ 2420, "cnz" ], +[ 2421, "coa" ], +[ 2422, "cob" ], +[ 2423, "coc" ], +[ 2424, "cod" ], +[ 2425, "coe" ], +[ 2426, "cof" ], +[ 2427, "cog" ], +[ 2428, "coh" ], +[ 2429, "coi" ], +[ 2430, "coj" ], +[ 2431, "cok" ], +[ 2432, "col" ], +[ 2433, "com" ], +[ 2434, "con" ], +[ 2435, "coo" ], +[ 2436, "cop" ], +[ 2437, "coq" ], +[ 2438, "cor" ], +[ 2439, "cos" ], +[ 2440, "cot" ], +[ 2441, "cou" ], +[ 2442, "cov" ], +[ 2443, "cow" ], +[ 2444, "cox" ], +[ 2445, "coy" ], +[ 2446, "coz" ], +[ 2447, "cpa" ], +[ 2448, "cpb" ], +[ 2449, "cpc" ], +[ 2450, "cpd" ], +[ 2451, "cpe" ], +[ 2452, "cpf" ], +[ 2453, "cpg" ], +[ 2454, "cph" ], +[ 2455, "cpi" ], +[ 2456, "cpj" ], +[ 2457, "cpk" ], +[ 2458, "cpl" ], +[ 2459, "cpm" ], +[ 2460, "cpn" ], +[ 2461, "cpo" ], +[ 2462, "cpp" ], +[ 2463, "cpq" ], +[ 2464, "cpr" ], +[ 2465, "cps" ], +[ 2466, "cpt" ], +[ 2467, "cpu" ], +[ 2468, "cpv" ], +[ 2469, "cpw" ], +[ 2470, "cpx" ], +[ 2471, "cpy" ], +[ 2472, "cpz" ], +[ 2473, "cqa" ], +[ 2474, "cqb" ], +[ 2475, "cqc" ], +[ 2476, "cqd" ], +[ 2477, "cqe" ], +[ 2478, "cqf" ], +[ 2479, "cqg" ], +[ 2480, "cqh" ], +[ 2481, "cqi" ], +[ 2482, "cqj" ], +[ 2483, "cqk" ], +[ 2484, "cql" ], +[ 2485, "cqm" ], +[ 2486, "cqn" ], +[ 2487, "cqo" ], +[ 2488, "cqp" ], +[ 2489, "cqq" ], +[ 2490, "cqr" ], +[ 2491, "cqs" ], +[ 2492, "cqt" ], +[ 2493, "cqu" ], +[ 2494, "cqv" ], +[ 2495, "cqw" ], +[ 2496, "cqx" ], +[ 2497, "cqy" ], +[ 2498, "cqz" ], +[ 2499, "cra" ], +[ 2500, "crb" ], +[ 2501, "crc" ], +[ 2502, "crd" ], +[ 2503, "cre" ], +[ 2504, "crf" ], +[ 2505, "crg" ], +[ 2506, "crh" ], +[ 2507, "cri" ], +[ 2508, "crj" ], +[ 2509, "crk" ], +[ 2510, "crl" ], +[ 2511, "crm" ], +[ 2512, "crn" ], +[ 2513, "cro" ], +[ 2514, "crp" ], +[ 2515, "crq" ], +[ 2516, "crr" ], +[ 2517, "crs" ], +[ 2518, "crt" ], +[ 2519, "cru" ], +[ 2520, "crv" ], +[ 2521, "crw" ], +[ 2522, "crx" ], +[ 2523, "cry" ], +[ 2524, "crz" ], +[ 2525, "csa" ], +[ 2526, "csb" ], +[ 2527, "csc" ], +[ 2528, "csd" ], +[ 2529, "cse" ], +[ 2530, "csf" ], +[ 2531, "csg" ], +[ 2532, "csh" ], +[ 2533, "csi" ], +[ 2534, "csj" ], +[ 2535, "csk" ], +[ 2536, "csl" ], +[ 2537, "csm" ], +[ 2538, "csn" ], +[ 2539, "cso" ], +[ 2540, "csp" ], +[ 2541, "csq" ], +[ 2542, "csr" ], +[ 2543, "css" ], +[ 2544, "cst" ], +[ 2545, "csu" ], +[ 2546, "csv" ], +[ 2547, "csw" ], +[ 2548, "csx" ], +[ 2549, "csy" ], +[ 2550, "csz" ], +[ 2551, "cta" ], +[ 2552, "ctb" ], +[ 2553, "ctc" ], +[ 2554, "ctd" ], +[ 2555, "cte" ], +[ 2556, "ctf" ], +[ 2557, "ctg" ], +[ 2558, "cth" ], +[ 2559, "cti" ], +[ 2560, "ctj" ], +[ 2561, "ctk" ], +[ 2562, "ctl" ], +[ 2563, "ctm" ], +[ 2564, "ctn" ], +[ 2565, "cto" ], +[ 2566, "ctp" ], +[ 2567, "ctq" ], +[ 2568, "ctr" ], +[ 2569, "cts" ], +[ 2570, "ctt" ], +[ 2571, "ctu" ], +[ 2572, "ctv" ], +[ 2573, "ctw" ], +[ 2574, "ctx" ], +[ 2575, "cty" ], +[ 2576, "ctz" ], +[ 2577, "cua" ], +[ 2578, "cub" ], +[ 2579, "cuc" ], +[ 2580, "cud" ], +[ 2581, "cue" ], +[ 2582, "cuf" ], +[ 2583, "cug" ], +[ 2584, "cuh" ], +[ 2585, "cui" ], +[ 2586, "cuj" ], +[ 2587, "cuk" ], +[ 2588, "cul" ], +[ 2589, "cum" ], +[ 2590, "cun" ], +[ 2591, "cuo" ], +[ 2592, "cup" ], +[ 2593, "cuq" ], +[ 2594, "cur" ], +[ 2595, "cus" ], +[ 2596, "cut" ], +[ 2597, "cuu" ], +[ 2598, "cuv" ], +[ 2599, "cuw" ], +[ 2600, "cux" ], +[ 2601, "cuy" ], +[ 2602, "cuz" ], +[ 2603, "cva" ], +[ 2604, "cvb" ], +[ 2605, "cvc" ], +[ 2606, "cvd" ], +[ 2607, "cve" ], +[ 2608, "cvf" ], +[ 2609, "cvg" ], +[ 2610, "cvh" ], +[ 2611, "cvi" ], +[ 2612, "cvj" ], +[ 2613, "cvk" ], +[ 2614, "cvl" ], +[ 2615, "cvm" ], +[ 2616, "cvn" ], +[ 2617, "cvo" ], +[ 2618, "cvp" ], +[ 2619, "cvq" ], +[ 2620, "cvr" ], +[ 2621, "cvs" ], +[ 2622, "cvt" ], +[ 2623, "cvu" ], +[ 2624, "cvv" ], +[ 2625, "cvw" ], +[ 2626, "cvx" ], +[ 2627, "cvy" ], +[ 2628, "cvz" ], +[ 2629, "cwa" ], +[ 2630, "cwb" ], +[ 2631, "cwc" ], +[ 2632, "cwd" ], +[ 2633, "cwe" ], +[ 2634, "cwf" ], +[ 2635, "cwg" ], +[ 2636, "cwh" ], +[ 2637, "cwi" ], +[ 2638, "cwj" ], +[ 2639, "cwk" ], +[ 2640, "cwl" ], +[ 2641, "cwm" ], +[ 2642, "cwn" ], +[ 2643, "cwo" ], +[ 2644, "cwp" ], +[ 2645, "cwq" ], +[ 2646, "cwr" ], +[ 2647, "cws" ], +[ 2648, "cwt" ], +[ 2649, "cwu" ], +[ 2650, "cwv" ], +[ 2651, "cww" ], +[ 2652, "cwx" ], +[ 2653, "cwy" ], +[ 2654, "cwz" ], +[ 2655, "cxa" ], +[ 2656, "cxb" ], +[ 2657, "cxc" ], +[ 2658, "cxd" ], +[ 2659, "cxe" ], +[ 2660, "cxf" ], +[ 2661, "cxg" ], +[ 2662, "cxh" ], +[ 2663, "cxi" ], +[ 2664, "cxj" ], +[ 2665, "cxk" ], +[ 2666, "cxl" ], +[ 2667, "cxm" ], +[ 2668, "cxn" ], +[ 2669, "cxo" ], +[ 2670, "cxp" ], +[ 2671, "cxq" ], +[ 2672, "cxr" ], +[ 2673, "cxs" ], +[ 2674, "cxt" ], +[ 2675, "cxu" ], +[ 2676, "cxv" ], +[ 2677, "cxw" ], +[ 2678, "cxx" ], +[ 2679, "cxy" ], +[ 2680, "cxz" ], +[ 2681, "cya" ], +[ 2682, "cyb" ], +[ 2683, "cyc" ], +[ 2684, "cyd" ], +[ 2685, "cye" ], +[ 2686, "cyf" ], +[ 2687, "cyg" ], +[ 2688, "cyh" ], +[ 2689, "cyi" ], +[ 2690, "cyj" ], +[ 2691, "cyk" ], +[ 2692, "cyl" ], +[ 2693, "cym" ], +[ 2694, "cyn" ], +[ 2695, "cyo" ], +[ 2696, "cyp" ], +[ 2697, "cyq" ], +[ 2698, "cyr" ], +[ 2699, "cys" ], +[ 2700, "cyt" ], +[ 2701, "cyu" ], +[ 2702, "cyv" ], +[ 2703, "cyw" ], +[ 2704, "cyx" ], +[ 2705, "cyy" ], +[ 2706, "cyz" ], +[ 2707, "cza" ], +[ 2708, "czb" ], +[ 2709, "czc" ], +[ 2710, "czd" ], +[ 2711, "cze" ], +[ 2712, "czf" ], +[ 2713, "czg" ], +[ 2714, "czh" ], +[ 2715, "czi" ], +[ 2716, "czj" ], +[ 2717, "czk" ], +[ 2718, "czl" ], +[ 2719, "czm" ], +[ 2720, "czn" ], +[ 2721, "czo" ], +[ 2722, "czp" ], +[ 2723, "czq" ], +[ 2724, "czr" ], +[ 2725, "czs" ], +[ 2726, "czt" ], +[ 2727, "czu" ], +[ 2728, "czv" ], +[ 2729, "czw" ], +[ 2730, "czx" ], +[ 2731, "czy" ], +[ 2732, "czz" ], +[ 2733, "daa" ], +[ 2734, "dab" ], +[ 2735, "dac" ], +[ 2736, "dad" ], +[ 2737, "dae" ], +[ 2738, "daf" ], +[ 2739, "dag" ], +[ 2740, "dah" ], +[ 2741, "dai" ], +[ 2742, "daj" ], +[ 2743, "dak" ], +[ 2744, "dal" ], +[ 2745, "dam" ], +[ 2746, "dan" ], +[ 2747, "dao" ], +[ 2748, "dap" ], +[ 2749, "daq" ], +[ 2750, "dar" ], +[ 2751, "das" ], +[ 2752, "dat" ], +[ 2753, "dau" ], +[ 2754, "dav" ], +[ 2755, "daw" ], +[ 2756, "dax" ], +[ 2757, "day" ], +[ 2758, "daz" ], +[ 2759, "dba" ], +[ 2760, "dbb" ], +[ 2761, "dbc" ], +[ 2762, "dbd" ], +[ 2763, "dbe" ], +[ 2764, "dbf" ], +[ 2765, "dbg" ], +[ 2766, "dbh" ], +[ 2767, "dbi" ], +[ 2768, "dbj" ], +[ 2769, "dbk" ], +[ 2770, "dbl" ], +[ 2771, "dbm" ], +[ 2772, "dbn" ], +[ 2773, "dbo" ], +[ 2774, "dbp" ], +[ 2775, "dbq" ], +[ 2776, "dbr" ], +[ 2777, "dbs" ], +[ 2778, "dbt" ], +[ 2779, "dbu" ], +[ 2780, "dbv" ], +[ 2781, "dbw" ], +[ 2782, "dbx" ], +[ 2783, "dby" ], +[ 2784, "dbz" ], +[ 2785, "dca" ], +[ 2786, "dcb" ], +[ 2787, "dcc" ], +[ 2788, "dcd" ], +[ 2789, "dce" ], +[ 2790, "dcf" ], +[ 2791, "dcg" ], +[ 2792, "dch" ], +[ 2793, "dci" ], +[ 2794, "dcj" ], +[ 2795, "dck" ], +[ 2796, "dcl" ], +[ 2797, "dcm" ], +[ 2798, "dcn" ], +[ 2799, "dco" ], +[ 2800, "dcp" ], +[ 2801, "dcq" ], +[ 2802, "dcr" ], +[ 2803, "dcs" ], +[ 2804, "dct" ], +[ 2805, "dcu" ], +[ 2806, "dcv" ], +[ 2807, "dcw" ], +[ 2808, "dcx" ], +[ 2809, "dcy" ], +[ 2810, "dcz" ], +[ 2811, "dda" ], +[ 2812, "ddb" ], +[ 2813, "ddc" ], +[ 2814, "ddd" ], +[ 2815, "dde" ], +[ 2816, "ddf" ], +[ 2817, "ddg" ], +[ 2818, "ddh" ], +[ 2819, "ddi" ], +[ 2820, "ddj" ], +[ 2821, "ddk" ], +[ 2822, "ddl" ], +[ 2823, "ddm" ], +[ 2824, "ddn" ], +[ 2825, "ddo" ], +[ 2826, "ddp" ], +[ 2827, "ddq" ], +[ 2828, "ddr" ], +[ 2829, "dds" ], +[ 2830, "ddt" ], +[ 2831, "ddu" ], +[ 2832, "ddv" ], +[ 2833, "ddw" ], +[ 2834, "ddx" ], +[ 2835, "ddy" ], +[ 2836, "ddz" ], +[ 2837, "dea" ], +[ 2838, "deb" ], +[ 2839, "dec" ], +[ 2840, "ded" ], +[ 2841, "dee" ], +[ 2842, "def" ], +[ 2843, "deg" ], +[ 2844, "deh" ], +[ 2845, "dei" ], +[ 2846, "dej" ], +[ 2847, "dek" ], +[ 2848, "del" ], +[ 2849, "dem" ], +[ 2850, "den" ], +[ 2851, "deo" ], +[ 2852, "dep" ], +[ 2853, "deq" ], +[ 2854, "der" ], +[ 2855, "des" ], +[ 2856, "det" ], +[ 2857, "deu" ], +[ 2858, "dev" ], +[ 2859, "dew" ], +[ 2860, "dex" ], +[ 2861, "dey" ], +[ 2862, "dez" ], +[ 2863, "dfa" ], +[ 2864, "dfb" ], +[ 2865, "dfc" ], +[ 2866, "dfd" ], +[ 2867, "dfe" ], +[ 2868, "dff" ], +[ 2869, "dfg" ], +[ 2870, "dfh" ], +[ 2871, "dfi" ], +[ 2872, "dfj" ], +[ 2873, "dfk" ], +[ 2874, "dfl" ], +[ 2875, "dfm" ], +[ 2876, "dfn" ], +[ 2877, "dfo" ], +[ 2878, "dfp" ], +[ 2879, "dfq" ], +[ 2880, "dfr" ], +[ 2881, "dfs" ], +[ 2882, "dft" ], +[ 2883, "dfu" ], +[ 2884, "dfv" ], +[ 2885, "dfw" ], +[ 2886, "dfx" ], +[ 2887, "dfy" ], +[ 2888, "dfz" ], +[ 2889, "dga" ], +[ 2890, "dgb" ], +[ 2891, "dgc" ], +[ 2892, "dgd" ], +[ 2893, "dge" ], +[ 2894, "dgf" ], +[ 2895, "dgg" ], +[ 2896, "dgh" ], +[ 2897, "dgi" ], +[ 2898, "dgj" ], +[ 2899, "dgk" ], +[ 2900, "dgl" ], +[ 2901, "dgm" ], +[ 2902, "dgn" ], +[ 2903, "dgo" ], +[ 2904, "dgp" ], +[ 2905, "dgq" ], +[ 2906, "dgr" ], +[ 2907, "dgs" ], +[ 2908, "dgt" ], +[ 2909, "dgu" ], +[ 2910, "dgv" ], +[ 2911, "dgw" ], +[ 2912, "dgx" ], +[ 2913, "dgy" ], +[ 2914, "dgz" ], +[ 2915, "dha" ], +[ 2916, "dhb" ], +[ 2917, "dhc" ], +[ 2918, "dhd" ], +[ 2919, "dhe" ], +[ 2920, "dhf" ], +[ 2921, "dhg" ], +[ 2922, "dhh" ], +[ 2923, "dhi" ], +[ 2924, "dhj" ], +[ 2925, "dhk" ], +[ 2926, "dhl" ], +[ 2927, "dhm" ], +[ 2928, "dhn" ], +[ 2929, "dho" ], +[ 2930, "dhp" ], +[ 2931, "dhq" ], +[ 2932, "dhr" ], +[ 2933, "dhs" ], +[ 2934, "dht" ], +[ 2935, "dhu" ], +[ 2936, "dhv" ], +[ 2937, "dhw" ], +[ 2938, "dhx" ], +[ 2939, "dhy" ], +[ 2940, "dhz" ], +[ 2941, "dia" ], +[ 2942, "dib" ], +[ 2943, "dic" ], +[ 2944, "did" ], +[ 2945, "die" ], +[ 2946, "dif" ], +[ 2947, "dig" ], +[ 2948, "dih" ], +[ 2949, "dii" ], +[ 2950, "dij" ], +[ 2951, "dik" ], +[ 2952, "dil" ], +[ 2953, "dim" ], +[ 2954, "din" ], +[ 2955, "dio" ], +[ 2956, "dip" ], +[ 2957, "diq" ], +[ 2958, "dir" ], +[ 2959, "dis" ], +[ 2960, "dit" ], +[ 2961, "diu" ], +[ 2962, "div" ], +[ 2963, "diw" ], +[ 2964, "dix" ], +[ 2965, "diy" ], +[ 2966, "diz" ], +[ 2967, "dja" ], +[ 2968, "djb" ], +[ 2969, "djc" ], +[ 2970, "djd" ], +[ 2971, "dje" ], +[ 2972, "djf" ], +[ 2973, "djg" ], +[ 2974, "djh" ], +[ 2975, "dji" ], +[ 2976, "djj" ], +[ 2977, "djk" ], +[ 2978, "djl" ], +[ 2979, "djm" ], +[ 2980, "djn" ], +[ 2981, "djo" ], +[ 2982, "djp" ], +[ 2983, "djq" ], +[ 2984, "djr" ], +[ 2985, "djs" ], +[ 2986, "djt" ], +[ 2987, "dju" ], +[ 2988, "djv" ], +[ 2989, "djw" ], +[ 2990, "djx" ], +[ 2991, "djy" ], +[ 2992, "djz" ], +[ 2993, "dka" ], +[ 2994, "dkb" ], +[ 2995, "dkc" ], +[ 2996, "dkd" ], +[ 2997, "dke" ], +[ 2998, "dkf" ], +[ 2999, "dkg" ], +[ 3000, "dkh" ], +[ 3001, "dki" ], +[ 3002, "dkj" ], +[ 3003, "dkk" ], +[ 3004, "dkl" ], +[ 3005, "dkm" ], +[ 3006, "dkn" ], +[ 3007, "dko" ], +[ 3008, "dkp" ], +[ 3009, "dkq" ], +[ 3010, "dkr" ], +[ 3011, "dks" ], +[ 3012, "dkt" ], +[ 3013, "dku" ], +[ 3014, "dkv" ], +[ 3015, "dkw" ], +[ 3016, "dkx" ], +[ 3017, "dky" ], +[ 3018, "dkz" ], +[ 3019, "dla" ], +[ 3020, "dlb" ], +[ 3021, "dlc" ], +[ 3022, "dld" ], +[ 3023, "dle" ], +[ 3024, "dlf" ], +[ 3025, "dlg" ], +[ 3026, "dlh" ], +[ 3027, "dli" ], +[ 3028, "dlj" ], +[ 3029, "dlk" ], +[ 3030, "dll" ], +[ 3031, "dlm" ], +[ 3032, "dln" ], +[ 3033, "dlo" ], +[ 3034, "dlp" ], +[ 3035, "dlq" ], +[ 3036, "dlr" ], +[ 3037, "dls" ], +[ 3038, "dlt" ], +[ 3039, "dlu" ], +[ 3040, "dlv" ], +[ 3041, "dlw" ], +[ 3042, "dlx" ], +[ 3043, "dly" ], +[ 3044, "dlz" ], +[ 3045, "dma" ], +[ 3046, "dmb" ], +[ 3047, "dmc" ], +[ 3048, "dmd" ], +[ 3049, "dme" ], +[ 3050, "dmf" ], +[ 3051, "dmg" ], +[ 3052, "dmh" ], +[ 3053, "dmi" ], +[ 3054, "dmj" ], +[ 3055, "dmk" ], +[ 3056, "dml" ], +[ 3057, "dmm" ], +[ 3058, "dmn" ], +[ 3059, "dmo" ], +[ 3060, "dmp" ], +[ 3061, "dmq" ], +[ 3062, "dmr" ], +[ 3063, "dms" ], +[ 3064, "dmt" ], +[ 3065, "dmu" ], +[ 3066, "dmv" ], +[ 3067, "dmw" ], +[ 3068, "dmx" ], +[ 3069, "dmy" ], +[ 3070, "dmz" ], +[ 3071, "dna" ], +[ 3072, "dnb" ], +[ 3073, "dnc" ], +[ 3074, "dnd" ], +[ 3075, "dne" ], +[ 3076, "dnf" ], +[ 3077, "dng" ], +[ 3078, "dnh" ], +[ 3079, "dni" ], +[ 3080, "dnj" ], +[ 3081, "dnk" ], +[ 3082, "dnl" ], +[ 3083, "dnm" ], +[ 3084, "dnn" ], +[ 3085, "dno" ], +[ 3086, "dnp" ], +[ 3087, "dnq" ], +[ 3088, "dnr" ], +[ 3089, "dns" ], +[ 3090, "dnt" ], +[ 3091, "dnu" ], +[ 3092, "dnv" ], +[ 3093, "dnw" ], +[ 3094, "dnx" ], +[ 3095, "dny" ], +[ 3096, "dnz" ], +[ 3097, "doa" ], +[ 3098, "dob" ], +[ 3099, "doc" ], +[ 3100, "dod" ], +[ 3101, "doe" ], +[ 3102, "dof" ], +[ 3103, "dog" ], +[ 3104, "doh" ], +[ 3105, "doi" ], +[ 3106, "doj" ], +[ 3107, "dok" ], +[ 3108, "dol" ], +[ 3109, "dom" ], +[ 3110, "don" ], +[ 3111, "doo" ], +[ 3112, "dop" ], +[ 3113, "doq" ], +[ 3114, "dor" ], +[ 3115, "dos" ], +[ 3116, "dot" ], +[ 3117, "dou" ], +[ 3118, "dov" ], +[ 3119, "dow" ], +[ 3120, "dox" ], +[ 3121, "doy" ], +[ 3122, "doz" ], +[ 3123, "dpa" ], +[ 3124, "dpb" ], +[ 3125, "dpc" ], +[ 3126, "dpd" ], +[ 3127, "dpe" ], +[ 3128, "dpf" ], +[ 3129, "dpg" ], +[ 3130, "dph" ], +[ 3131, "dpi" ], +[ 3132, "dpj" ], +[ 3133, "dpk" ], +[ 3134, "dpl" ], +[ 3135, "dpm" ], +[ 3136, "dpn" ], +[ 3137, "dpo" ], +[ 3138, "dpp" ], +[ 3139, "dpq" ], +[ 3140, "dpr" ], +[ 3141, "dps" ], +[ 3142, "dpt" ], +[ 3143, "dpu" ], +[ 3144, "dpv" ], +[ 3145, "dpw" ], +[ 3146, "dpx" ], +[ 3147, "dpy" ], +[ 3148, "dpz" ], +[ 3149, "dqa" ], +[ 3150, "dqb" ], +[ 3151, "dqc" ], +[ 3152, "dqd" ], +[ 3153, "dqe" ], +[ 3154, "dqf" ], +[ 3155, "dqg" ], +[ 3156, "dqh" ], +[ 3157, "dqi" ], +[ 3158, "dqj" ], +[ 3159, "dqk" ], +[ 3160, "dql" ], +[ 3161, "dqm" ], +[ 3162, "dqn" ], +[ 3163, "dqo" ], +[ 3164, "dqp" ], +[ 3165, "dqq" ], +[ 3166, "dqr" ], +[ 3167, "dqs" ], +[ 3168, "dqt" ], +[ 3169, "dqu" ], +[ 3170, "dqv" ], +[ 3171, "dqw" ], +[ 3172, "dqx" ], +[ 3173, "dqy" ], +[ 3174, "dqz" ], +[ 3175, "dra" ], +[ 3176, "drb" ], +[ 3177, "drc" ], +[ 3178, "drd" ], +[ 3179, "dre" ], +[ 3180, "drf" ], +[ 3181, "drg" ], +[ 3182, "drh" ], +[ 3183, "dri" ], +[ 3184, "drj" ], +[ 3185, "drk" ], +[ 3186, "drl" ], +[ 3187, "drm" ], +[ 3188, "drn" ], +[ 3189, "dro" ], +[ 3190, "drp" ], +[ 3191, "drq" ], +[ 3192, "drr" ], +[ 3193, "drs" ], +[ 3194, "drt" ], +[ 3195, "dru" ], +[ 3196, "drv" ], +[ 3197, "drw" ], +[ 3198, "drx" ], +[ 3199, "dry" ], +[ 3200, "drz" ], +[ 3201, "dsa" ], +[ 3202, "dsb" ], +[ 3203, "dsc" ], +[ 3204, "dsd" ], +[ 3205, "dse" ], +[ 3206, "dsf" ], +[ 3207, "dsg" ], +[ 3208, "dsh" ], +[ 3209, "dsi" ], +[ 3210, "dsj" ], +[ 3211, "dsk" ], +[ 3212, "dsl" ], +[ 3213, "dsm" ], +[ 3214, "dsn" ], +[ 3215, "dso" ], +[ 3216, "dsp" ], +[ 3217, "dsq" ], +[ 3218, "dsr" ], +[ 3219, "dss" ], +[ 3220, "dst" ], +[ 3221, "dsu" ], +[ 3222, "dsv" ], +[ 3223, "dsw" ], +[ 3224, "dsx" ], +[ 3225, "dsy" ], +[ 3226, "dsz" ], +[ 3227, "dta" ], +[ 3228, "dtb" ], +[ 3229, "dtc" ], +[ 3230, "dtd" ], +[ 3231, "dte" ], +[ 3232, "dtf" ], +[ 3233, "dtg" ], +[ 3234, "dth" ], +[ 3235, "dti" ], +[ 3236, "dtj" ], +[ 3237, "dtk" ], +[ 3238, "dtl" ], +[ 3239, "dtm" ], +[ 3240, "dtn" ], +[ 3241, "dto" ], +[ 3242, "dtp" ], +[ 3243, "dtq" ], +[ 3244, "dtr" ], +[ 3245, "dts" ], +[ 3246, "dtt" ], +[ 3247, "dtu" ], +[ 3248, "dtv" ], +[ 3249, "dtw" ], +[ 3250, "dtx" ], +[ 3251, "dty" ], +[ 3252, "dtz" ], +[ 3253, "dua" ], +[ 3254, "dub" ], +[ 3255, "duc" ], +[ 3256, "dud" ], +[ 3257, "due" ], +[ 3258, "duf" ], +[ 3259, "dug" ], +[ 3260, "duh" ], +[ 3261, "dui" ], +[ 3262, "duj" ], +[ 3263, "duk" ], +[ 3264, "dul" ], +[ 3265, "dum" ], +[ 3266, "dun" ], +[ 3267, "duo" ], +[ 3268, "dup" ], +[ 3269, "duq" ], +[ 3270, "dur" ], +[ 3271, "dus" ], +[ 3272, "dut" ], +[ 3273, "duu" ], +[ 3274, "duv" ], +[ 3275, "duw" ], +[ 3276, "dux" ], +[ 3277, "duy" ], +[ 3278, "duz" ], +[ 3279, "dva" ], +[ 3280, "dvb" ], +[ 3281, "dvc" ], +[ 3282, "dvd" ], +[ 3283, "dve" ], +[ 3284, "dvf" ], +[ 3285, "dvg" ], +[ 3286, "dvh" ], +[ 3287, "dvi" ], +[ 3288, "dvj" ], +[ 3289, "dvk" ], +[ 3290, "dvl" ], +[ 3291, "dvm" ], +[ 3292, "dvn" ], +[ 3293, "dvo" ], +[ 3294, "dvp" ], +[ 3295, "dvq" ], +[ 3296, "dvr" ], +[ 3297, "dvs" ], +[ 3298, "dvt" ], +[ 3299, "dvu" ], +[ 3300, "dvv" ], +[ 3301, "dvw" ], +[ 3302, "dvx" ], +[ 3303, "dvy" ], +[ 3304, "dvz" ], +[ 3305, "dwa" ], +[ 3306, "dwb" ], +[ 3307, "dwc" ], +[ 3308, "dwd" ], +[ 3309, "dwe" ], +[ 3310, "dwf" ], +[ 3311, "dwg" ], +[ 3312, "dwh" ], +[ 3313, "dwi" ], +[ 3314, "dwj" ], +[ 3315, "dwk" ], +[ 3316, "dwl" ], +[ 3317, "dwm" ], +[ 3318, "dwn" ], +[ 3319, "dwo" ], +[ 3320, "dwp" ], +[ 3321, "dwq" ], +[ 3322, "dwr" ], +[ 3323, "dws" ], +[ 3324, "dwt" ], +[ 3325, "dwu" ], +[ 3326, "dwv" ], +[ 3327, "dww" ], +[ 3328, "dwx" ], +[ 3329, "dwy" ], +[ 3330, "dwz" ], +[ 3331, "dxa" ], +[ 3332, "dxb" ], +[ 3333, "dxc" ], +[ 3334, "dxd" ], +[ 3335, "dxe" ], +[ 3336, "dxf" ], +[ 3337, "dxg" ], +[ 3338, "dxh" ], +[ 3339, "dxi" ], +[ 3340, "dxj" ], +[ 3341, "dxk" ], +[ 3342, "dxl" ], +[ 3343, "dxm" ], +[ 3344, "dxn" ], +[ 3345, "dxo" ], +[ 3346, "dxp" ], +[ 3347, "dxq" ], +[ 3348, "dxr" ], +[ 3349, "dxs" ], +[ 3350, "dxt" ], +[ 3351, "dxu" ], +[ 3352, "dxv" ], +[ 3353, "dxw" ], +[ 3354, "dxx" ], +[ 3355, "dxy" ], +[ 3356, "dxz" ], +[ 3357, "dya" ], +[ 3358, "dyb" ], +[ 3359, "dyc" ], +[ 3360, "dyd" ], +[ 3361, "dye" ], +[ 3362, "dyf" ], +[ 3363, "dyg" ], +[ 3364, "dyh" ], +[ 3365, "dyi" ], +[ 3366, "dyj" ], +[ 3367, "dyk" ], +[ 3368, "dyl" ], +[ 3369, "dym" ], +[ 3370, "dyn" ], +[ 3371, "dyo" ], +[ 3372, "dyp" ], +[ 3373, "dyq" ], +[ 3374, "dyr" ], +[ 3375, "dys" ], +[ 3376, "dyt" ], +[ 3377, "dyu" ], +[ 3378, "dyv" ], +[ 3379, "dyw" ], +[ 3380, "dyx" ], +[ 3381, "dyy" ], +[ 3382, "dyz" ], +[ 3383, "dza" ], +[ 3384, "dzb" ], +[ 3385, "dzc" ], +[ 3386, "dzd" ], +[ 3387, "dze" ], +[ 3388, "dzf" ], +[ 3389, "dzg" ], +[ 3390, "dzh" ], +[ 3391, "dzi" ], +[ 3392, "dzj" ], +[ 3393, "dzk" ], +[ 3394, "dzl" ], +[ 3395, "dzm" ], +[ 3396, "dzn" ], +[ 3397, "dzo" ], +[ 3398, "dzp" ], +[ 3399, "dzq" ], +[ 3400, "dzr" ], +[ 3401, "dzs" ], +[ 3402, "dzt" ], +[ 3403, "dzu" ], +[ 3404, "dzv" ], +[ 3405, "dzw" ], +[ 3406, "dzx" ], +[ 3407, "dzy" ], +[ 3408, "dzz" ], +[ 3409, "eaa" ], +[ 3410, "eab" ], +[ 3411, "eac" ], +[ 3412, "ead" ], +[ 3413, "eae" ], +[ 3414, "eaf" ], +[ 3415, "eag" ], +[ 3416, "eah" ], +[ 3417, "eai" ], +[ 3418, "eaj" ], +[ 3419, "eak" ], +[ 3420, "eal" ], +[ 3421, "eam" ], +[ 3422, "ean" ], +[ 3423, "eao" ], +[ 3424, "eap" ], +[ 3425, "eaq" ], +[ 3426, "ear" ], +[ 3427, "eas" ], +[ 3428, "eat" ], +[ 3429, "eau" ], +[ 3430, "eav" ], +[ 3431, "eaw" ], +[ 3432, "eax" ], +[ 3433, "eay" ], +[ 3434, "eaz" ], +[ 3435, "eba" ], +[ 3436, "ebb" ], +[ 3437, "ebc" ], +[ 3438, "ebd" ], +[ 3439, "ebe" ], +[ 3440, "ebf" ], +[ 3441, "ebg" ], +[ 3442, "ebh" ], +[ 3443, "ebi" ], +[ 3444, "ebj" ], +[ 3445, "ebk" ], +[ 3446, "ebl" ], +[ 3447, "ebm" ], +[ 3448, "ebn" ], +[ 3449, "ebo" ], +[ 3450, "ebp" ], +[ 3451, "ebq" ], +[ 3452, "ebr" ], +[ 3453, "ebs" ], +[ 3454, "ebt" ], +[ 3455, "ebu" ], +[ 3456, "ebv" ], +[ 3457, "ebw" ], +[ 3458, "ebx" ], +[ 3459, "eby" ], +[ 3460, "ebz" ], +[ 3461, "eca" ], +[ 3462, "ecb" ], +[ 3463, "ecc" ], +[ 3464, "ecd" ], +[ 3465, "ece" ], +[ 3466, "ecf" ], +[ 3467, "ecg" ], +[ 3468, "ech" ], +[ 3469, "eci" ], +[ 3470, "ecj" ], +[ 3471, "eck" ], +[ 3472, "ecl" ], +[ 3473, "ecm" ], +[ 3474, "ecn" ], +[ 3475, "eco" ], +[ 3476, "ecp" ], +[ 3477, "ecq" ], +[ 3478, "ecr" ], +[ 3479, "ecs" ], +[ 3480, "ect" ], +[ 3481, "ecu" ], +[ 3482, "ecv" ], +[ 3483, "ecw" ], +[ 3484, "ecx" ], +[ 3485, "ecy" ], +[ 3486, "ecz" ], +[ 3487, "eda" ], +[ 3488, "edb" ], +[ 3489, "edc" ], +[ 3490, "edd" ], +[ 3491, "ede" ], +[ 3492, "edf" ], +[ 3493, "edg" ], +[ 3494, "edh" ], +[ 3495, "edi" ], +[ 3496, "edj" ], +[ 3497, "edk" ], +[ 3498, "edl" ], +[ 3499, "edm" ], +[ 3500, "edn" ], +[ 3501, "edo" ], +[ 3502, "edp" ], +[ 3503, "edq" ], +[ 3504, "edr" ], +[ 3505, "eds" ], +[ 3506, "edt" ], +[ 3507, "edu" ], +[ 3508, "edv" ], +[ 3509, "edw" ], +[ 3510, "edx" ], +[ 3511, "edy" ], +[ 3512, "edz" ], +[ 3513, "eea" ], +[ 3514, "eeb" ], +[ 3515, "eec" ], +[ 3516, "eed" ], +[ 3517, "eee" ], +[ 3518, "eef" ], +[ 3519, "eeg" ], +[ 3520, "eeh" ], +[ 3521, "eei" ], +[ 3522, "eej" ], +[ 3523, "eek" ], +[ 3524, "eel" ], +[ 3525, "eem" ], +[ 3526, "een" ], +[ 3527, "eeo" ], +[ 3528, "eep" ], +[ 3529, "eeq" ], +[ 3530, "eer" ], +[ 3531, "ees" ], +[ 3532, "eet" ], +[ 3533, "eeu" ], +[ 3534, "eev" ], +[ 3535, "eew" ], +[ 3536, "eex" ], +[ 3537, "eey" ], +[ 3538, "eez" ], +[ 3539, "efa" ], +[ 3540, "efb" ], +[ 3541, "efc" ], +[ 3542, "efd" ], +[ 3543, "efe" ], +[ 3544, "eff" ], +[ 3545, "efg" ], +[ 3546, "efh" ], +[ 3547, "efi" ], +[ 3548, "efj" ], +[ 3549, "efk" ], +[ 3550, "efl" ], +[ 3551, "efm" ], +[ 3552, "efn" ], +[ 3553, "efo" ], +[ 3554, "efp" ], +[ 3555, "efq" ], +[ 3556, "efr" ], +[ 3557, "efs" ], +[ 3558, "eft" ], +[ 3559, "efu" ], +[ 3560, "efv" ], +[ 3561, "efw" ], +[ 3562, "efx" ], +[ 3563, "efy" ], +[ 3564, "efz" ], +[ 3565, "ega" ], +[ 3566, "egb" ], +[ 3567, "egc" ], +[ 3568, "egd" ], +[ 3569, "ege" ], +[ 3570, "egf" ], +[ 3571, "egg" ], +[ 3572, "egh" ], +[ 3573, "egi" ], +[ 3574, "egj" ], +[ 3575, "egk" ], +[ 3576, "egl" ], +[ 3577, "egm" ], +[ 3578, "egn" ], +[ 3579, "ego" ], +[ 3580, "egp" ], +[ 3581, "egq" ], +[ 3582, "egr" ], +[ 3583, "egs" ], +[ 3584, "egt" ], +[ 3585, "egu" ], +[ 3586, "egv" ], +[ 3587, "egw" ], +[ 3588, "egx" ], +[ 3589, "egy" ], +[ 3590, "egz" ], +[ 3591, "eha" ], +[ 3592, "ehb" ], +[ 3593, "ehc" ], +[ 3594, "ehd" ], +[ 3595, "ehe" ], +[ 3596, "ehf" ], +[ 3597, "ehg" ], +[ 3598, "ehh" ], +[ 3599, "ehi" ], +[ 3600, "ehj" ], +[ 3601, "ehk" ], +[ 3602, "ehl" ], +[ 3603, "ehm" ], +[ 3604, "ehn" ], +[ 3605, "eho" ], +[ 3606, "ehp" ], +[ 3607, "ehq" ], +[ 3608, "ehr" ], +[ 3609, "ehs" ], +[ 3610, "eht" ], +[ 3611, "ehu" ], +[ 3612, "ehv" ], +[ 3613, "ehw" ], +[ 3614, "ehx" ], +[ 3615, "ehy" ], +[ 3616, "ehz" ], +[ 3617, "eia" ], +[ 3618, "eib" ], +[ 3619, "eic" ], +[ 3620, "eid" ], +[ 3621, "eie" ], +[ 3622, "eif" ], +[ 3623, "eig" ], +[ 3624, "eih" ], +[ 3625, "eii" ], +[ 3626, "eij" ], +[ 3627, "eik" ], +[ 3628, "eil" ], +[ 3629, "eim" ], +[ 3630, "ein" ], +[ 3631, "eio" ], +[ 3632, "eip" ], +[ 3633, "eiq" ], +[ 3634, "eir" ], +[ 3635, "eis" ], +[ 3636, "eit" ], +[ 3637, "eiu" ], +[ 3638, "eiv" ], +[ 3639, "eiw" ], +[ 3640, "eix" ], +[ 3641, "eiy" ], +[ 3642, "eiz" ], +[ 3643, "eja" ], +[ 3644, "ejb" ], +[ 3645, "ejc" ], +[ 3646, "ejd" ], +[ 3647, "eje" ], +[ 3648, "ejf" ], +[ 3649, "ejg" ], +[ 3650, "ejh" ], +[ 3651, "eji" ], +[ 3652, "ejj" ], +[ 3653, "ejk" ], +[ 3654, "ejl" ], +[ 3655, "ejm" ], +[ 3656, "ejn" ], +[ 3657, "ejo" ], +[ 3658, "ejp" ], +[ 3659, "ejq" ], +[ 3660, "ejr" ], +[ 3661, "ejs" ], +[ 3662, "ejt" ], +[ 3663, "eju" ], +[ 3664, "ejv" ], +[ 3665, "ejw" ], +[ 3666, "ejx" ], +[ 3667, "ejy" ], +[ 3668, "ejz" ], +[ 3669, "eka" ], +[ 3670, "ekb" ], +[ 3671, "ekc" ], +[ 3672, "ekd" ], +[ 3673, "eke" ], +[ 3674, "ekf" ], +[ 3675, "ekg" ], +[ 3676, "ekh" ], +[ 3677, "eki" ], +[ 3678, "ekj" ], +[ 3679, "ekk" ], +[ 3680, "ekl" ], +[ 3681, "ekm" ], +[ 3682, "ekn" ], +[ 3683, "eko" ], +[ 3684, "ekp" ], +[ 3685, "ekq" ], +[ 3686, "ekr" ], +[ 3687, "eks" ], +[ 3688, "ekt" ], +[ 3689, "eku" ], +[ 3690, "ekv" ], +[ 3691, "ekw" ], +[ 3692, "ekx" ], +[ 3693, "eky" ], +[ 3694, "ekz" ], +[ 3695, "ela" ], +[ 3696, "elb" ], +[ 3697, "elc" ], +[ 3698, "eld" ], +[ 3699, "ele" ], +[ 3700, "elf" ], +[ 3701, "elg" ], +[ 3702, "elh" ], +[ 3703, "eli" ], +[ 3704, "elj" ], +[ 3705, "elk" ], +[ 3706, "ell" ], +[ 3707, "elm" ], +[ 3708, "eln" ], +[ 3709, "elo" ], +[ 3710, "elp" ], +[ 3711, "elq" ], +[ 3712, "elr" ], +[ 3713, "els" ], +[ 3714, "elt" ], +[ 3715, "elu" ], +[ 3716, "elv" ], +[ 3717, "elw" ], +[ 3718, "elx" ], +[ 3719, "ely" ], +[ 3720, "elz" ], +[ 3721, "ema" ], +[ 3722, "emb" ], +[ 3723, "emc" ], +[ 3724, "emd" ], +[ 3725, "eme" ], +[ 3726, "emf" ], +[ 3727, "emg" ], +[ 3728, "emh" ], +[ 3729, "emi" ], +[ 3730, "emj" ], +[ 3731, "emk" ], +[ 3732, "eml" ], +[ 3733, "emm" ], +[ 3734, "emn" ], +[ 3735, "emo" ], +[ 3736, "emp" ], +[ 3737, "emq" ], +[ 3738, "emr" ], +[ 3739, "ems" ], +[ 3740, "emt" ], +[ 3741, "emu" ], +[ 3742, "emv" ], +[ 3743, "emw" ], +[ 3744, "emx" ], +[ 3745, "emy" ], +[ 3746, "emz" ], +[ 3747, "ena" ], +[ 3748, "enb" ], +[ 3749, "enc" ], +[ 3750, "end" ], +[ 3751, "ene" ], +[ 3752, "enf" ], +[ 3753, "eng" ], +[ 3754, "enh" ], +[ 3755, "eni" ], +[ 3756, "enj" ], +[ 3757, "enk" ], +[ 3758, "enl" ], +[ 3759, "enm" ], +[ 3760, "enn" ], +[ 3761, "eno" ], +[ 3762, "enp" ], +[ 3763, "enq" ], +[ 3764, "enr" ], +[ 3765, "ens" ], +[ 3766, "ent" ], +[ 3767, "enu" ], +[ 3768, "env" ], +[ 3769, "enw" ], +[ 3770, "enx" ], +[ 3771, "eny" ], +[ 3772, "enz" ], +[ 3773, "eoa" ], +[ 3774, "eob" ], +[ 3775, "eoc" ], +[ 3776, "eod" ], +[ 3777, "eoe" ], +[ 3778, "eof" ], +[ 3779, "eog" ], +[ 3780, "eoh" ], +[ 3781, "eoi" ], +[ 3782, "eoj" ], +[ 3783, "eok" ], +[ 3784, "eol" ], +[ 3785, "eom" ], +[ 3786, "eon" ], +[ 3787, "eoo" ], +[ 3788, "eop" ], +[ 3789, "eoq" ], +[ 3790, "eor" ], +[ 3791, "eos" ], +[ 3792, "eot" ], +[ 3793, "eou" ], +[ 3794, "eov" ], +[ 3795, "eow" ], +[ 3796, "eox" ], +[ 3797, "eoy" ], +[ 3798, "eoz" ], +[ 3799, "epa" ], +[ 3800, "epb" ], +[ 3801, "epc" ], +[ 3802, "epd" ], +[ 3803, "epe" ], +[ 3804, "epf" ], +[ 3805, "epg" ], +[ 3806, "eph" ], +[ 3807, "epi" ], +[ 3808, "epj" ], +[ 3809, "epk" ], +[ 3810, "epl" ], +[ 3811, "epm" ], +[ 3812, "epn" ], +[ 3813, "epo" ], +[ 3814, "epp" ], +[ 3815, "epq" ], +[ 3816, "epr" ], +[ 3817, "eps" ], +[ 3818, "ept" ], +[ 3819, "epu" ], +[ 3820, "epv" ], +[ 3821, "epw" ], +[ 3822, "epx" ], +[ 3823, "epy" ], +[ 3824, "epz" ], +[ 3825, "eqa" ], +[ 3826, "eqb" ], +[ 3827, "eqc" ], +[ 3828, "eqd" ], +[ 3829, "eqe" ], +[ 3830, "eqf" ], +[ 3831, "eqg" ], +[ 3832, "eqh" ], +[ 3833, "eqi" ], +[ 3834, "eqj" ], +[ 3835, "eqk" ], +[ 3836, "eql" ], +[ 3837, "eqm" ], +[ 3838, "eqn" ], +[ 3839, "eqo" ], +[ 3840, "eqp" ], +[ 3841, "eqq" ], +[ 3842, "eqr" ], +[ 3843, "eqs" ], +[ 3844, "eqt" ], +[ 3845, "equ" ], +[ 3846, "eqv" ], +[ 3847, "eqw" ], +[ 3848, "eqx" ], +[ 3849, "eqy" ], +[ 3850, "eqz" ], +[ 3851, "era" ], +[ 3852, "erb" ], +[ 3853, "erc" ], +[ 3854, "erd" ], +[ 3855, "ere" ], +[ 3856, "erf" ], +[ 3857, "erg" ], +[ 3858, "erh" ], +[ 3859, "eri" ], +[ 3860, "erj" ], +[ 3861, "erk" ], +[ 3862, "erl" ], +[ 3863, "erm" ], +[ 3864, "ern" ], +[ 3865, "ero" ], +[ 3866, "erp" ], +[ 3867, "erq" ], +[ 3868, "err" ], +[ 3869, "ers" ], +[ 3870, "ert" ], +[ 3871, "eru" ], +[ 3872, "erv" ], +[ 3873, "erw" ], +[ 3874, "erx" ], +[ 3875, "ery" ], +[ 3876, "erz" ], +[ 3877, "esa" ], +[ 3878, "esb" ], +[ 3879, "esc" ], +[ 3880, "esd" ], +[ 3881, "ese" ], +[ 3882, "esf" ], +[ 3883, "esg" ], +[ 3884, "esh" ], +[ 3885, "esi" ], +[ 3886, "esj" ], +[ 3887, "esk" ], +[ 3888, "esl" ], +[ 3889, "esm" ], +[ 3890, "esn" ], +[ 3891, "eso" ], +[ 3892, "esp" ], +[ 3893, "esq" ], +[ 3894, "esr" ], +[ 3895, "ess" ], +[ 3896, "est" ], +[ 3897, "esu" ], +[ 3898, "esv" ], +[ 3899, "esw" ], +[ 3900, "esx" ], +[ 3901, "esy" ], +[ 3902, "esz" ], +[ 3903, "eta" ], +[ 3904, "etb" ], +[ 3905, "etc" ], +[ 3906, "etd" ], +[ 3907, "ete" ], +[ 3908, "etf" ], +[ 3909, "etg" ], +[ 3910, "eth" ], +[ 3911, "eti" ], +[ 3912, "etj" ], +[ 3913, "etk" ], +[ 3914, "etl" ], +[ 3915, "etm" ], +[ 3916, "etn" ], +[ 3917, "eto" ], +[ 3918, "etp" ], +[ 3919, "etq" ], +[ 3920, "etr" ], +[ 3921, "ets" ], +[ 3922, "ett" ], +[ 3923, "etu" ], +[ 3924, "etv" ], +[ 3925, "etw" ], +[ 3926, "etx" ], +[ 3927, "ety" ], +[ 3928, "etz" ], +[ 3929, "eua" ], +[ 3930, "eub" ], +[ 3931, "euc" ], +[ 3932, "eud" ], +[ 3933, "eue" ], +[ 3934, "euf" ], +[ 3935, "eug" ], +[ 3936, "euh" ], +[ 3937, "eui" ], +[ 3938, "euj" ], +[ 3939, "euk" ], +[ 3940, "eul" ], +[ 3941, "eum" ], +[ 3942, "eun" ], +[ 3943, "euo" ], +[ 3944, "eup" ], +[ 3945, "euq" ], +[ 3946, "eur" ], +[ 3947, "eus" ], +[ 3948, "eut" ], +[ 3949, "euu" ], +[ 3950, "euv" ], +[ 3951, "euw" ], +[ 3952, "eux" ], +[ 3953, "euy" ], +[ 3954, "euz" ], +[ 3955, "eva" ], +[ 3956, "evb" ], +[ 3957, "evc" ], +[ 3958, "evd" ], +[ 3959, "eve" ], +[ 3960, "evf" ], +[ 3961, "evg" ], +[ 3962, "evh" ], +[ 3963, "evi" ], +[ 3964, "evj" ], +[ 3965, "evk" ], +[ 3966, "evl" ], +[ 3967, "evm" ], +[ 3968, "evn" ], +[ 3969, "evo" ], +[ 3970, "evp" ], +[ 3971, "evq" ], +[ 3972, "evr" ], +[ 3973, "evs" ], +[ 3974, "evt" ], +[ 3975, "evu" ], +[ 3976, "evv" ], +[ 3977, "evw" ], +[ 3978, "evx" ], +[ 3979, "evy" ], +[ 3980, "evz" ], +[ 3981, "ewa" ], +[ 3982, "ewb" ], +[ 3983, "ewc" ], +[ 3984, "ewd" ], +[ 3985, "ewe" ], +[ 3986, "ewf" ], +[ 3987, "ewg" ], +[ 3988, "ewh" ], +[ 3989, "ewi" ], +[ 3990, "ewj" ], +[ 3991, "ewk" ], +[ 3992, "ewl" ], +[ 3993, "ewm" ], +[ 3994, "ewn" ], +[ 3995, "ewo" ], +[ 3996, "ewp" ], +[ 3997, "ewq" ], +[ 3998, "ewr" ], +[ 3999, "ews" ], +[ 4000, "ewt" ], +[ 4001, "ewu" ], +[ 4002, "ewv" ], +[ 4003, "eww" ], +[ 4004, "ewx" ], +[ 4005, "ewy" ], +[ 4006, "ewz" ], +[ 4007, "exa" ], +[ 4008, "exb" ], +[ 4009, "exc" ], +[ 4010, "exd" ], +[ 4011, "exe" ], +[ 4012, "exf" ], +[ 4013, "exg" ], +[ 4014, "exh" ], +[ 4015, "exi" ], +[ 4016, "exj" ], +[ 4017, "exk" ], +[ 4018, "exl" ], +[ 4019, "exm" ], +[ 4020, "exn" ], +[ 4021, "exo" ], +[ 4022, "exp" ], +[ 4023, "exq" ], +[ 4024, "exr" ], +[ 4025, "exs" ], +[ 4026, "ext" ], +[ 4027, "exu" ], +[ 4028, "exv" ], +[ 4029, "exw" ], +[ 4030, "exx" ], +[ 4031, "exy" ], +[ 4032, "exz" ], +[ 4033, "eya" ], +[ 4034, "eyb" ], +[ 4035, "eyc" ], +[ 4036, "eyd" ], +[ 4037, "eye" ], +[ 4038, "eyf" ], +[ 4039, "eyg" ], +[ 4040, "eyh" ], +[ 4041, "eyi" ], +[ 4042, "eyj" ], +[ 4043, "eyk" ], +[ 4044, "eyl" ], +[ 4045, "eym" ], +[ 4046, "eyn" ], +[ 4047, "eyo" ], +[ 4048, "eyp" ], +[ 4049, "eyq" ], +[ 4050, "eyr" ], +[ 4051, "eys" ], +[ 4052, "eyt" ], +[ 4053, "eyu" ], +[ 4054, "eyv" ], +[ 4055, "eyw" ], +[ 4056, "eyx" ], +[ 4057, "eyy" ], +[ 4058, "eyz" ], +[ 4059, "eza" ], +[ 4060, "ezb" ], +[ 4061, "ezc" ], +[ 4062, "ezd" ], +[ 4063, "eze" ], +[ 4064, "ezf" ], +[ 4065, "ezg" ], +[ 4066, "ezh" ], +[ 4067, "ezi" ], +[ 4068, "ezj" ], +[ 4069, "ezk" ], +[ 4070, "ezl" ], +[ 4071, "ezm" ], +[ 4072, "ezn" ], +[ 4073, "ezo" ], +[ 4074, "ezp" ], +[ 4075, "ezq" ], +[ 4076, "ezr" ], +[ 4077, "ezs" ], +[ 4078, "ezt" ], +[ 4079, "ezu" ], +[ 4080, "ezv" ], +[ 4081, "ezw" ], +[ 4082, "ezx" ], +[ 4083, "ezy" ], +[ 4084, "ezz" ], +[ 4085, "faa" ], +[ 4086, "fab" ], +[ 4087, "fac" ], +[ 4088, "fad" ], +[ 4089, "fae" ], +[ 4090, "faf" ], +[ 4091, "fag" ], +[ 4092, "fah" ], +[ 4093, "fai" ], +[ 4094, "faj" ], +[ 4095, "fak" ], +[ 4096, "fal" ], +[ 4097, "fam" ], +[ 4098, "fan" ], +[ 4099, "fao" ], +[ 4100, "fap" ], +[ 4101, "faq" ], +[ 4102, "far" ], +[ 4103, "fas" ], +[ 4104, "fat" ], +[ 4105, "fau" ], +[ 4106, "fav" ], +[ 4107, "faw" ], +[ 4108, "fax" ], +[ 4109, "fay" ], +[ 4110, "faz" ], +[ 4111, "fba" ], +[ 4112, "fbb" ], +[ 4113, "fbc" ], +[ 4114, "fbd" ], +[ 4115, "fbe" ], +[ 4116, "fbf" ], +[ 4117, "fbg" ], +[ 4118, "fbh" ], +[ 4119, "fbi" ], +[ 4120, "fbj" ], +[ 4121, "fbk" ], +[ 4122, "fbl" ], +[ 4123, "fbm" ], +[ 4124, "fbn" ], +[ 4125, "fbo" ], +[ 4126, "fbp" ], +[ 4127, "fbq" ], +[ 4128, "fbr" ], +[ 4129, "fbs" ], +[ 4130, "fbt" ], +[ 4131, "fbu" ], +[ 4132, "fbv" ], +[ 4133, "fbw" ], +[ 4134, "fbx" ], +[ 4135, "fby" ], +[ 4136, "fbz" ], +[ 4137, "fca" ], +[ 4138, "fcb" ], +[ 4139, "fcc" ], +[ 4140, "fcd" ], +[ 4141, "fce" ], +[ 4142, "fcf" ], +[ 4143, "fcg" ], +[ 4144, "fch" ], +[ 4145, "fci" ], +[ 4146, "fcj" ], +[ 4147, "fck" ], +[ 4148, "fcl" ], +[ 4149, "fcm" ], +[ 4150, "fcn" ], +[ 4151, "fco" ], +[ 4152, "fcp" ], +[ 4153, "fcq" ], +[ 4154, "fcr" ], +[ 4155, "fcs" ], +[ 4156, "fct" ], +[ 4157, "fcu" ], +[ 4158, "fcv" ], +[ 4159, "fcw" ], +[ 4160, "fcx" ], +[ 4161, "fcy" ], +[ 4162, "fcz" ], +[ 4163, "fda" ], +[ 4164, "fdb" ], +[ 4165, "fdc" ], +[ 4166, "fdd" ], +[ 4167, "fde" ], +[ 4168, "fdf" ], +[ 4169, "fdg" ], +[ 4170, "fdh" ], +[ 4171, "fdi" ], +[ 4172, "fdj" ], +[ 4173, "fdk" ], +[ 4174, "fdl" ], +[ 4175, "fdm" ], +[ 4176, "fdn" ], +[ 4177, "fdo" ], +[ 4178, "fdp" ], +[ 4179, "fdq" ], +[ 4180, "fdr" ], +[ 4181, "fds" ], +[ 4182, "fdt" ], +[ 4183, "fdu" ], +[ 4184, "fdv" ], +[ 4185, "fdw" ], +[ 4186, "fdx" ], +[ 4187, "fdy" ], +[ 4188, "fdz" ], +[ 4189, "fea" ], +[ 4190, "feb" ], +[ 4191, "fec" ], +[ 4192, "fed" ], +[ 4193, "fee" ], +[ 4194, "fef" ], +[ 4195, "feg" ], +[ 4196, "feh" ], +[ 4197, "fei" ], +[ 4198, "fej" ], +[ 4199, "fek" ], +[ 4200, "fel" ], +[ 4201, "fem" ], +[ 4202, "fen" ], +[ 4203, "feo" ], +[ 4204, "fep" ], +[ 4205, "feq" ], +[ 4206, "fer" ], +[ 4207, "fes" ], +[ 4208, "fet" ], +[ 4209, "feu" ], +[ 4210, "fev" ], +[ 4211, "few" ], +[ 4212, "fex" ], +[ 4213, "fey" ], +[ 4214, "fez" ], +[ 4215, "ffa" ], +[ 4216, "ffb" ], +[ 4217, "ffc" ], +[ 4218, "ffd" ], +[ 4219, "ffe" ], +[ 4220, "fff" ], +[ 4221, "ffg" ], +[ 4222, "ffh" ], +[ 4223, "ffi" ], +[ 4224, "ffj" ], +[ 4225, "ffk" ], +[ 4226, "ffl" ], +[ 4227, "ffm" ], +[ 4228, "ffn" ], +[ 4229, "ffo" ], +[ 4230, "ffp" ], +[ 4231, "ffq" ], +[ 4232, "ffr" ], +[ 4233, "ffs" ], +[ 4234, "fft" ], +[ 4235, "ffu" ], +[ 4236, "ffv" ], +[ 4237, "ffw" ], +[ 4238, "ffx" ], +[ 4239, "ffy" ], +[ 4240, "ffz" ], +[ 4241, "fga" ], +[ 4242, "fgb" ], +[ 4243, "fgc" ], +[ 4244, "fgd" ], +[ 4245, "fge" ], +[ 4246, "fgf" ], +[ 4247, "fgg" ], +[ 4248, "fgh" ], +[ 4249, "fgi" ], +[ 4250, "fgj" ], +[ 4251, "fgk" ], +[ 4252, "fgl" ], +[ 4253, "fgm" ], +[ 4254, "fgn" ], +[ 4255, "fgo" ], +[ 4256, "fgp" ], +[ 4257, "fgq" ], +[ 4258, "fgr" ], +[ 4259, "fgs" ], +[ 4260, "fgt" ], +[ 4261, "fgu" ], +[ 4262, "fgv" ], +[ 4263, "fgw" ], +[ 4264, "fgx" ], +[ 4265, "fgy" ], +[ 4266, "fgz" ], +[ 4267, "fha" ], +[ 4268, "fhb" ], +[ 4269, "fhc" ], +[ 4270, "fhd" ], +[ 4271, "fhe" ], +[ 4272, "fhf" ], +[ 4273, "fhg" ], +[ 4274, "fhh" ], +[ 4275, "fhi" ], +[ 4276, "fhj" ], +[ 4277, "fhk" ], +[ 4278, "fhl" ], +[ 4279, "fhm" ], +[ 4280, "fhn" ], +[ 4281, "fho" ], +[ 4282, "fhp" ], +[ 4283, "fhq" ], +[ 4284, "fhr" ], +[ 4285, "fhs" ], +[ 4286, "fht" ], +[ 4287, "fhu" ], +[ 4288, "fhv" ], +[ 4289, "fhw" ], +[ 4290, "fhx" ], +[ 4291, "fhy" ], +[ 4292, "fhz" ], +[ 4293, "fia" ], +[ 4294, "fib" ], +[ 4295, "fic" ], +[ 4296, "fid" ], +[ 4297, "fie" ], +[ 4298, "fif" ], +[ 4299, "fig" ], +[ 4300, "fih" ], +[ 4301, "fii" ], +[ 4302, "fij" ], +[ 4303, "fik" ], +[ 4304, "fil" ], +[ 4305, "fim" ], +[ 4306, "fin" ], +[ 4307, "fio" ], +[ 4308, "fip" ], +[ 4309, "fiq" ], +[ 4310, "fir" ], +[ 4311, "fis" ], +[ 4312, "fit" ], +[ 4313, "fiu" ], +[ 4314, "fiv" ], +[ 4315, "fiw" ], +[ 4316, "fix" ], +[ 4317, "fiy" ], +[ 4318, "fiz" ], +[ 4319, "fja" ], +[ 4320, "fjb" ], +[ 4321, "fjc" ], +[ 4322, "fjd" ], +[ 4323, "fje" ], +[ 4324, "fjf" ], +[ 4325, "fjg" ], +[ 4326, "fjh" ], +[ 4327, "fji" ], +[ 4328, "fjj" ], +[ 4329, "fjk" ], +[ 4330, "fjl" ], +[ 4331, "fjm" ], +[ 4332, "fjn" ], +[ 4333, "fjo" ], +[ 4334, "fjp" ], +[ 4335, "fjq" ], +[ 4336, "fjr" ], +[ 4337, "fjs" ], +[ 4338, "fjt" ], +[ 4339, "fju" ], +[ 4340, "fjv" ], +[ 4341, "fjw" ], +[ 4342, "fjx" ], +[ 4343, "fjy" ], +[ 4344, "fjz" ], +[ 4345, "fka" ], +[ 4346, "fkb" ], +[ 4347, "fkc" ], +[ 4348, "fkd" ], +[ 4349, "fke" ], +[ 4350, "fkf" ], +[ 4351, "fkg" ], +[ 4352, "fkh" ], +[ 4353, "fki" ], +[ 4354, "fkj" ], +[ 4355, "fkk" ], +[ 4356, "fkl" ], +[ 4357, "fkm" ], +[ 4358, "fkn" ], +[ 4359, "fko" ], +[ 4360, "fkp" ], +[ 4361, "fkq" ], +[ 4362, "fkr" ], +[ 4363, "fks" ], +[ 4364, "fkt" ], +[ 4365, "fku" ], +[ 4366, "fkv" ], +[ 4367, "fkw" ], +[ 4368, "fkx" ], +[ 4369, "fky" ], +[ 4370, "fkz" ], +[ 4371, "fla" ], +[ 4372, "flb" ], +[ 4373, "flc" ], +[ 4374, "fld" ], +[ 4375, "fle" ], +[ 4376, "flf" ], +[ 4377, "flg" ], +[ 4378, "flh" ], +[ 4379, "fli" ], +[ 4380, "flj" ], +[ 4381, "flk" ], +[ 4382, "fll" ], +[ 4383, "flm" ], +[ 4384, "fln" ], +[ 4385, "flo" ], +[ 4386, "flp" ], +[ 4387, "flq" ], +[ 4388, "flr" ], +[ 4389, "fls" ], +[ 4390, "flt" ], +[ 4391, "flu" ], +[ 4392, "flv" ], +[ 4393, "flw" ], +[ 4394, "flx" ], +[ 4395, "fly" ], +[ 4396, "flz" ], +[ 4397, "fma" ], +[ 4398, "fmb" ], +[ 4399, "fmc" ], +[ 4400, "fmd" ], +[ 4401, "fme" ], +[ 4402, "fmf" ], +[ 4403, "fmg" ], +[ 4404, "fmh" ], +[ 4405, "fmi" ], +[ 4406, "fmj" ], +[ 4407, "fmk" ], +[ 4408, "fml" ], +[ 4409, "fmm" ], +[ 4410, "fmn" ], +[ 4411, "fmo" ], +[ 4412, "fmp" ], +[ 4413, "fmq" ], +[ 4414, "fmr" ], +[ 4415, "fms" ], +[ 4416, "fmt" ], +[ 4417, "fmu" ], +[ 4418, "fmv" ], +[ 4419, "fmw" ], +[ 4420, "fmx" ], +[ 4421, "fmy" ], +[ 4422, "fmz" ], +[ 4423, "fna" ], +[ 4424, "fnb" ], +[ 4425, "fnc" ], +[ 4426, "fnd" ], +[ 4427, "fne" ], +[ 4428, "fnf" ], +[ 4429, "fng" ], +[ 4430, "fnh" ], +[ 4431, "fni" ], +[ 4432, "fnj" ], +[ 4433, "fnk" ], +[ 4434, "fnl" ], +[ 4435, "fnm" ], +[ 4436, "fnn" ], +[ 4437, "fno" ], +[ 4438, "fnp" ], +[ 4439, "fnq" ], +[ 4440, "fnr" ], +[ 4441, "fns" ], +[ 4442, "fnt" ], +[ 4443, "fnu" ], +[ 4444, "fnv" ], +[ 4445, "fnw" ], +[ 4446, "fnx" ], +[ 4447, "fny" ], +[ 4448, "fnz" ], +[ 4449, "foa" ], +[ 4450, "fob" ], +[ 4451, "foc" ], +[ 4452, "fod" ], +[ 4453, "foe" ], +[ 4454, "fof" ], +[ 4455, "fog" ], +[ 4456, "foh" ], +[ 4457, "foi" ], +[ 4458, "foj" ], +[ 4459, "fok" ], +[ 4460, "fol" ], +[ 4461, "fom" ], +[ 4462, "fon" ], +[ 4463, "foo" ], +[ 4464, "fop" ], +[ 4465, "foq" ], +[ 4466, "for" ], +[ 4467, "fos" ], +[ 4468, "fot" ], +[ 4469, "fou" ], +[ 4470, "fov" ], +[ 4471, "fow" ], +[ 4472, "fox" ], +[ 4473, "foy" ], +[ 4474, "foz" ], +[ 4475, "fpa" ], +[ 4476, "fpb" ], +[ 4477, "fpc" ], +[ 4478, "fpd" ], +[ 4479, "fpe" ], +[ 4480, "fpf" ], +[ 4481, "fpg" ], +[ 4482, "fph" ], +[ 4483, "fpi" ], +[ 4484, "fpj" ], +[ 4485, "fpk" ], +[ 4486, "fpl" ], +[ 4487, "fpm" ], +[ 4488, "fpn" ], +[ 4489, "fpo" ], +[ 4490, "fpp" ], +[ 4491, "fpq" ], +[ 4492, "fpr" ], +[ 4493, "fps" ], +[ 4494, "fpt" ], +[ 4495, "fpu" ], +[ 4496, "fpv" ], +[ 4497, "fpw" ], +[ 4498, "fpx" ], +[ 4499, "fpy" ], +[ 4500, "fpz" ], +[ 4501, "fqa" ], +[ 4502, "fqb" ], +[ 4503, "fqc" ], +[ 4504, "fqd" ], +[ 4505, "fqe" ], +[ 4506, "fqf" ], +[ 4507, "fqg" ], +[ 4508, "fqh" ], +[ 4509, "fqi" ], +[ 4510, "fqj" ], +[ 4511, "fqk" ], +[ 4512, "fql" ], +[ 4513, "fqm" ], +[ 4514, "fqn" ], +[ 4515, "fqo" ], +[ 4516, "fqp" ], +[ 4517, "fqq" ], +[ 4518, "fqr" ], +[ 4519, "fqs" ], +[ 4520, "fqt" ], +[ 4521, "fqu" ], +[ 4522, "fqv" ], +[ 4523, "fqw" ], +[ 4524, "fqx" ], +[ 4525, "fqy" ], +[ 4526, "fqz" ], +[ 4527, "fra" ], +[ 4528, "frb" ], +[ 4529, "frc" ], +[ 4530, "frd" ], +[ 4531, "fre" ], +[ 4532, "frf" ], +[ 4533, "frg" ], +[ 4534, "frh" ], +[ 4535, "fri" ], +[ 4536, "frj" ], +[ 4537, "frk" ], +[ 4538, "frl" ], +[ 4539, "frm" ], +[ 4540, "frn" ], +[ 4541, "fro" ], +[ 4542, "frp" ], +[ 4543, "frq" ], +[ 4544, "frr" ], +[ 4545, "frs" ], +[ 4546, "frt" ], +[ 4547, "fru" ], +[ 4548, "frv" ], +[ 4549, "frw" ], +[ 4550, "frx" ], +[ 4551, "fry" ], +[ 4552, "frz" ], +[ 4553, "fsa" ], +[ 4554, "fsb" ], +[ 4555, "fsc" ], +[ 4556, "fsd" ], +[ 4557, "fse" ], +[ 4558, "fsf" ], +[ 4559, "fsg" ], +[ 4560, "fsh" ], +[ 4561, "fsi" ], +[ 4562, "fsj" ], +[ 4563, "fsk" ], +[ 4564, "fsl" ], +[ 4565, "fsm" ], +[ 4566, "fsn" ], +[ 4567, "fso" ], +[ 4568, "fsp" ], +[ 4569, "fsq" ], +[ 4570, "fsr" ], +[ 4571, "fss" ], +[ 4572, "fst" ], +[ 4573, "fsu" ], +[ 4574, "fsv" ], +[ 4575, "fsw" ], +[ 4576, "fsx" ], +[ 4577, "fsy" ], +[ 4578, "fsz" ], +[ 4579, "fta" ], +[ 4580, "ftb" ], +[ 4581, "ftc" ], +[ 4582, "ftd" ], +[ 4583, "fte" ], +[ 4584, "ftf" ], +[ 4585, "ftg" ], +[ 4586, "fth" ], +[ 4587, "fti" ], +[ 4588, "ftj" ], +[ 4589, "ftk" ], +[ 4590, "ftl" ], +[ 4591, "ftm" ], +[ 4592, "ftn" ], +[ 4593, "fto" ], +[ 4594, "ftp" ], +[ 4595, "ftq" ], +[ 4596, "ftr" ], +[ 4597, "fts" ], +[ 4598, "ftt" ], +[ 4599, "ftu" ], +[ 4600, "ftv" ], +[ 4601, "ftw" ], +[ 4602, "ftx" ], +[ 4603, "fty" ], +[ 4604, "ftz" ], +[ 4605, "fua" ], +[ 4606, "fub" ], +[ 4607, "fuc" ], +[ 4608, "fud" ], +[ 4609, "fue" ], +[ 4610, "fuf" ], +[ 4611, "fug" ], +[ 4612, "fuh" ], +[ 4613, "fui" ], +[ 4614, "fuj" ], +[ 4615, "fuk" ], +[ 4616, "ful" ], +[ 4617, "fum" ], +[ 4618, "fun" ], +[ 4619, "fuo" ], +[ 4620, "fup" ], +[ 4621, "fuq" ], +[ 4622, "fur" ], +[ 4623, "fus" ], +[ 4624, "fut" ], +[ 4625, "fuu" ], +[ 4626, "fuv" ], +[ 4627, "fuw" ], +[ 4628, "fux" ], +[ 4629, "fuy" ], +[ 4630, "fuz" ], +[ 4631, "fva" ], +[ 4632, "fvb" ], +[ 4633, "fvc" ], +[ 4634, "fvd" ], +[ 4635, "fve" ], +[ 4636, "fvf" ], +[ 4637, "fvg" ], +[ 4638, "fvh" ], +[ 4639, "fvi" ], +[ 4640, "fvj" ], +[ 4641, "fvk" ], +[ 4642, "fvl" ], +[ 4643, "fvm" ], +[ 4644, "fvn" ], +[ 4645, "fvo" ], +[ 4646, "fvp" ], +[ 4647, "fvq" ], +[ 4648, "fvr" ], +[ 4649, "fvs" ], +[ 4650, "fvt" ], +[ 4651, "fvu" ], +[ 4652, "fvv" ], +[ 4653, "fvw" ], +[ 4654, "fvx" ], +[ 4655, "fvy" ], +[ 4656, "fvz" ], +[ 4657, "fwa" ], +[ 4658, "fwb" ], +[ 4659, "fwc" ], +[ 4660, "fwd" ], +[ 4661, "fwe" ], +[ 4662, "fwf" ], +[ 4663, "fwg" ], +[ 4664, "fwh" ], +[ 4665, "fwi" ], +[ 4666, "fwj" ], +[ 4667, "fwk" ], +[ 4668, "fwl" ], +[ 4669, "fwm" ], +[ 4670, "fwn" ], +[ 4671, "fwo" ], +[ 4672, "fwp" ], +[ 4673, "fwq" ], +[ 4674, "fwr" ], +[ 4675, "fws" ], +[ 4676, "fwt" ], +[ 4677, "fwu" ], +[ 4678, "fwv" ], +[ 4679, "fww" ], +[ 4680, "fwx" ], +[ 4681, "fwy" ], +[ 4682, "fwz" ], +[ 4683, "fxa" ], +[ 4684, "fxb" ], +[ 4685, "fxc" ], +[ 4686, "fxd" ], +[ 4687, "fxe" ], +[ 4688, "fxf" ], +[ 4689, "fxg" ], +[ 4690, "fxh" ], +[ 4691, "fxi" ], +[ 4692, "fxj" ], +[ 4693, "fxk" ], +[ 4694, "fxl" ], +[ 4695, "fxm" ], +[ 4696, "fxn" ], +[ 4697, "fxo" ], +[ 4698, "fxp" ], +[ 4699, "fxq" ], +[ 4700, "fxr" ], +[ 4701, "fxs" ], +[ 4702, "fxt" ], +[ 4703, "fxu" ], +[ 4704, "fxv" ], +[ 4705, "fxw" ], +[ 4706, "fxx" ], +[ 4707, "fxy" ], +[ 4708, "fxz" ], +[ 4709, "fya" ], +[ 4710, "fyb" ], +[ 4711, "fyc" ], +[ 4712, "fyd" ], +[ 4713, "fye" ], +[ 4714, "fyf" ], +[ 4715, "fyg" ], +[ 4716, "fyh" ], +[ 4717, "fyi" ], +[ 4718, "fyj" ], +[ 4719, "fyk" ], +[ 4720, "fyl" ], +[ 4721, "fym" ], +[ 4722, "fyn" ], +[ 4723, "fyo" ], +[ 4724, "fyp" ], +[ 4725, "fyq" ], +[ 4726, "fyr" ], +[ 4727, "fys" ], +[ 4728, "fyt" ], +[ 4729, "fyu" ], +[ 4730, "fyv" ], +[ 4731, "fyw" ], +[ 4732, "fyx" ], +[ 4733, "fyy" ], +[ 4734, "fyz" ], +[ 4735, "fza" ], +[ 4736, "fzb" ], +[ 4737, "fzc" ], +[ 4738, "fzd" ], +[ 4739, "fze" ], +[ 4740, "fzf" ], +[ 4741, "fzg" ], +[ 4742, "fzh" ], +[ 4743, "fzi" ], +[ 4744, "fzj" ], +[ 4745, "fzk" ], +[ 4746, "fzl" ], +[ 4747, "fzm" ], +[ 4748, "fzn" ], +[ 4749, "fzo" ], +[ 4750, "fzp" ], +[ 4751, "fzq" ], +[ 4752, "fzr" ], +[ 4753, "fzs" ], +[ 4754, "fzt" ], +[ 4755, "fzu" ], +[ 4756, "fzv" ], +[ 4757, "fzw" ], +[ 4758, "fzx" ], +[ 4759, "fzy" ], +[ 4760, "fzz" ], +[ 4761, "gaa" ], +[ 4762, "gab" ], +[ 4763, "gac" ], +[ 4764, "gad" ], +[ 4765, "gae" ], +[ 4766, "gaf" ], +[ 4767, "gag" ], +[ 4768, "gah" ], +[ 4769, "gai" ], +[ 4770, "gaj" ], +[ 4771, "gak" ], +[ 4772, "gal" ], +[ 4773, "gam" ], +[ 4774, "gan" ], +[ 4775, "gao" ], +[ 4776, "gap" ], +[ 4777, "gaq" ], +[ 4778, "gar" ], +[ 4779, "gas" ], +[ 4780, "gat" ], +[ 4781, "gau" ], +[ 4782, "gav" ], +[ 4783, "gaw" ], +[ 4784, "gax" ], +[ 4785, "gay" ], +[ 4786, "gaz" ], +[ 4787, "gba" ], +[ 4788, "gbb" ], +[ 4789, "gbc" ], +[ 4790, "gbd" ], +[ 4791, "gbe" ], +[ 4792, "gbf" ], +[ 4793, "gbg" ], +[ 4794, "gbh" ], +[ 4795, "gbi" ], +[ 4796, "gbj" ], +[ 4797, "gbk" ], +[ 4798, "gbl" ], +[ 4799, "gbm" ], +[ 4800, "gbn" ], +[ 4801, "gbo" ], +[ 4802, "gbp" ], +[ 4803, "gbq" ], +[ 4804, "gbr" ], +[ 4805, "gbs" ], +[ 4806, "gbt" ], +[ 4807, "gbu" ], +[ 4808, "gbv" ], +[ 4809, "gbw" ], +[ 4810, "gbx" ], +[ 4811, "gby" ], +[ 4812, "gbz" ], +[ 4813, "gca" ], +[ 4814, "gcb" ], +[ 4815, "gcc" ], +[ 4816, "gcd" ], +[ 4817, "gce" ], +[ 4818, "gcf" ], +[ 4819, "gcg" ], +[ 4820, "gch" ], +[ 4821, "gci" ], +[ 4822, "gcj" ], +[ 4823, "gck" ], +[ 4824, "gcl" ], +[ 4825, "gcm" ], +[ 4826, "gcn" ], +[ 4827, "gco" ], +[ 4828, "gcp" ], +[ 4829, "gcq" ], +[ 4830, "gcr" ], +[ 4831, "gcs" ], +[ 4832, "gct" ], +[ 4833, "gcu" ], +[ 4834, "gcv" ], +[ 4835, "gcw" ], +[ 4836, "gcx" ], +[ 4837, "gcy" ], +[ 4838, "gcz" ], +[ 4839, "gda" ], +[ 4840, "gdb" ], +[ 4841, "gdc" ], +[ 4842, "gdd" ], +[ 4843, "gde" ], +[ 4844, "gdf" ], +[ 4845, "gdg" ], +[ 4846, "gdh" ], +[ 4847, "gdi" ], +[ 4848, "gdj" ], +[ 4849, "gdk" ], +[ 4850, "gdl" ], +[ 4851, "gdm" ], +[ 4852, "gdn" ], +[ 4853, "gdo" ], +[ 4854, "gdp" ], +[ 4855, "gdq" ], +[ 4856, "gdr" ], +[ 4857, "gds" ], +[ 4858, "gdt" ], +[ 4859, "gdu" ], +[ 4860, "gdv" ], +[ 4861, "gdw" ], +[ 4862, "gdx" ], +[ 4863, "gdy" ], +[ 4864, "gdz" ], +[ 4865, "gea" ], +[ 4866, "geb" ], +[ 4867, "gec" ], +[ 4868, "ged" ], +[ 4869, "gee" ], +[ 4870, "gef" ], +[ 4871, "geg" ], +[ 4872, "geh" ], +[ 4873, "gei" ], +[ 4874, "gej" ], +[ 4875, "gek" ], +[ 4876, "gel" ], +[ 4877, "gem" ], +[ 4878, "gen" ], +[ 4879, "geo" ], +[ 4880, "gep" ], +[ 4881, "geq" ], +[ 4882, "ger" ], +[ 4883, "ges" ], +[ 4884, "get" ], +[ 4885, "geu" ], +[ 4886, "gev" ], +[ 4887, "gew" ], +[ 4888, "gex" ], +[ 4889, "gey" ], +[ 4890, "gez" ], +[ 4891, "gfa" ], +[ 4892, "gfb" ], +[ 4893, "gfc" ], +[ 4894, "gfd" ], +[ 4895, "gfe" ], +[ 4896, "gff" ], +[ 4897, "gfg" ], +[ 4898, "gfh" ], +[ 4899, "gfi" ], +[ 4900, "gfj" ], +[ 4901, "gfk" ], +[ 4902, "gfl" ], +[ 4903, "gfm" ], +[ 4904, "gfn" ], +[ 4905, "gfo" ], +[ 4906, "gfp" ], +[ 4907, "gfq" ], +[ 4908, "gfr" ], +[ 4909, "gfs" ], +[ 4910, "gft" ], +[ 4911, "gfu" ], +[ 4912, "gfv" ], +[ 4913, "gfw" ], +[ 4914, "gfx" ], +[ 4915, "gfy" ], +[ 4916, "gfz" ], +[ 4917, "gga" ], +[ 4918, "ggb" ], +[ 4919, "ggc" ], +[ 4920, "ggd" ], +[ 4921, "gge" ], +[ 4922, "ggf" ], +[ 4923, "ggg" ], +[ 4924, "ggh" ], +[ 4925, "ggi" ], +[ 4926, "ggj" ], +[ 4927, "ggk" ], +[ 4928, "ggl" ], +[ 4929, "ggm" ], +[ 4930, "ggn" ], +[ 4931, "ggo" ], +[ 4932, "ggp" ], +[ 4933, "ggq" ], +[ 4934, "ggr" ], +[ 4935, "ggs" ], +[ 4936, "ggt" ], +[ 4937, "ggu" ], +[ 4938, "ggv" ], +[ 4939, "ggw" ], +[ 4940, "ggx" ], +[ 4941, "ggy" ], +[ 4942, "ggz" ], +[ 4943, "gha" ], +[ 4944, "ghb" ], +[ 4945, "ghc" ], +[ 4946, "ghd" ], +[ 4947, "ghe" ], +[ 4948, "ghf" ], +[ 4949, "ghg" ], +[ 4950, "ghh" ], +[ 4951, "ghi" ], +[ 4952, "ghj" ], +[ 4953, "ghk" ], +[ 4954, "ghl" ], +[ 4955, "ghm" ], +[ 4956, "ghn" ], +[ 4957, "gho" ], +[ 4958, "ghp" ], +[ 4959, "ghq" ], +[ 4960, "ghr" ], +[ 4961, "ghs" ], +[ 4962, "ght" ], +[ 4963, "ghu" ], +[ 4964, "ghv" ], +[ 4965, "ghw" ], +[ 4966, "ghx" ], +[ 4967, "ghy" ], +[ 4968, "ghz" ], +[ 4969, "gia" ], +[ 4970, "gib" ], +[ 4971, "gic" ], +[ 4972, "gid" ], +[ 4973, "gie" ], +[ 4974, "gif" ], +[ 4975, "gig" ], +[ 4976, "gih" ], +[ 4977, "gii" ], +[ 4978, "gij" ], +[ 4979, "gik" ], +[ 4980, "gil" ], +[ 4981, "gim" ], +[ 4982, "gin" ], +[ 4983, "gio" ], +[ 4984, "gip" ], +[ 4985, "giq" ], +[ 4986, "gir" ], +[ 4987, "gis" ], +[ 4988, "git" ], +[ 4989, "giu" ], +[ 4990, "giv" ], +[ 4991, "giw" ], +[ 4992, "gix" ], +[ 4993, "giy" ], +[ 4994, "giz" ], +[ 4995, "gja" ], +[ 4996, "gjb" ], +[ 4997, "gjc" ], +[ 4998, "gjd" ], +[ 4999, "gje" ], +[ 5000, "gjf" ], +[ 5001, "gjg" ], +[ 5002, "gjh" ], +[ 5003, "gji" ], +[ 5004, "gjj" ], +[ 5005, "gjk" ], +[ 5006, "gjl" ], +[ 5007, "gjm" ], +[ 5008, "gjn" ], +[ 5009, "gjo" ], +[ 5010, "gjp" ], +[ 5011, "gjq" ], +[ 5012, "gjr" ], +[ 5013, "gjs" ], +[ 5014, "gjt" ], +[ 5015, "gju" ], +[ 5016, "gjv" ], +[ 5017, "gjw" ], +[ 5018, "gjx" ], +[ 5019, "gjy" ], +[ 5020, "gjz" ], +[ 5021, "gka" ], +[ 5022, "gkb" ], +[ 5023, "gkc" ], +[ 5024, "gkd" ], +[ 5025, "gke" ], +[ 5026, "gkf" ], +[ 5027, "gkg" ], +[ 5028, "gkh" ], +[ 5029, "gki" ], +[ 5030, "gkj" ], +[ 5031, "gkk" ], +[ 5032, "gkl" ], +[ 5033, "gkm" ], +[ 5034, "gkn" ], +[ 5035, "gko" ], +[ 5036, "gkp" ], +[ 5037, "gkq" ], +[ 5038, "gkr" ], +[ 5039, "gks" ], +[ 5040, "gkt" ], +[ 5041, "gku" ], +[ 5042, "gkv" ], +[ 5043, "gkw" ], +[ 5044, "gkx" ], +[ 5045, "gky" ], +[ 5046, "gkz" ], +[ 5047, "gla" ], +[ 5048, "glb" ], +[ 5049, "glc" ], +[ 5050, "gld" ], +[ 5051, "gle" ], +[ 5052, "glf" ], +[ 5053, "glg" ], +[ 5054, "glh" ], +[ 5055, "gli" ], +[ 5056, "glj" ], +[ 5057, "glk" ], +[ 5058, "gll" ], +[ 5059, "glm" ], +[ 5060, "gln" ], +[ 5061, "glo" ], +[ 5062, "glp" ], +[ 5063, "glq" ], +[ 5064, "glr" ], +[ 5065, "gls" ], +[ 5066, "glt" ], +[ 5067, "glu" ], +[ 5068, "glv" ], +[ 5069, "glw" ], +[ 5070, "glx" ], +[ 5071, "gly" ], +[ 5072, "glz" ], +[ 5073, "gma" ], +[ 5074, "gmb" ], +[ 5075, "gmc" ], +[ 5076, "gmd" ], +[ 5077, "gme" ], +[ 5078, "gmf" ], +[ 5079, "gmg" ], +[ 5080, "gmh" ], +[ 5081, "gmi" ], +[ 5082, "gmj" ], +[ 5083, "gmk" ], +[ 5084, "gml" ], +[ 5085, "gmm" ], +[ 5086, "gmn" ], +[ 5087, "gmo" ], +[ 5088, "gmp" ], +[ 5089, "gmq" ], +[ 5090, "gmr" ], +[ 5091, "gms" ], +[ 5092, "gmt" ], +[ 5093, "gmu" ], +[ 5094, "gmv" ], +[ 5095, "gmw" ], +[ 5096, "gmx" ], +[ 5097, "gmy" ], +[ 5098, "gmz" ], +[ 5099, "gna" ], +[ 5100, "gnb" ], +[ 5101, "gnc" ], +[ 5102, "gnd" ], +[ 5103, "gne" ], +[ 5104, "gnf" ], +[ 5105, "gng" ], +[ 5106, "gnh" ], +[ 5107, "gni" ], +[ 5108, "gnj" ], +[ 5109, "gnk" ], +[ 5110, "gnl" ], +[ 5111, "gnm" ], +[ 5112, "gnn" ], +[ 5113, "gno" ], +[ 5114, "gnp" ], +[ 5115, "gnq" ], +[ 5116, "gnr" ], +[ 5117, "gns" ], +[ 5118, "gnt" ], +[ 5119, "gnu" ], +[ 5120, "gnv" ], +[ 5121, "gnw" ], +[ 5122, "gnx" ], +[ 5123, "gny" ], +[ 5124, "gnz" ], +[ 5125, "goa" ], +[ 5126, "gob" ], +[ 5127, "goc" ], +[ 5128, "god" ], +[ 5129, "goe" ], +[ 5130, "gof" ], +[ 5131, "gog" ], +[ 5132, "goh" ], +[ 5133, "goi" ], +[ 5134, "goj" ], +[ 5135, "gok" ], +[ 5136, "gol" ], +[ 5137, "gom" ], +[ 5138, "gon" ], +[ 5139, "goo" ], +[ 5140, "gop" ], +[ 5141, "goq" ], +[ 5142, "gor" ], +[ 5143, "gos" ], +[ 5144, "got" ], +[ 5145, "gou" ], +[ 5146, "gov" ], +[ 5147, "gow" ], +[ 5148, "gox" ], +[ 5149, "goy" ], +[ 5150, "goz" ], +[ 5151, "gpa" ], +[ 5152, "gpb" ], +[ 5153, "gpc" ], +[ 5154, "gpd" ], +[ 5155, "gpe" ], +[ 5156, "gpf" ], +[ 5157, "gpg" ], +[ 5158, "gph" ], +[ 5159, "gpi" ], +[ 5160, "gpj" ], +[ 5161, "gpk" ], +[ 5162, "gpl" ], +[ 5163, "gpm" ], +[ 5164, "gpn" ], +[ 5165, "gpo" ], +[ 5166, "gpp" ], +[ 5167, "gpq" ], +[ 5168, "gpr" ], +[ 5169, "gps" ], +[ 5170, "gpt" ], +[ 5171, "gpu" ], +[ 5172, "gpv" ], +[ 5173, "gpw" ], +[ 5174, "gpx" ], +[ 5175, "gpy" ], +[ 5176, "gpz" ], +[ 5177, "gqa" ], +[ 5178, "gqb" ], +[ 5179, "gqc" ], +[ 5180, "gqd" ], +[ 5181, "gqe" ], +[ 5182, "gqf" ], +[ 5183, "gqg" ], +[ 5184, "gqh" ], +[ 5185, "gqi" ], +[ 5186, "gqj" ], +[ 5187, "gqk" ], +[ 5188, "gql" ], +[ 5189, "gqm" ], +[ 5190, "gqn" ], +[ 5191, "gqo" ], +[ 5192, "gqp" ], +[ 5193, "gqq" ], +[ 5194, "gqr" ], +[ 5195, "gqs" ], +[ 5196, "gqt" ], +[ 5197, "gqu" ], +[ 5198, "gqv" ], +[ 5199, "gqw" ], +[ 5200, "gqx" ], +[ 5201, "gqy" ], +[ 5202, "gqz" ], +[ 5203, "gra" ], +[ 5204, "grb" ], +[ 5205, "grc" ], +[ 5206, "grd" ], +[ 5207, "gre" ], +[ 5208, "grf" ], +[ 5209, "grg" ], +[ 5210, "grh" ], +[ 5211, "gri" ], +[ 5212, "grj" ], +[ 5213, "grk" ], +[ 5214, "grl" ], +[ 5215, "grm" ], +[ 5216, "grn" ], +[ 5217, "gro" ], +[ 5218, "grp" ], +[ 5219, "grq" ], +[ 5220, "grr" ], +[ 5221, "grs" ], +[ 5222, "grt" ], +[ 5223, "gru" ], +[ 5224, "grv" ], +[ 5225, "grw" ], +[ 5226, "grx" ], +[ 5227, "gry" ], +[ 5228, "grz" ], +[ 5229, "gsa" ], +[ 5230, "gsb" ], +[ 5231, "gsc" ], +[ 5232, "gsd" ], +[ 5233, "gse" ], +[ 5234, "gsf" ], +[ 5235, "gsg" ], +[ 5236, "gsh" ], +[ 5237, "gsi" ], +[ 5238, "gsj" ], +[ 5239, "gsk" ], +[ 5240, "gsl" ], +[ 5241, "gsm" ], +[ 5242, "gsn" ], +[ 5243, "gso" ], +[ 5244, "gsp" ], +[ 5245, "gsq" ], +[ 5246, "gsr" ], +[ 5247, "gss" ], +[ 5248, "gst" ], +[ 5249, "gsu" ], +[ 5250, "gsv" ], +[ 5251, "gsw" ], +[ 5252, "gsx" ], +[ 5253, "gsy" ], +[ 5254, "gsz" ], +[ 5255, "gta" ], +[ 5256, "gtb" ], +[ 5257, "gtc" ], +[ 5258, "gtd" ], +[ 5259, "gte" ], +[ 5260, "gtf" ], +[ 5261, "gtg" ], +[ 5262, "gth" ], +[ 5263, "gti" ], +[ 5264, "gtj" ], +[ 5265, "gtk" ], +[ 5266, "gtl" ], +[ 5267, "gtm" ], +[ 5268, "gtn" ], +[ 5269, "gto" ], +[ 5270, "gtp" ], +[ 5271, "gtq" ], +[ 5272, "gtr" ], +[ 5273, "gts" ], +[ 5274, "gtt" ], +[ 5275, "gtu" ], +[ 5276, "gtv" ], +[ 5277, "gtw" ], +[ 5278, "gtx" ], +[ 5279, "gty" ], +[ 5280, "gtz" ], +[ 5281, "gua" ], +[ 5282, "gub" ], +[ 5283, "guc" ], +[ 5284, "gud" ], +[ 5285, "gue" ], +[ 5286, "guf" ], +[ 5287, "gug" ], +[ 5288, "guh" ], +[ 5289, "gui" ], +[ 5290, "guj" ], +[ 5291, "guk" ], +[ 5292, "gul" ], +[ 5293, "gum" ], +[ 5294, "gun" ], +[ 5295, "guo" ], +[ 5296, "gup" ], +[ 5297, "guq" ], +[ 5298, "gur" ], +[ 5299, "gus" ], +[ 5300, "gut" ], +[ 5301, "guu" ], +[ 5302, "guv" ], +[ 5303, "guw" ], +[ 5304, "gux" ], +[ 5305, "guy" ], +[ 5306, "guz" ], +[ 5307, "gva" ], +[ 5308, "gvb" ], +[ 5309, "gvc" ], +[ 5310, "gvd" ], +[ 5311, "gve" ], +[ 5312, "gvf" ], +[ 5313, "gvg" ], +[ 5314, "gvh" ], +[ 5315, "gvi" ], +[ 5316, "gvj" ], +[ 5317, "gvk" ], +[ 5318, "gvl" ], +[ 5319, "gvm" ], +[ 5320, "gvn" ], +[ 5321, "gvo" ], +[ 5322, "gvp" ], +[ 5323, "gvq" ], +[ 5324, "gvr" ], +[ 5325, "gvs" ], +[ 5326, "gvt" ], +[ 5327, "gvu" ], +[ 5328, "gvv" ], +[ 5329, "gvw" ], +[ 5330, "gvx" ], +[ 5331, "gvy" ], +[ 5332, "gvz" ], +[ 5333, "gwa" ], +[ 5334, "gwb" ], +[ 5335, "gwc" ], +[ 5336, "gwd" ], +[ 5337, "gwe" ], +[ 5338, "gwf" ], +[ 5339, "gwg" ], +[ 5340, "gwh" ], +[ 5341, "gwi" ], +[ 5342, "gwj" ], +[ 5343, "gwk" ], +[ 5344, "gwl" ], +[ 5345, "gwm" ], +[ 5346, "gwn" ], +[ 5347, "gwo" ], +[ 5348, "gwp" ], +[ 5349, "gwq" ], +[ 5350, "gwr" ], +[ 5351, "gws" ], +[ 5352, "gwt" ], +[ 5353, "gwu" ], +[ 5354, "gwv" ], +[ 5355, "gww" ], +[ 5356, "gwx" ], +[ 5357, "gwy" ], +[ 5358, "gwz" ], +[ 5359, "gxa" ], +[ 5360, "gxb" ], +[ 5361, "gxc" ], +[ 5362, "gxd" ], +[ 5363, "gxe" ], +[ 5364, "gxf" ], +[ 5365, "gxg" ], +[ 5366, "gxh" ], +[ 5367, "gxi" ], +[ 5368, "gxj" ], +[ 5369, "gxk" ], +[ 5370, "gxl" ], +[ 5371, "gxm" ], +[ 5372, "gxn" ], +[ 5373, "gxo" ], +[ 5374, "gxp" ], +[ 5375, "gxq" ], +[ 5376, "gxr" ], +[ 5377, "gxs" ], +[ 5378, "gxt" ], +[ 5379, "gxu" ], +[ 5380, "gxv" ], +[ 5381, "gxw" ], +[ 5382, "gxx" ], +[ 5383, "gxy" ], +[ 5384, "gxz" ], +[ 5385, "gya" ], +[ 5386, "gyb" ], +[ 5387, "gyc" ], +[ 5388, "gyd" ], +[ 5389, "gye" ], +[ 5390, "gyf" ], +[ 5391, "gyg" ], +[ 5392, "gyh" ], +[ 5393, "gyi" ], +[ 5394, "gyj" ], +[ 5395, "gyk" ], +[ 5396, "gyl" ], +[ 5397, "gym" ], +[ 5398, "gyn" ], +[ 5399, "gyo" ], +[ 5400, "gyp" ], +[ 5401, "gyq" ], +[ 5402, "gyr" ], +[ 5403, "gys" ], +[ 5404, "gyt" ], +[ 5405, "gyu" ], +[ 5406, "gyv" ], +[ 5407, "gyw" ], +[ 5408, "gyx" ], +[ 5409, "gyy" ], +[ 5410, "gyz" ], +[ 5411, "gza" ], +[ 5412, "gzb" ], +[ 5413, "gzc" ], +[ 5414, "gzd" ], +[ 5415, "gze" ], +[ 5416, "gzf" ], +[ 5417, "gzg" ], +[ 5418, "gzh" ], +[ 5419, "gzi" ], +[ 5420, "gzj" ], +[ 5421, "gzk" ], +[ 5422, "gzl" ], +[ 5423, "gzm" ], +[ 5424, "gzn" ], +[ 5425, "gzo" ], +[ 5426, "gzp" ], +[ 5427, "gzq" ], +[ 5428, "gzr" ], +[ 5429, "gzs" ], +[ 5430, "gzt" ], +[ 5431, "gzu" ], +[ 5432, "gzv" ], +[ 5433, "gzw" ], +[ 5434, "gzx" ], +[ 5435, "gzy" ], +[ 5436, "gzz" ], +[ 5437, "haa" ], +[ 5438, "hab" ], +[ 5439, "hac" ], +[ 5440, "had" ], +[ 5441, "hae" ], +[ 5442, "haf" ], +[ 5443, "hag" ], +[ 5444, "hah" ], +[ 5445, "hai" ], +[ 5446, "haj" ], +[ 5447, "hak" ], +[ 5448, "hal" ], +[ 5449, "ham" ], +[ 5450, "han" ], +[ 5451, "hao" ], +[ 5452, "hap" ], +[ 5453, "haq" ], +[ 5454, "har" ], +[ 5455, "has" ], +[ 5456, "hat" ], +[ 5457, "hau" ], +[ 5458, "hav" ], +[ 5459, "haw" ], +[ 5460, "hax" ], +[ 5461, "hay" ], +[ 5462, "haz" ], +[ 5463, "hba" ], +[ 5464, "hbb" ], +[ 5465, "hbc" ], +[ 5466, "hbd" ], +[ 5467, "hbe" ], +[ 5468, "hbf" ], +[ 5469, "hbg" ], +[ 5470, "hbh" ], +[ 5471, "hbi" ], +[ 5472, "hbj" ], +[ 5473, "hbk" ], +[ 5474, "hbl" ], +[ 5475, "hbm" ], +[ 5476, "hbn" ], +[ 5477, "hbo" ], +[ 5478, "hbp" ], +[ 5479, "hbq" ], +[ 5480, "hbr" ], +[ 5481, "hbs" ], +[ 5482, "hbt" ], +[ 5483, "hbu" ], +[ 5484, "hbv" ], +[ 5485, "hbw" ], +[ 5486, "hbx" ], +[ 5487, "hby" ], +[ 5488, "hbz" ], +[ 5489, "hca" ], +[ 5490, "hcb" ], +[ 5491, "hcc" ], +[ 5492, "hcd" ], +[ 5493, "hce" ], +[ 5494, "hcf" ], +[ 5495, "hcg" ], +[ 5496, "hch" ], +[ 5497, "hci" ], +[ 5498, "hcj" ], +[ 5499, "hck" ], +[ 5500, "hcl" ], +[ 5501, "hcm" ], +[ 5502, "hcn" ], +[ 5503, "hco" ], +[ 5504, "hcp" ], +[ 5505, "hcq" ], +[ 5506, "hcr" ], +[ 5507, "hcs" ], +[ 5508, "hct" ], +[ 5509, "hcu" ], +[ 5510, "hcv" ], +[ 5511, "hcw" ], +[ 5512, "hcx" ], +[ 5513, "hcy" ], +[ 5514, "hcz" ], +[ 5515, "hda" ], +[ 5516, "hdb" ], +[ 5517, "hdc" ], +[ 5518, "hdd" ], +[ 5519, "hde" ], +[ 5520, "hdf" ], +[ 5521, "hdg" ], +[ 5522, "hdh" ], +[ 5523, "hdi" ], +[ 5524, "hdj" ], +[ 5525, "hdk" ], +[ 5526, "hdl" ], +[ 5527, "hdm" ], +[ 5528, "hdn" ], +[ 5529, "hdo" ], +[ 5530, "hdp" ], +[ 5531, "hdq" ], +[ 5532, "hdr" ], +[ 5533, "hds" ], +[ 5534, "hdt" ], +[ 5535, "hdu" ], +[ 5536, "hdv" ], +[ 5537, "hdw" ], +[ 5538, "hdx" ], +[ 5539, "hdy" ], +[ 5540, "hdz" ], +[ 5541, "hea" ], +[ 5542, "heb" ], +[ 5543, "hec" ], +[ 5544, "hed" ], +[ 5545, "hee" ], +[ 5546, "hef" ], +[ 5547, "heg" ], +[ 5548, "heh" ], +[ 5549, "hei" ], +[ 5550, "hej" ], +[ 5551, "hek" ], +[ 5552, "hel" ], +[ 5553, "hem" ], +[ 5554, "hen" ], +[ 5555, "heo" ], +[ 5556, "hep" ], +[ 5557, "heq" ], +[ 5558, "her" ], +[ 5559, "hes" ], +[ 5560, "het" ], +[ 5561, "heu" ], +[ 5562, "hev" ], +[ 5563, "hew" ], +[ 5564, "hex" ], +[ 5565, "hey" ], +[ 5566, "hez" ], +[ 5567, "hfa" ], +[ 5568, "hfb" ], +[ 5569, "hfc" ], +[ 5570, "hfd" ], +[ 5571, "hfe" ], +[ 5572, "hff" ], +[ 5573, "hfg" ], +[ 5574, "hfh" ], +[ 5575, "hfi" ], +[ 5576, "hfj" ], +[ 5577, "hfk" ], +[ 5578, "hfl" ], +[ 5579, "hfm" ], +[ 5580, "hfn" ], +[ 5581, "hfo" ], +[ 5582, "hfp" ], +[ 5583, "hfq" ], +[ 5584, "hfr" ], +[ 5585, "hfs" ], +[ 5586, "hft" ], +[ 5587, "hfu" ], +[ 5588, "hfv" ], +[ 5589, "hfw" ], +[ 5590, "hfx" ], +[ 5591, "hfy" ], +[ 5592, "hfz" ], +[ 5593, "hga" ], +[ 5594, "hgb" ], +[ 5595, "hgc" ], +[ 5596, "hgd" ], +[ 5597, "hge" ], +[ 5598, "hgf" ], +[ 5599, "hgg" ], +[ 5600, "hgh" ], +[ 5601, "hgi" ], +[ 5602, "hgj" ], +[ 5603, "hgk" ], +[ 5604, "hgl" ], +[ 5605, "hgm" ], +[ 5606, "hgn" ], +[ 5607, "hgo" ], +[ 5608, "hgp" ], +[ 5609, "hgq" ], +[ 5610, "hgr" ], +[ 5611, "hgs" ], +[ 5612, "hgt" ], +[ 5613, "hgu" ], +[ 5614, "hgv" ], +[ 5615, "hgw" ], +[ 5616, "hgx" ], +[ 5617, "hgy" ], +[ 5618, "hgz" ], +[ 5619, "hha" ], +[ 5620, "hhb" ], +[ 5621, "hhc" ], +[ 5622, "hhd" ], +[ 5623, "hhe" ], +[ 5624, "hhf" ], +[ 5625, "hhg" ], +[ 5626, "hhh" ], +[ 5627, "hhi" ], +[ 5628, "hhj" ], +[ 5629, "hhk" ], +[ 5630, "hhl" ], +[ 5631, "hhm" ], +[ 5632, "hhn" ], +[ 5633, "hho" ], +[ 5634, "hhp" ], +[ 5635, "hhq" ], +[ 5636, "hhr" ], +[ 5637, "hhs" ], +[ 5638, "hht" ], +[ 5639, "hhu" ], +[ 5640, "hhv" ], +[ 5641, "hhw" ], +[ 5642, "hhx" ], +[ 5643, "hhy" ], +[ 5644, "hhz" ], +[ 5645, "hia" ], +[ 5646, "hib" ], +[ 5647, "hic" ], +[ 5648, "hid" ], +[ 5649, "hie" ], +[ 5650, "hif" ], +[ 5651, "hig" ], +[ 5652, "hih" ], +[ 5653, "hii" ], +[ 5654, "hij" ], +[ 5655, "hik" ], +[ 5656, "hil" ], +[ 5657, "him" ], +[ 5658, "hin" ], +[ 5659, "hio" ], +[ 5660, "hip" ], +[ 5661, "hiq" ], +[ 5662, "hir" ], +[ 5663, "his" ], +[ 5664, "hit" ], +[ 5665, "hiu" ], +[ 5666, "hiv" ], +[ 5667, "hiw" ], +[ 5668, "hix" ], +[ 5669, "hiy" ], +[ 5670, "hiz" ], +[ 5671, "hja" ], +[ 5672, "hjb" ], +[ 5673, "hjc" ], +[ 5674, "hjd" ], +[ 5675, "hje" ], +[ 5676, "hjf" ], +[ 5677, "hjg" ], +[ 5678, "hjh" ], +[ 5679, "hji" ], +[ 5680, "hjj" ], +[ 5681, "hjk" ], +[ 5682, "hjl" ], +[ 5683, "hjm" ], +[ 5684, "hjn" ], +[ 5685, "hjo" ], +[ 5686, "hjp" ], +[ 5687, "hjq" ], +[ 5688, "hjr" ], +[ 5689, "hjs" ], +[ 5690, "hjt" ], +[ 5691, "hju" ], +[ 5692, "hjv" ], +[ 5693, "hjw" ], +[ 5694, "hjx" ], +[ 5695, "hjy" ], +[ 5696, "hjz" ], +[ 5697, "hka" ], +[ 5698, "hkb" ], +[ 5699, "hkc" ], +[ 5700, "hkd" ], +[ 5701, "hke" ], +[ 5702, "hkf" ], +[ 5703, "hkg" ], +[ 5704, "hkh" ], +[ 5705, "hki" ], +[ 5706, "hkj" ], +[ 5707, "hkk" ], +[ 5708, "hkl" ], +[ 5709, "hkm" ], +[ 5710, "hkn" ], +[ 5711, "hko" ], +[ 5712, "hkp" ], +[ 5713, "hkq" ], +[ 5714, "hkr" ], +[ 5715, "hks" ], +[ 5716, "hkt" ], +[ 5717, "hku" ], +[ 5718, "hkv" ], +[ 5719, "hkw" ], +[ 5720, "hkx" ], +[ 5721, "hky" ], +[ 5722, "hkz" ], +[ 5723, "hla" ], +[ 5724, "hlb" ], +[ 5725, "hlc" ], +[ 5726, "hld" ], +[ 5727, "hle" ], +[ 5728, "hlf" ], +[ 5729, "hlg" ], +[ 5730, "hlh" ], +[ 5731, "hli" ], +[ 5732, "hlj" ], +[ 5733, "hlk" ], +[ 5734, "hll" ], +[ 5735, "hlm" ], +[ 5736, "hln" ], +[ 5737, "hlo" ], +[ 5738, "hlp" ], +[ 5739, "hlq" ], +[ 5740, "hlr" ], +[ 5741, "hls" ], +[ 5742, "hlt" ], +[ 5743, "hlu" ], +[ 5744, "hlv" ], +[ 5745, "hlw" ], +[ 5746, "hlx" ], +[ 5747, "hly" ], +[ 5748, "hlz" ], +[ 5749, "hma" ], +[ 5750, "hmb" ], +[ 5751, "hmc" ], +[ 5752, "hmd" ], +[ 5753, "hme" ], +[ 5754, "hmf" ], +[ 5755, "hmg" ], +[ 5756, "hmh" ], +[ 5757, "hmi" ], +[ 5758, "hmj" ], +[ 5759, "hmk" ], +[ 5760, "hml" ], +[ 5761, "hmm" ], +[ 5762, "hmn" ], +[ 5763, "hmo" ], +[ 5764, "hmp" ], +[ 5765, "hmq" ], +[ 5766, "hmr" ], +[ 5767, "hms" ], +[ 5768, "hmt" ], +[ 5769, "hmu" ], +[ 5770, "hmv" ], +[ 5771, "hmw" ], +[ 5772, "hmx" ], +[ 5773, "hmy" ], +[ 5774, "hmz" ], +[ 5775, "hna" ], +[ 5776, "hnb" ], +[ 5777, "hnc" ], +[ 5778, "hnd" ], +[ 5779, "hne" ], +[ 5780, "hnf" ], +[ 5781, "hng" ], +[ 5782, "hnh" ], +[ 5783, "hni" ], +[ 5784, "hnj" ], +[ 5785, "hnk" ], +[ 5786, "hnl" ], +[ 5787, "hnm" ], +[ 5788, "hnn" ], +[ 5789, "hno" ], +[ 5790, "hnp" ], +[ 5791, "hnq" ], +[ 5792, "hnr" ], +[ 5793, "hns" ], +[ 5794, "hnt" ], +[ 5795, "hnu" ], +[ 5796, "hnv" ], +[ 5797, "hnw" ], +[ 5798, "hnx" ], +[ 5799, "hny" ], +[ 5800, "hnz" ], +[ 5801, "hoa" ], +[ 5802, "hob" ], +[ 5803, "hoc" ], +[ 5804, "hod" ], +[ 5805, "hoe" ], +[ 5806, "hof" ], +[ 5807, "hog" ], +[ 5808, "hoh" ], +[ 5809, "hoi" ], +[ 5810, "hoj" ], +[ 5811, "hok" ], +[ 5812, "hol" ], +[ 5813, "hom" ], +[ 5814, "hon" ], +[ 5815, "hoo" ], +[ 5816, "hop" ], +[ 5817, "hoq" ], +[ 5818, "hor" ], +[ 5819, "hos" ], +[ 5820, "hot" ], +[ 5821, "hou" ], +[ 5822, "hov" ], +[ 5823, "how" ], +[ 5824, "hox" ], +[ 5825, "hoy" ], +[ 5826, "hoz" ], +[ 5827, "hpa" ], +[ 5828, "hpb" ], +[ 5829, "hpc" ], +[ 5830, "hpd" ], +[ 5831, "hpe" ], +[ 5832, "hpf" ], +[ 5833, "hpg" ], +[ 5834, "hph" ], +[ 5835, "hpi" ], +[ 5836, "hpj" ], +[ 5837, "hpk" ], +[ 5838, "hpl" ], +[ 5839, "hpm" ], +[ 5840, "hpn" ], +[ 5841, "hpo" ], +[ 5842, "hpp" ], +[ 5843, "hpq" ], +[ 5844, "hpr" ], +[ 5845, "hps" ], +[ 5846, "hpt" ], +[ 5847, "hpu" ], +[ 5848, "hpv" ], +[ 5849, "hpw" ], +[ 5850, "hpx" ], +[ 5851, "hpy" ], +[ 5852, "hpz" ], +[ 5853, "hqa" ], +[ 5854, "hqb" ], +[ 5855, "hqc" ], +[ 5856, "hqd" ], +[ 5857, "hqe" ], +[ 5858, "hqf" ], +[ 5859, "hqg" ], +[ 5860, "hqh" ], +[ 5861, "hqi" ], +[ 5862, "hqj" ], +[ 5863, "hqk" ], +[ 5864, "hql" ], +[ 5865, "hqm" ], +[ 5866, "hqn" ], +[ 5867, "hqo" ], +[ 5868, "hqp" ], +[ 5869, "hqq" ], +[ 5870, "hqr" ], +[ 5871, "hqs" ], +[ 5872, "hqt" ], +[ 5873, "hqu" ], +[ 5874, "hqv" ], +[ 5875, "hqw" ], +[ 5876, "hqx" ], +[ 5877, "hqy" ], +[ 5878, "hqz" ], +[ 5879, "hra" ], +[ 5880, "hrb" ], +[ 5881, "hrc" ], +[ 5882, "hrd" ], +[ 5883, "hre" ], +[ 5884, "hrf" ], +[ 5885, "hrg" ], +[ 5886, "hrh" ], +[ 5887, "hri" ], +[ 5888, "hrj" ], +[ 5889, "hrk" ], +[ 5890, "hrl" ], +[ 5891, "hrm" ], +[ 5892, "hrn" ], +[ 5893, "hro" ], +[ 5894, "hrp" ], +[ 5895, "hrq" ], +[ 5896, "hrr" ], +[ 5897, "hrs" ], +[ 5898, "hrt" ], +[ 5899, "hru" ], +[ 5900, "hrv" ], +[ 5901, "hrw" ], +[ 5902, "hrx" ], +[ 5903, "hry" ], +[ 5904, "hrz" ], +[ 5905, "hsa" ], +[ 5906, "hsb" ], +[ 5907, "hsc" ], +[ 5908, "hsd" ], +[ 5909, "hse" ], +[ 5910, "hsf" ], +[ 5911, "hsg" ], +[ 5912, "hsh" ], +[ 5913, "hsi" ], +[ 5914, "hsj" ], +[ 5915, "hsk" ], +[ 5916, "hsl" ], +[ 5917, "hsm" ], +[ 5918, "hsn" ], +[ 5919, "hso" ], +[ 5920, "hsp" ], +[ 5921, "hsq" ], +[ 5922, "hsr" ], +[ 5923, "hss" ], +[ 5924, "hst" ], +[ 5925, "hsu" ], +[ 5926, "hsv" ], +[ 5927, "hsw" ], +[ 5928, "hsx" ], +[ 5929, "hsy" ], +[ 5930, "hsz" ], +[ 5931, "hta" ], +[ 5932, "htb" ], +[ 5933, "htc" ], +[ 5934, "htd" ], +[ 5935, "hte" ], +[ 5936, "htf" ], +[ 5937, "htg" ], +[ 5938, "hth" ], +[ 5939, "hti" ], +[ 5940, "htj" ], +[ 5941, "htk" ], +[ 5942, "htl" ], +[ 5943, "htm" ], +[ 5944, "htn" ], +[ 5945, "hto" ], +[ 5946, "htp" ], +[ 5947, "htq" ], +[ 5948, "htr" ], +[ 5949, "hts" ], +[ 5950, "htt" ], +[ 5951, "htu" ], +[ 5952, "htv" ], +[ 5953, "htw" ], +[ 5954, "htx" ], +[ 5955, "hty" ], +[ 5956, "htz" ], +[ 5957, "hua" ], +[ 5958, "hub" ], +[ 5959, "huc" ], +[ 5960, "hud" ], +[ 5961, "hue" ], +[ 5962, "huf" ], +[ 5963, "hug" ], +[ 5964, "huh" ], +[ 5965, "hui" ], +[ 5966, "huj" ], +[ 5967, "huk" ], +[ 5968, "hul" ], +[ 5969, "hum" ], +[ 5970, "hun" ], +[ 5971, "huo" ], +[ 5972, "hup" ], +[ 5973, "huq" ], +[ 5974, "hur" ], +[ 5975, "hus" ], +[ 5976, "hut" ], +[ 5977, "huu" ], +[ 5978, "huv" ], +[ 5979, "huw" ], +[ 5980, "hux" ], +[ 5981, "huy" ], +[ 5982, "huz" ], +[ 5983, "hva" ], +[ 5984, "hvb" ], +[ 5985, "hvc" ], +[ 5986, "hvd" ], +[ 5987, "hve" ], +[ 5988, "hvf" ], +[ 5989, "hvg" ], +[ 5990, "hvh" ], +[ 5991, "hvi" ], +[ 5992, "hvj" ], +[ 5993, "hvk" ], +[ 5994, "hvl" ], +[ 5995, "hvm" ], +[ 5996, "hvn" ], +[ 5997, "hvo" ], +[ 5998, "hvp" ], +[ 5999, "hvq" ], +[ 6000, "hvr" ], +[ 6001, "hvs" ], +[ 6002, "hvt" ], +[ 6003, "hvu" ], +[ 6004, "hvv" ], +[ 6005, "hvw" ], +[ 6006, "hvx" ], +[ 6007, "hvy" ], +[ 6008, "hvz" ], +[ 6009, "hwa" ], +[ 6010, "hwb" ], +[ 6011, "hwc" ], +[ 6012, "hwd" ], +[ 6013, "hwe" ], +[ 6014, "hwf" ], +[ 6015, "hwg" ], +[ 6016, "hwh" ], +[ 6017, "hwi" ], +[ 6018, "hwj" ], +[ 6019, "hwk" ], +[ 6020, "hwl" ], +[ 6021, "hwm" ], +[ 6022, "hwn" ], +[ 6023, "hwo" ], +[ 6024, "hwp" ], +[ 6025, "hwq" ], +[ 6026, "hwr" ], +[ 6027, "hws" ], +[ 6028, "hwt" ], +[ 6029, "hwu" ], +[ 6030, "hwv" ], +[ 6031, "hww" ], +[ 6032, "hwx" ], +[ 6033, "hwy" ], +[ 6034, "hwz" ], +[ 6035, "hxa" ], +[ 6036, "hxb" ], +[ 6037, "hxc" ], +[ 6038, "hxd" ], +[ 6039, "hxe" ], +[ 6040, "hxf" ], +[ 6041, "hxg" ], +[ 6042, "hxh" ], +[ 6043, "hxi" ], +[ 6044, "hxj" ], +[ 6045, "hxk" ], +[ 6046, "hxl" ], +[ 6047, "hxm" ], +[ 6048, "hxn" ], +[ 6049, "hxo" ], +[ 6050, "hxp" ], +[ 6051, "hxq" ], +[ 6052, "hxr" ], +[ 6053, "hxs" ], +[ 6054, "hxt" ], +[ 6055, "hxu" ], +[ 6056, "hxv" ], +[ 6057, "hxw" ], +[ 6058, "hxx" ], +[ 6059, "hxy" ], +[ 6060, "hxz" ], +[ 6061, "hya" ], +[ 6062, "hyb" ], +[ 6063, "hyc" ], +[ 6064, "hyd" ], +[ 6065, "hye" ], +[ 6066, "hyf" ], +[ 6067, "hyg" ], +[ 6068, "hyh" ], +[ 6069, "hyi" ], +[ 6070, "hyj" ], +[ 6071, "hyk" ], +[ 6072, "hyl" ], +[ 6073, "hym" ], +[ 6074, "hyn" ], +[ 6075, "hyo" ], +[ 6076, "hyp" ], +[ 6077, "hyq" ], +[ 6078, "hyr" ], +[ 6079, "hys" ], +[ 6080, "hyt" ], +[ 6081, "hyu" ], +[ 6082, "hyv" ], +[ 6083, "hyw" ], +[ 6084, "hyx" ], +[ 6085, "hyy" ], +[ 6086, "hyz" ], +[ 6087, "hza" ], +[ 6088, "hzb" ], +[ 6089, "hzc" ], +[ 6090, "hzd" ], +[ 6091, "hze" ], +[ 6092, "hzf" ], +[ 6093, "hzg" ], +[ 6094, "hzh" ], +[ 6095, "hzi" ], +[ 6096, "hzj" ], +[ 6097, "hzk" ], +[ 6098, "hzl" ], +[ 6099, "hzm" ], +[ 6100, "hzn" ], +[ 6101, "hzo" ], +[ 6102, "hzp" ], +[ 6103, "hzq" ], +[ 6104, "hzr" ], +[ 6105, "hzs" ], +[ 6106, "hzt" ], +[ 6107, "hzu" ], +[ 6108, "hzv" ], +[ 6109, "hzw" ], +[ 6110, "hzx" ], +[ 6111, "hzy" ], +[ 6112, "hzz" ], +[ 6113, "iaa" ], +[ 6114, "iab" ], +[ 6115, "iac" ], +[ 6116, "iad" ], +[ 6117, "iae" ], +[ 6118, "iaf" ], +[ 6119, "iag" ], +[ 6120, "iah" ], +[ 6121, "iai" ], +[ 6122, "iaj" ], +[ 6123, "iak" ], +[ 6124, "ial" ], +[ 6125, "iam" ], +[ 6126, "ian" ], +[ 6127, "iao" ], +[ 6128, "iap" ], +[ 6129, "iaq" ], +[ 6130, "iar" ], +[ 6131, "ias" ], +[ 6132, "iat" ], +[ 6133, "iau" ], +[ 6134, "iav" ], +[ 6135, "iaw" ], +[ 6136, "iax" ], +[ 6137, "iay" ], +[ 6138, "iaz" ], +[ 6139, "iba" ], +[ 6140, "ibb" ], +[ 6141, "ibc" ], +[ 6142, "ibd" ], +[ 6143, "ibe" ], +[ 6144, "ibf" ], +[ 6145, "ibg" ], +[ 6146, "ibh" ], +[ 6147, "ibi" ], +[ 6148, "ibj" ], +[ 6149, "ibk" ], +[ 6150, "ibl" ], +[ 6151, "ibm" ], +[ 6152, "ibn" ], +[ 6153, "ibo" ], +[ 6154, "ibp" ], +[ 6155, "ibq" ], +[ 6156, "ibr" ], +[ 6157, "ibs" ], +[ 6158, "ibt" ], +[ 6159, "ibu" ], +[ 6160, "ibv" ], +[ 6161, "ibw" ], +[ 6162, "ibx" ], +[ 6163, "iby" ], +[ 6164, "ibz" ], +[ 6165, "ica" ], +[ 6166, "icb" ], +[ 6167, "icc" ], +[ 6168, "icd" ], +[ 6169, "ice" ], +[ 6170, "icf" ], +[ 6171, "icg" ], +[ 6172, "ich" ], +[ 6173, "ici" ], +[ 6174, "icj" ], +[ 6175, "ick" ], +[ 6176, "icl" ], +[ 6177, "icm" ], +[ 6178, "icn" ], +[ 6179, "ico" ], +[ 6180, "icp" ], +[ 6181, "icq" ], +[ 6182, "icr" ], +[ 6183, "ics" ], +[ 6184, "ict" ], +[ 6185, "icu" ], +[ 6186, "icv" ], +[ 6187, "icw" ], +[ 6188, "icx" ], +[ 6189, "icy" ], +[ 6190, "icz" ], +[ 6191, "ida" ], +[ 6192, "idb" ], +[ 6193, "idc" ], +[ 6194, "idd" ], +[ 6195, "ide" ], +[ 6196, "idf" ], +[ 6197, "idg" ], +[ 6198, "idh" ], +[ 6199, "idi" ], +[ 6200, "idj" ], +[ 6201, "idk" ], +[ 6202, "idl" ], +[ 6203, "idm" ], +[ 6204, "idn" ], +[ 6205, "ido" ], +[ 6206, "idp" ], +[ 6207, "idq" ], +[ 6208, "idr" ], +[ 6209, "ids" ], +[ 6210, "idt" ], +[ 6211, "idu" ], +[ 6212, "idv" ], +[ 6213, "idw" ], +[ 6214, "idx" ], +[ 6215, "idy" ], +[ 6216, "idz" ], +[ 6217, "iea" ], +[ 6218, "ieb" ], +[ 6219, "iec" ], +[ 6220, "ied" ], +[ 6221, "iee" ], +[ 6222, "ief" ], +[ 6223, "ieg" ], +[ 6224, "ieh" ], +[ 6225, "iei" ], +[ 6226, "iej" ], +[ 6227, "iek" ], +[ 6228, "iel" ], +[ 6229, "iem" ], +[ 6230, "ien" ], +[ 6231, "ieo" ], +[ 6232, "iep" ], +[ 6233, "ieq" ], +[ 6234, "ier" ], +[ 6235, "ies" ], +[ 6236, "iet" ], +[ 6237, "ieu" ], +[ 6238, "iev" ], +[ 6239, "iew" ], +[ 6240, "iex" ], +[ 6241, "iey" ], +[ 6242, "iez" ], +[ 6243, "ifa" ], +[ 6244, "ifb" ], +[ 6245, "ifc" ], +[ 6246, "ifd" ], +[ 6247, "ife" ], +[ 6248, "iff" ], +[ 6249, "ifg" ], +[ 6250, "ifh" ], +[ 6251, "ifi" ], +[ 6252, "ifj" ], +[ 6253, "ifk" ], +[ 6254, "ifl" ], +[ 6255, "ifm" ], +[ 6256, "ifn" ], +[ 6257, "ifo" ], +[ 6258, "ifp" ], +[ 6259, "ifq" ], +[ 6260, "ifr" ], +[ 6261, "ifs" ], +[ 6262, "ift" ], +[ 6263, "ifu" ], +[ 6264, "ifv" ], +[ 6265, "ifw" ], +[ 6266, "ifx" ], +[ 6267, "ify" ], +[ 6268, "ifz" ], +[ 6269, "iga" ], +[ 6270, "igb" ], +[ 6271, "igc" ], +[ 6272, "igd" ], +[ 6273, "ige" ], +[ 6274, "igf" ], +[ 6275, "igg" ], +[ 6276, "igh" ], +[ 6277, "igi" ], +[ 6278, "igj" ], +[ 6279, "igk" ], +[ 6280, "igl" ], +[ 6281, "igm" ], +[ 6282, "ign" ], +[ 6283, "igo" ], +[ 6284, "igp" ], +[ 6285, "igq" ], +[ 6286, "igr" ], +[ 6287, "igs" ], +[ 6288, "igt" ], +[ 6289, "igu" ], +[ 6290, "igv" ], +[ 6291, "igw" ], +[ 6292, "igx" ], +[ 6293, "igy" ], +[ 6294, "igz" ], +[ 6295, "iha" ], +[ 6296, "ihb" ], +[ 6297, "ihc" ], +[ 6298, "ihd" ], +[ 6299, "ihe" ], +[ 6300, "ihf" ], +[ 6301, "ihg" ], +[ 6302, "ihh" ], +[ 6303, "ihi" ], +[ 6304, "ihj" ], +[ 6305, "ihk" ], +[ 6306, "ihl" ], +[ 6307, "ihm" ], +[ 6308, "ihn" ], +[ 6309, "iho" ], +[ 6310, "ihp" ], +[ 6311, "ihq" ], +[ 6312, "ihr" ], +[ 6313, "ihs" ], +[ 6314, "iht" ], +[ 6315, "ihu" ], +[ 6316, "ihv" ], +[ 6317, "ihw" ], +[ 6318, "ihx" ], +[ 6319, "ihy" ], +[ 6320, "ihz" ], +[ 6321, "iia" ], +[ 6322, "iib" ], +[ 6323, "iic" ], +[ 6324, "iid" ], +[ 6325, "iie" ], +[ 6326, "iif" ], +[ 6327, "iig" ], +[ 6328, "iih" ], +[ 6329, "iii" ], +[ 6330, "iij" ], +[ 6331, "iik" ], +[ 6332, "iil" ], +[ 6333, "iim" ], +[ 6334, "iin" ], +[ 6335, "iio" ], +[ 6336, "iip" ], +[ 6337, "iiq" ], +[ 6338, "iir" ], +[ 6339, "iis" ], +[ 6340, "iit" ], +[ 6341, "iiu" ], +[ 6342, "iiv" ], +[ 6343, "iiw" ], +[ 6344, "iix" ], +[ 6345, "iiy" ], +[ 6346, "iiz" ], +[ 6347, "ija" ], +[ 6348, "ijb" ], +[ 6349, "ijc" ], +[ 6350, "ijd" ], +[ 6351, "ije" ], +[ 6352, "ijf" ], +[ 6353, "ijg" ], +[ 6354, "ijh" ], +[ 6355, "iji" ], +[ 6356, "ijj" ], +[ 6357, "ijk" ], +[ 6358, "ijl" ], +[ 6359, "ijm" ], +[ 6360, "ijn" ], +[ 6361, "ijo" ], +[ 6362, "ijp" ], +[ 6363, "ijq" ], +[ 6364, "ijr" ], +[ 6365, "ijs" ], +[ 6366, "ijt" ], +[ 6367, "iju" ], +[ 6368, "ijv" ], +[ 6369, "ijw" ], +[ 6370, "ijx" ], +[ 6371, "ijy" ], +[ 6372, "ijz" ], +[ 6373, "ika" ], +[ 6374, "ikb" ], +[ 6375, "ikc" ], +[ 6376, "ikd" ], +[ 6377, "ike" ], +[ 6378, "ikf" ], +[ 6379, "ikg" ], +[ 6380, "ikh" ], +[ 6381, "iki" ], +[ 6382, "ikj" ], +[ 6383, "ikk" ], +[ 6384, "ikl" ], +[ 6385, "ikm" ], +[ 6386, "ikn" ], +[ 6387, "iko" ], +[ 6388, "ikp" ], +[ 6389, "ikq" ], +[ 6390, "ikr" ], +[ 6391, "iks" ], +[ 6392, "ikt" ], +[ 6393, "iku" ], +[ 6394, "ikv" ], +[ 6395, "ikw" ], +[ 6396, "ikx" ], +[ 6397, "iky" ], +[ 6398, "ikz" ], +[ 6399, "ila" ], +[ 6400, "ilb" ], +[ 6401, "ilc" ], +[ 6402, "ild" ], +[ 6403, "ile" ], +[ 6404, "ilf" ], +[ 6405, "ilg" ], +[ 6406, "ilh" ], +[ 6407, "ili" ], +[ 6408, "ilj" ], +[ 6409, "ilk" ], +[ 6410, "ill" ], +[ 6411, "ilm" ], +[ 6412, "iln" ], +[ 6413, "ilo" ], +[ 6414, "ilp" ], +[ 6415, "ilq" ], +[ 6416, "ilr" ], +[ 6417, "ils" ], +[ 6418, "ilt" ], +[ 6419, "ilu" ], +[ 6420, "ilv" ], +[ 6421, "ilw" ], +[ 6422, "ilx" ], +[ 6423, "ily" ], +[ 6424, "ilz" ], +[ 6425, "ima" ], +[ 6426, "imb" ], +[ 6427, "imc" ], +[ 6428, "imd" ], +[ 6429, "ime" ], +[ 6430, "imf" ], +[ 6431, "img" ], +[ 6432, "imh" ], +[ 6433, "imi" ], +[ 6434, "imj" ], +[ 6435, "imk" ], +[ 6436, "iml" ], +[ 6437, "imm" ], +[ 6438, "imn" ], +[ 6439, "imo" ], +[ 6440, "imp" ], +[ 6441, "imq" ], +[ 6442, "imr" ], +[ 6443, "ims" ], +[ 6444, "imt" ], +[ 6445, "imu" ], +[ 6446, "imv" ], +[ 6447, "imw" ], +[ 6448, "imx" ], +[ 6449, "imy" ], +[ 6450, "imz" ], +[ 6451, "ina" ], +[ 6452, "inb" ], +[ 6453, "inc" ], +[ 6454, "ind" ], +[ 6455, "ine" ], +[ 6456, "inf" ], +[ 6457, "ing" ], +[ 6458, "inh" ], +[ 6459, "ini" ], +[ 6460, "inj" ], +[ 6461, "ink" ], +[ 6462, "inl" ], +[ 6463, "inm" ], +[ 6464, "inn" ], +[ 6465, "ino" ], +[ 6466, "inp" ], +[ 6467, "inq" ], +[ 6468, "inr" ], +[ 6469, "ins" ], +[ 6470, "int" ], +[ 6471, "inu" ], +[ 6472, "inv" ], +[ 6473, "inw" ], +[ 6474, "inx" ], +[ 6475, "iny" ], +[ 6476, "inz" ], +[ 6477, "ioa" ], +[ 6478, "iob" ], +[ 6479, "ioc" ], +[ 6480, "iod" ], +[ 6481, "ioe" ], +[ 6482, "iof" ], +[ 6483, "iog" ], +[ 6484, "ioh" ], +[ 6485, "ioi" ], +[ 6486, "ioj" ], +[ 6487, "iok" ], +[ 6488, "iol" ], +[ 6489, "iom" ], +[ 6490, "ion" ], +[ 6491, "ioo" ], +[ 6492, "iop" ], +[ 6493, "ioq" ], +[ 6494, "ior" ], +[ 6495, "ios" ], +[ 6496, "iot" ], +[ 6497, "iou" ], +[ 6498, "iov" ], +[ 6499, "iow" ], +[ 6500, "iox" ], +[ 6501, "ioy" ], +[ 6502, "ioz" ], +[ 6503, "ipa" ], +[ 6504, "ipb" ], +[ 6505, "ipc" ], +[ 6506, "ipd" ], +[ 6507, "ipe" ], +[ 6508, "ipf" ], +[ 6509, "ipg" ], +[ 6510, "iph" ], +[ 6511, "ipi" ], +[ 6512, "ipj" ], +[ 6513, "ipk" ], +[ 6514, "ipl" ], +[ 6515, "ipm" ], +[ 6516, "ipn" ], +[ 6517, "ipo" ], +[ 6518, "ipp" ], +[ 6519, "ipq" ], +[ 6520, "ipr" ], +[ 6521, "ips" ], +[ 6522, "ipt" ], +[ 6523, "ipu" ], +[ 6524, "ipv" ], +[ 6525, "ipw" ], +[ 6526, "ipx" ], +[ 6527, "ipy" ], +[ 6528, "ipz" ], +[ 6529, "iqa" ], +[ 6530, "iqb" ], +[ 6531, "iqc" ], +[ 6532, "iqd" ], +[ 6533, "iqe" ], +[ 6534, "iqf" ], +[ 6535, "iqg" ], +[ 6536, "iqh" ], +[ 6537, "iqi" ], +[ 6538, "iqj" ], +[ 6539, "iqk" ], +[ 6540, "iql" ], +[ 6541, "iqm" ], +[ 6542, "iqn" ], +[ 6543, "iqo" ], +[ 6544, "iqp" ], +[ 6545, "iqq" ], +[ 6546, "iqr" ], +[ 6547, "iqs" ], +[ 6548, "iqt" ], +[ 6549, "iqu" ], +[ 6550, "iqv" ], +[ 6551, "iqw" ], +[ 6552, "iqx" ], +[ 6553, "iqy" ], +[ 6554, "iqz" ], +[ 6555, "ira" ], +[ 6556, "irb" ], +[ 6557, "irc" ], +[ 6558, "ird" ], +[ 6559, "ire" ], +[ 6560, "irf" ], +[ 6561, "irg" ], +[ 6562, "irh" ], +[ 6563, "iri" ], +[ 6564, "irj" ], +[ 6565, "irk" ], +[ 6566, "irl" ], +[ 6567, "irm" ], +[ 6568, "irn" ], +[ 6569, "iro" ], +[ 6570, "irp" ], +[ 6571, "irq" ], +[ 6572, "irr" ], +[ 6573, "irs" ], +[ 6574, "irt" ], +[ 6575, "iru" ], +[ 6576, "irv" ], +[ 6577, "irw" ], +[ 6578, "irx" ], +[ 6579, "iry" ], +[ 6580, "irz" ], +[ 6581, "isa" ], +[ 6582, "isb" ], +[ 6583, "isc" ], +[ 6584, "isd" ], +[ 6585, "ise" ], +[ 6586, "isf" ], +[ 6587, "isg" ], +[ 6588, "ish" ], +[ 6589, "isi" ], +[ 6590, "isj" ], +[ 6591, "isk" ], +[ 6592, "isl" ], +[ 6593, "ism" ], +[ 6594, "isn" ], +[ 6595, "iso" ], +[ 6596, "isp" ], +[ 6597, "isq" ], +[ 6598, "isr" ], +[ 6599, "iss" ], +[ 6600, "ist" ], +[ 6601, "isu" ], +[ 6602, "isv" ], +[ 6603, "isw" ], +[ 6604, "isx" ], +[ 6605, "isy" ], +[ 6606, "isz" ], +[ 6607, "ita" ], +[ 6608, "itb" ], +[ 6609, "itc" ], +[ 6610, "itd" ], +[ 6611, "ite" ], +[ 6612, "itf" ], +[ 6613, "itg" ], +[ 6614, "ith" ], +[ 6615, "iti" ], +[ 6616, "itj" ], +[ 6617, "itk" ], +[ 6618, "itl" ], +[ 6619, "itm" ], +[ 6620, "itn" ], +[ 6621, "ito" ], +[ 6622, "itp" ], +[ 6623, "itq" ], +[ 6624, "itr" ], +[ 6625, "its" ], +[ 6626, "itt" ], +[ 6627, "itu" ], +[ 6628, "itv" ], +[ 6629, "itw" ], +[ 6630, "itx" ], +[ 6631, "ity" ], +[ 6632, "itz" ], +[ 6633, "iua" ], +[ 6634, "iub" ], +[ 6635, "iuc" ], +[ 6636, "iud" ], +[ 6637, "iue" ], +[ 6638, "iuf" ], +[ 6639, "iug" ], +[ 6640, "iuh" ], +[ 6641, "iui" ], +[ 6642, "iuj" ], +[ 6643, "iuk" ], +[ 6644, "iul" ], +[ 6645, "ium" ], +[ 6646, "iun" ], +[ 6647, "iuo" ], +[ 6648, "iup" ], +[ 6649, "iuq" ], +[ 6650, "iur" ], +[ 6651, "ius" ], +[ 6652, "iut" ], +[ 6653, "iuu" ], +[ 6654, "iuv" ], +[ 6655, "iuw" ], +[ 6656, "iux" ], +[ 6657, "iuy" ], +[ 6658, "iuz" ], +[ 6659, "iva" ], +[ 6660, "ivb" ], +[ 6661, "ivc" ], +[ 6662, "ivd" ], +[ 6663, "ive" ], +[ 6664, "ivf" ], +[ 6665, "ivg" ], +[ 6666, "ivh" ], +[ 6667, "ivi" ], +[ 6668, "ivj" ], +[ 6669, "ivk" ], +[ 6670, "ivl" ], +[ 6671, "ivm" ], +[ 6672, "ivn" ], +[ 6673, "ivo" ], +[ 6674, "ivp" ], +[ 6675, "ivq" ], +[ 6676, "ivr" ], +[ 6677, "ivs" ], +[ 6678, "ivt" ], +[ 6679, "ivu" ], +[ 6680, "ivv" ], +[ 6681, "ivw" ], +[ 6682, "ivx" ], +[ 6683, "ivy" ], +[ 6684, "ivz" ], +[ 6685, "iwa" ], +[ 6686, "iwb" ], +[ 6687, "iwc" ], +[ 6688, "iwd" ], +[ 6689, "iwe" ], +[ 6690, "iwf" ], +[ 6691, "iwg" ], +[ 6692, "iwh" ], +[ 6693, "iwi" ], +[ 6694, "iwj" ], +[ 6695, "iwk" ], +[ 6696, "iwl" ], +[ 6697, "iwm" ], +[ 6698, "iwn" ], +[ 6699, "iwo" ], +[ 6700, "iwp" ], +[ 6701, "iwq" ], +[ 6702, "iwr" ], +[ 6703, "iws" ], +[ 6704, "iwt" ], +[ 6705, "iwu" ], +[ 6706, "iwv" ], +[ 6707, "iww" ], +[ 6708, "iwx" ], +[ 6709, "iwy" ], +[ 6710, "iwz" ], +[ 6711, "ixa" ], +[ 6712, "ixb" ], +[ 6713, "ixc" ], +[ 6714, "ixd" ], +[ 6715, "ixe" ], +[ 6716, "ixf" ], +[ 6717, "ixg" ], +[ 6718, "ixh" ], +[ 6719, "ixi" ], +[ 6720, "ixj" ], +[ 6721, "ixk" ], +[ 6722, "ixl" ], +[ 6723, "ixm" ], +[ 6724, "ixn" ], +[ 6725, "ixo" ], +[ 6726, "ixp" ], +[ 6727, "ixq" ], +[ 6728, "ixr" ], +[ 6729, "ixs" ], +[ 6730, "ixt" ], +[ 6731, "ixu" ], +[ 6732, "ixv" ], +[ 6733, "ixw" ], +[ 6734, "ixx" ], +[ 6735, "ixy" ], +[ 6736, "ixz" ], +[ 6737, "iya" ], +[ 6738, "iyb" ], +[ 6739, "iyc" ], +[ 6740, "iyd" ], +[ 6741, "iye" ], +[ 6742, "iyf" ], +[ 6743, "iyg" ], +[ 6744, "iyh" ], +[ 6745, "iyi" ], +[ 6746, "iyj" ], +[ 6747, "iyk" ], +[ 6748, "iyl" ], +[ 6749, "iym" ], +[ 6750, "iyn" ], +[ 6751, "iyo" ], +[ 6752, "iyp" ], +[ 6753, "iyq" ], +[ 6754, "iyr" ], +[ 6755, "iys" ], +[ 6756, "iyt" ], +[ 6757, "iyu" ], +[ 6758, "iyv" ], +[ 6759, "iyw" ], +[ 6760, "iyx" ], +[ 6761, "iyy" ], +[ 6762, "iyz" ], +[ 6763, "iza" ], +[ 6764, "izb" ], +[ 6765, "izc" ], +[ 6766, "izd" ], +[ 6767, "ize" ], +[ 6768, "izf" ], +[ 6769, "izg" ], +[ 6770, "izh" ], +[ 6771, "izi" ], +[ 6772, "izj" ], +[ 6773, "izk" ], +[ 6774, "izl" ], +[ 6775, "izm" ], +[ 6776, "izn" ], +[ 6777, "izo" ], +[ 6778, "izp" ], +[ 6779, "izq" ], +[ 6780, "izr" ], +[ 6781, "izs" ], +[ 6782, "izt" ], +[ 6783, "izu" ], +[ 6784, "izv" ], +[ 6785, "izw" ], +[ 6786, "izx" ], +[ 6787, "izy" ], +[ 6788, "izz" ], +[ 6789, "jaa" ], +[ 6790, "jab" ], +[ 6791, "jac" ], +[ 6792, "jad" ], +[ 6793, "jae" ], +[ 6794, "jaf" ], +[ 6795, "jag" ], +[ 6796, "jah" ], +[ 6797, "jai" ], +[ 6798, "jaj" ], +[ 6799, "jak" ], +[ 6800, "jal" ], +[ 6801, "jam" ], +[ 6802, "jan" ], +[ 6803, "jao" ], +[ 6804, "jap" ], +[ 6805, "jaq" ], +[ 6806, "jar" ], +[ 6807, "jas" ], +[ 6808, "jat" ], +[ 6809, "jau" ], +[ 6810, "jav" ], +[ 6811, "jaw" ], +[ 6812, "jax" ], +[ 6813, "jay" ], +[ 6814, "jaz" ], +[ 6815, "jba" ], +[ 6816, "jbb" ], +[ 6817, "jbc" ], +[ 6818, "jbd" ], +[ 6819, "jbe" ], +[ 6820, "jbf" ], +[ 6821, "jbg" ], +[ 6822, "jbh" ], +[ 6823, "jbi" ], +[ 6824, "jbj" ], +[ 6825, "jbk" ], +[ 6826, "jbl" ], +[ 6827, "jbm" ], +[ 6828, "jbn" ], +[ 6829, "jbo" ], +[ 6830, "jbp" ], +[ 6831, "jbq" ], +[ 6832, "jbr" ], +[ 6833, "jbs" ], +[ 6834, "jbt" ], +[ 6835, "jbu" ], +[ 6836, "jbv" ], +[ 6837, "jbw" ], +[ 6838, "jbx" ], +[ 6839, "jby" ], +[ 6840, "jbz" ], +[ 6841, "jca" ], +[ 6842, "jcb" ], +[ 6843, "jcc" ], +[ 6844, "jcd" ], +[ 6845, "jce" ], +[ 6846, "jcf" ], +[ 6847, "jcg" ], +[ 6848, "jch" ], +[ 6849, "jci" ], +[ 6850, "jcj" ], +[ 6851, "jck" ], +[ 6852, "jcl" ], +[ 6853, "jcm" ], +[ 6854, "jcn" ], +[ 6855, "jco" ], +[ 6856, "jcp" ], +[ 6857, "jcq" ], +[ 6858, "jcr" ], +[ 6859, "jcs" ], +[ 6860, "jct" ], +[ 6861, "jcu" ], +[ 6862, "jcv" ], +[ 6863, "jcw" ], +[ 6864, "jcx" ], +[ 6865, "jcy" ], +[ 6866, "jcz" ], +[ 6867, "jda" ], +[ 6868, "jdb" ], +[ 6869, "jdc" ], +[ 6870, "jdd" ], +[ 6871, "jde" ], +[ 6872, "jdf" ], +[ 6873, "jdg" ], +[ 6874, "jdh" ], +[ 6875, "jdi" ], +[ 6876, "jdj" ], +[ 6877, "jdk" ], +[ 6878, "jdl" ], +[ 6879, "jdm" ], +[ 6880, "jdn" ], +[ 6881, "jdo" ], +[ 6882, "jdp" ], +[ 6883, "jdq" ], +[ 6884, "jdr" ], +[ 6885, "jds" ], +[ 6886, "jdt" ], +[ 6887, "jdu" ], +[ 6888, "jdv" ], +[ 6889, "jdw" ], +[ 6890, "jdx" ], +[ 6891, "jdy" ], +[ 6892, "jdz" ], +[ 6893, "jea" ], +[ 6894, "jeb" ], +[ 6895, "jec" ], +[ 6896, "jed" ], +[ 6897, "jee" ], +[ 6898, "jef" ], +[ 6899, "jeg" ], +[ 6900, "jeh" ], +[ 6901, "jei" ], +[ 6902, "jej" ], +[ 6903, "jek" ], +[ 6904, "jel" ], +[ 6905, "jem" ], +[ 6906, "jen" ], +[ 6907, "jeo" ], +[ 6908, "jep" ], +[ 6909, "jeq" ], +[ 6910, "jer" ], +[ 6911, "jes" ], +[ 6912, "jet" ], +[ 6913, "jeu" ], +[ 6914, "jev" ], +[ 6915, "jew" ], +[ 6916, "jex" ], +[ 6917, "jey" ], +[ 6918, "jez" ], +[ 6919, "jfa" ], +[ 6920, "jfb" ], +[ 6921, "jfc" ], +[ 6922, "jfd" ], +[ 6923, "jfe" ], +[ 6924, "jff" ], +[ 6925, "jfg" ], +[ 6926, "jfh" ], +[ 6927, "jfi" ], +[ 6928, "jfj" ], +[ 6929, "jfk" ], +[ 6930, "jfl" ], +[ 6931, "jfm" ], +[ 6932, "jfn" ], +[ 6933, "jfo" ], +[ 6934, "jfp" ], +[ 6935, "jfq" ], +[ 6936, "jfr" ], +[ 6937, "jfs" ], +[ 6938, "jft" ], +[ 6939, "jfu" ], +[ 6940, "jfv" ], +[ 6941, "jfw" ], +[ 6942, "jfx" ], +[ 6943, "jfy" ], +[ 6944, "jfz" ], +[ 6945, "jga" ], +[ 6946, "jgb" ], +[ 6947, "jgc" ], +[ 6948, "jgd" ], +[ 6949, "jge" ], +[ 6950, "jgf" ], +[ 6951, "jgg" ], +[ 6952, "jgh" ], +[ 6953, "jgi" ], +[ 6954, "jgj" ], +[ 6955, "jgk" ], +[ 6956, "jgl" ], +[ 6957, "jgm" ], +[ 6958, "jgn" ], +[ 6959, "jgo" ], +[ 6960, "jgp" ], +[ 6961, "jgq" ], +[ 6962, "jgr" ], +[ 6963, "jgs" ], +[ 6964, "jgt" ], +[ 6965, "jgu" ], +[ 6966, "jgv" ], +[ 6967, "jgw" ], +[ 6968, "jgx" ], +[ 6969, "jgy" ], +[ 6970, "jgz" ], +[ 6971, "jha" ], +[ 6972, "jhb" ], +[ 6973, "jhc" ], +[ 6974, "jhd" ], +[ 6975, "jhe" ], +[ 6976, "jhf" ], +[ 6977, "jhg" ], +[ 6978, "jhh" ], +[ 6979, "jhi" ], +[ 6980, "jhj" ], +[ 6981, "jhk" ], +[ 6982, "jhl" ], +[ 6983, "jhm" ], +[ 6984, "jhn" ], +[ 6985, "jho" ], +[ 6986, "jhp" ], +[ 6987, "jhq" ], +[ 6988, "jhr" ], +[ 6989, "jhs" ], +[ 6990, "jht" ], +[ 6991, "jhu" ], +[ 6992, "jhv" ], +[ 6993, "jhw" ], +[ 6994, "jhx" ], +[ 6995, "jhy" ], +[ 6996, "jhz" ], +[ 6997, "jia" ], +[ 6998, "jib" ], +[ 6999, "jic" ], +[ 7000, "jid" ], +[ 7001, "jie" ], +[ 7002, "jif" ], +[ 7003, "jig" ], +[ 7004, "jih" ], +[ 7005, "jii" ], +[ 7006, "jij" ], +[ 7007, "jik" ], +[ 7008, "jil" ], +[ 7009, "jim" ], +[ 7010, "jin" ], +[ 7011, "jio" ], +[ 7012, "jip" ], +[ 7013, "jiq" ], +[ 7014, "jir" ], +[ 7015, "jis" ], +[ 7016, "jit" ], +[ 7017, "jiu" ], +[ 7018, "jiv" ], +[ 7019, "jiw" ], +[ 7020, "jix" ], +[ 7021, "jiy" ], +[ 7022, "jiz" ], +[ 7023, "jja" ], +[ 7024, "jjb" ], +[ 7025, "jjc" ], +[ 7026, "jjd" ], +[ 7027, "jje" ], +[ 7028, "jjf" ], +[ 7029, "jjg" ], +[ 7030, "jjh" ], +[ 7031, "jji" ], +[ 7032, "jjj" ], +[ 7033, "jjk" ], +[ 7034, "jjl" ], +[ 7035, "jjm" ], +[ 7036, "jjn" ], +[ 7037, "jjo" ], +[ 7038, "jjp" ], +[ 7039, "jjq" ], +[ 7040, "jjr" ], +[ 7041, "jjs" ], +[ 7042, "jjt" ], +[ 7043, "jju" ], +[ 7044, "jjv" ], +[ 7045, "jjw" ], +[ 7046, "jjx" ], +[ 7047, "jjy" ], +[ 7048, "jjz" ], +[ 7049, "jka" ], +[ 7050, "jkb" ], +[ 7051, "jkc" ], +[ 7052, "jkd" ], +[ 7053, "jke" ], +[ 7054, "jkf" ], +[ 7055, "jkg" ], +[ 7056, "jkh" ], +[ 7057, "jki" ], +[ 7058, "jkj" ], +[ 7059, "jkk" ], +[ 7060, "jkl" ], +[ 7061, "jkm" ], +[ 7062, "jkn" ], +[ 7063, "jko" ], +[ 7064, "jkp" ], +[ 7065, "jkq" ], +[ 7066, "jkr" ], +[ 7067, "jks" ], +[ 7068, "jkt" ], +[ 7069, "jku" ], +[ 7070, "jkv" ], +[ 7071, "jkw" ], +[ 7072, "jkx" ], +[ 7073, "jky" ], +[ 7074, "jkz" ], +[ 7075, "jla" ], +[ 7076, "jlb" ], +[ 7077, "jlc" ], +[ 7078, "jld" ], +[ 7079, "jle" ], +[ 7080, "jlf" ], +[ 7081, "jlg" ], +[ 7082, "jlh" ], +[ 7083, "jli" ], +[ 7084, "jlj" ], +[ 7085, "jlk" ], +[ 7086, "jll" ], +[ 7087, "jlm" ], +[ 7088, "jln" ], +[ 7089, "jlo" ], +[ 7090, "jlp" ], +[ 7091, "jlq" ], +[ 7092, "jlr" ], +[ 7093, "jls" ], +[ 7094, "jlt" ], +[ 7095, "jlu" ], +[ 7096, "jlv" ], +[ 7097, "jlw" ], +[ 7098, "jlx" ], +[ 7099, "jly" ], +[ 7100, "jlz" ], +[ 7101, "jma" ], +[ 7102, "jmb" ], +[ 7103, "jmc" ], +[ 7104, "jmd" ], +[ 7105, "jme" ], +[ 7106, "jmf" ], +[ 7107, "jmg" ], +[ 7108, "jmh" ], +[ 7109, "jmi" ], +[ 7110, "jmj" ], +[ 7111, "jmk" ], +[ 7112, "jml" ], +[ 7113, "jmm" ], +[ 7114, "jmn" ], +[ 7115, "jmo" ], +[ 7116, "jmp" ], +[ 7117, "jmq" ], +[ 7118, "jmr" ], +[ 7119, "jms" ], +[ 7120, "jmt" ], +[ 7121, "jmu" ], +[ 7122, "jmv" ], +[ 7123, "jmw" ], +[ 7124, "jmx" ], +[ 7125, "jmy" ], +[ 7126, "jmz" ], +[ 7127, "jna" ], +[ 7128, "jnb" ], +[ 7129, "jnc" ], +[ 7130, "jnd" ], +[ 7131, "jne" ], +[ 7132, "jnf" ], +[ 7133, "jng" ], +[ 7134, "jnh" ], +[ 7135, "jni" ], +[ 7136, "jnj" ], +[ 7137, "jnk" ], +[ 7138, "jnl" ], +[ 7139, "jnm" ], +[ 7140, "jnn" ], +[ 7141, "jno" ], +[ 7142, "jnp" ], +[ 7143, "jnq" ], +[ 7144, "jnr" ], +[ 7145, "jns" ], +[ 7146, "jnt" ], +[ 7147, "jnu" ], +[ 7148, "jnv" ], +[ 7149, "jnw" ], +[ 7150, "jnx" ], +[ 7151, "jny" ], +[ 7152, "jnz" ], +[ 7153, "joa" ], +[ 7154, "job" ], +[ 7155, "joc" ], +[ 7156, "jod" ], +[ 7157, "joe" ], +[ 7158, "jof" ], +[ 7159, "jog" ], +[ 7160, "joh" ], +[ 7161, "joi" ], +[ 7162, "joj" ], +[ 7163, "jok" ], +[ 7164, "jol" ], +[ 7165, "jom" ], +[ 7166, "jon" ], +[ 7167, "joo" ], +[ 7168, "jop" ], +[ 7169, "joq" ], +[ 7170, "jor" ], +[ 7171, "jos" ], +[ 7172, "jot" ], +[ 7173, "jou" ], +[ 7174, "jov" ], +[ 7175, "jow" ], +[ 7176, "jox" ], +[ 7177, "joy" ], +[ 7178, "joz" ], +[ 7179, "jpa" ], +[ 7180, "jpb" ], +[ 7181, "jpc" ], +[ 7182, "jpd" ], +[ 7183, "jpe" ], +[ 7184, "jpf" ], +[ 7185, "jpg" ], +[ 7186, "jph" ], +[ 7187, "jpi" ], +[ 7188, "jpj" ], +[ 7189, "jpk" ], +[ 7190, "jpl" ], +[ 7191, "jpm" ], +[ 7192, "jpn" ], +[ 7193, "jpo" ], +[ 7194, "jpp" ], +[ 7195, "jpq" ], +[ 7196, "jpr" ], +[ 7197, "jps" ], +[ 7198, "jpt" ], +[ 7199, "jpu" ], +[ 7200, "jpv" ], +[ 7201, "jpw" ], +[ 7202, "jpx" ], +[ 7203, "jpy" ], +[ 7204, "jpz" ], +[ 7205, "jqa" ], +[ 7206, "jqb" ], +[ 7207, "jqc" ], +[ 7208, "jqd" ], +[ 7209, "jqe" ], +[ 7210, "jqf" ], +[ 7211, "jqg" ], +[ 7212, "jqh" ], +[ 7213, "jqi" ], +[ 7214, "jqj" ], +[ 7215, "jqk" ], +[ 7216, "jql" ], +[ 7217, "jqm" ], +[ 7218, "jqn" ], +[ 7219, "jqo" ], +[ 7220, "jqp" ], +[ 7221, "jqq" ], +[ 7222, "jqr" ], +[ 7223, "jqs" ], +[ 7224, "jqt" ], +[ 7225, "jqu" ], +[ 7226, "jqv" ], +[ 7227, "jqw" ], +[ 7228, "jqx" ], +[ 7229, "jqy" ], +[ 7230, "jqz" ], +[ 7231, "jra" ], +[ 7232, "jrb" ], +[ 7233, "jrc" ], +[ 7234, "jrd" ], +[ 7235, "jre" ], +[ 7236, "jrf" ], +[ 7237, "jrg" ], +[ 7238, "jrh" ], +[ 7239, "jri" ], +[ 7240, "jrj" ], +[ 7241, "jrk" ], +[ 7242, "jrl" ], +[ 7243, "jrm" ], +[ 7244, "jrn" ], +[ 7245, "jro" ], +[ 7246, "jrp" ], +[ 7247, "jrq" ], +[ 7248, "jrr" ], +[ 7249, "jrs" ], +[ 7250, "jrt" ], +[ 7251, "jru" ], +[ 7252, "jrv" ], +[ 7253, "jrw" ], +[ 7254, "jrx" ], +[ 7255, "jry" ], +[ 7256, "jrz" ], +[ 7257, "jsa" ], +[ 7258, "jsb" ], +[ 7259, "jsc" ], +[ 7260, "jsd" ], +[ 7261, "jse" ], +[ 7262, "jsf" ], +[ 7263, "jsg" ], +[ 7264, "jsh" ], +[ 7265, "jsi" ], +[ 7266, "jsj" ], +[ 7267, "jsk" ], +[ 7268, "jsl" ], +[ 7269, "jsm" ], +[ 7270, "jsn" ], +[ 7271, "jso" ], +[ 7272, "jsp" ], +[ 7273, "jsq" ], +[ 7274, "jsr" ], +[ 7275, "jss" ], +[ 7276, "jst" ], +[ 7277, "jsu" ], +[ 7278, "jsv" ], +[ 7279, "jsw" ], +[ 7280, "jsx" ], +[ 7281, "jsy" ], +[ 7282, "jsz" ], +[ 7283, "jta" ], +[ 7284, "jtb" ], +[ 7285, "jtc" ], +[ 7286, "jtd" ], +[ 7287, "jte" ], +[ 7288, "jtf" ], +[ 7289, "jtg" ], +[ 7290, "jth" ], +[ 7291, "jti" ], +[ 7292, "jtj" ], +[ 7293, "jtk" ], +[ 7294, "jtl" ], +[ 7295, "jtm" ], +[ 7296, "jtn" ], +[ 7297, "jto" ], +[ 7298, "jtp" ], +[ 7299, "jtq" ], +[ 7300, "jtr" ], +[ 7301, "jts" ], +[ 7302, "jtt" ], +[ 7303, "jtu" ], +[ 7304, "jtv" ], +[ 7305, "jtw" ], +[ 7306, "jtx" ], +[ 7307, "jty" ], +[ 7308, "jtz" ], +[ 7309, "jua" ], +[ 7310, "jub" ], +[ 7311, "juc" ], +[ 7312, "jud" ], +[ 7313, "jue" ], +[ 7314, "juf" ], +[ 7315, "jug" ], +[ 7316, "juh" ], +[ 7317, "jui" ], +[ 7318, "juj" ], +[ 7319, "juk" ], +[ 7320, "jul" ], +[ 7321, "jum" ], +[ 7322, "jun" ], +[ 7323, "juo" ], +[ 7324, "jup" ], +[ 7325, "juq" ], +[ 7326, "jur" ], +[ 7327, "jus" ], +[ 7328, "jut" ], +[ 7329, "juu" ], +[ 7330, "juv" ], +[ 7331, "juw" ], +[ 7332, "jux" ], +[ 7333, "juy" ], +[ 7334, "juz" ], +[ 7335, "jva" ], +[ 7336, "jvb" ], +[ 7337, "jvc" ], +[ 7338, "jvd" ], +[ 7339, "jve" ], +[ 7340, "jvf" ], +[ 7341, "jvg" ], +[ 7342, "jvh" ], +[ 7343, "jvi" ], +[ 7344, "jvj" ], +[ 7345, "jvk" ], +[ 7346, "jvl" ], +[ 7347, "jvm" ], +[ 7348, "jvn" ], +[ 7349, "jvo" ], +[ 7350, "jvp" ], +[ 7351, "jvq" ], +[ 7352, "jvr" ], +[ 7353, "jvs" ], +[ 7354, "jvt" ], +[ 7355, "jvu" ], +[ 7356, "jvv" ], +[ 7357, "jvw" ], +[ 7358, "jvx" ], +[ 7359, "jvy" ], +[ 7360, "jvz" ], +[ 7361, "jwa" ], +[ 7362, "jwb" ], +[ 7363, "jwc" ], +[ 7364, "jwd" ], +[ 7365, "jwe" ], +[ 7366, "jwf" ], +[ 7367, "jwg" ], +[ 7368, "jwh" ], +[ 7369, "jwi" ], +[ 7370, "jwj" ], +[ 7371, "jwk" ], +[ 7372, "jwl" ], +[ 7373, "jwm" ], +[ 7374, "jwn" ], +[ 7375, "jwo" ], +[ 7376, "jwp" ], +[ 7377, "jwq" ], +[ 7378, "jwr" ], +[ 7379, "jws" ], +[ 7380, "jwt" ], +[ 7381, "jwu" ], +[ 7382, "jwv" ], +[ 7383, "jww" ], +[ 7384, "jwx" ], +[ 7385, "jwy" ], +[ 7386, "jwz" ], +[ 7387, "jxa" ], +[ 7388, "jxb" ], +[ 7389, "jxc" ], +[ 7390, "jxd" ], +[ 7391, "jxe" ], +[ 7392, "jxf" ], +[ 7393, "jxg" ], +[ 7394, "jxh" ], +[ 7395, "jxi" ], +[ 7396, "jxj" ], +[ 7397, "jxk" ], +[ 7398, "jxl" ], +[ 7399, "jxm" ], +[ 7400, "jxn" ], +[ 7401, "jxo" ], +[ 7402, "jxp" ], +[ 7403, "jxq" ], +[ 7404, "jxr" ], +[ 7405, "jxs" ], +[ 7406, "jxt" ], +[ 7407, "jxu" ], +[ 7408, "jxv" ], +[ 7409, "jxw" ], +[ 7410, "jxx" ], +[ 7411, "jxy" ], +[ 7412, "jxz" ], +[ 7413, "jya" ], +[ 7414, "jyb" ], +[ 7415, "jyc" ], +[ 7416, "jyd" ], +[ 7417, "jye" ], +[ 7418, "jyf" ], +[ 7419, "jyg" ], +[ 7420, "jyh" ], +[ 7421, "jyi" ], +[ 7422, "jyj" ], +[ 7423, "jyk" ], +[ 7424, "jyl" ], +[ 7425, "jym" ], +[ 7426, "jyn" ], +[ 7427, "jyo" ], +[ 7428, "jyp" ], +[ 7429, "jyq" ], +[ 7430, "jyr" ], +[ 7431, "jys" ], +[ 7432, "jyt" ], +[ 7433, "jyu" ], +[ 7434, "jyv" ], +[ 7435, "jyw" ], +[ 7436, "jyx" ], +[ 7437, "jyy" ], +[ 7438, "jyz" ], +[ 7439, "jza" ], +[ 7440, "jzb" ], +[ 7441, "jzc" ], +[ 7442, "jzd" ], +[ 7443, "jze" ], +[ 7444, "jzf" ], +[ 7445, "jzg" ], +[ 7446, "jzh" ], +[ 7447, "jzi" ], +[ 7448, "jzj" ], +[ 7449, "jzk" ], +[ 7450, "jzl" ], +[ 7451, "jzm" ], +[ 7452, "jzn" ], +[ 7453, "jzo" ], +[ 7454, "jzp" ], +[ 7455, "jzq" ], +[ 7456, "jzr" ], +[ 7457, "jzs" ], +[ 7458, "jzt" ], +[ 7459, "jzu" ], +[ 7460, "jzv" ], +[ 7461, "jzw" ], +[ 7462, "jzx" ], +[ 7463, "jzy" ], +[ 7464, "jzz" ], +[ 7465, "kaa" ], +[ 7466, "kab" ], +[ 7467, "kac" ], +[ 7468, "kad" ], +[ 7469, "kae" ], +[ 7470, "kaf" ], +[ 7471, "kag" ], +[ 7472, "kah" ], +[ 7473, "kai" ], +[ 7474, "kaj" ], +[ 7475, "kak" ], +[ 7476, "kal" ], +[ 7477, "kam" ], +[ 7478, "kan" ], +[ 7479, "kao" ], +[ 7480, "kap" ], +[ 7481, "kaq" ], +[ 7482, "kar" ], +[ 7483, "kas" ], +[ 7484, "kat" ], +[ 7485, "kau" ], +[ 7486, "kav" ], +[ 7487, "kaw" ], +[ 7488, "kax" ], +[ 7489, "kay" ], +[ 7490, "kaz" ], +[ 7491, "kba" ], +[ 7492, "kbb" ], +[ 7493, "kbc" ], +[ 7494, "kbd" ], +[ 7495, "kbe" ], +[ 7496, "kbf" ], +[ 7497, "kbg" ], +[ 7498, "kbh" ], +[ 7499, "kbi" ], +[ 7500, "kbj" ], +[ 7501, "kbk" ], +[ 7502, "kbl" ], +[ 7503, "kbm" ], +[ 7504, "kbn" ], +[ 7505, "kbo" ], +[ 7506, "kbp" ], +[ 7507, "kbq" ], +[ 7508, "kbr" ], +[ 7509, "kbs" ], +[ 7510, "kbt" ], +[ 7511, "kbu" ], +[ 7512, "kbv" ], +[ 7513, "kbw" ], +[ 7514, "kbx" ], +[ 7515, "kby" ], +[ 7516, "kbz" ], +[ 7517, "kca" ], +[ 7518, "kcb" ], +[ 7519, "kcc" ], +[ 7520, "kcd" ], +[ 7521, "kce" ], +[ 7522, "kcf" ], +[ 7523, "kcg" ], +[ 7524, "kch" ], +[ 7525, "kci" ], +[ 7526, "kcj" ], +[ 7527, "kck" ], +[ 7528, "kcl" ], +[ 7529, "kcm" ], +[ 7530, "kcn" ], +[ 7531, "kco" ], +[ 7532, "kcp" ], +[ 7533, "kcq" ], +[ 7534, "kcr" ], +[ 7535, "kcs" ], +[ 7536, "kct" ], +[ 7537, "kcu" ], +[ 7538, "kcv" ], +[ 7539, "kcw" ], +[ 7540, "kcx" ], +[ 7541, "kcy" ], +[ 7542, "kcz" ], +[ 7543, "kda" ], +[ 7544, "kdb" ], +[ 7545, "kdc" ], +[ 7546, "kdd" ], +[ 7547, "kde" ], +[ 7548, "kdf" ], +[ 7549, "kdg" ], +[ 7550, "kdh" ], +[ 7551, "kdi" ], +[ 7552, "kdj" ], +[ 7553, "kdk" ], +[ 7554, "kdl" ], +[ 7555, "kdm" ], +[ 7556, "kdn" ], +[ 7557, "kdo" ], +[ 7558, "kdp" ], +[ 7559, "kdq" ], +[ 7560, "kdr" ], +[ 7561, "kds" ], +[ 7562, "kdt" ], +[ 7563, "kdu" ], +[ 7564, "kdv" ], +[ 7565, "kdw" ], +[ 7566, "kdx" ], +[ 7567, "kdy" ], +[ 7568, "kdz" ], +[ 7569, "kea" ], +[ 7570, "keb" ], +[ 7571, "kec" ], +[ 7572, "ked" ], +[ 7573, "kee" ], +[ 7574, "kef" ], +[ 7575, "keg" ], +[ 7576, "keh" ], +[ 7577, "kei" ], +[ 7578, "kej" ], +[ 7579, "kek" ], +[ 7580, "kel" ], +[ 7581, "kem" ], +[ 7582, "ken" ], +[ 7583, "keo" ], +[ 7584, "kep" ], +[ 7585, "keq" ], +[ 7586, "ker" ], +[ 7587, "kes" ], +[ 7588, "ket" ], +[ 7589, "keu" ], +[ 7590, "kev" ], +[ 7591, "kew" ], +[ 7592, "kex" ], +[ 7593, "key" ], +[ 7594, "kez" ], +[ 7595, "kfa" ], +[ 7596, "kfb" ], +[ 7597, "kfc" ], +[ 7598, "kfd" ], +[ 7599, "kfe" ], +[ 7600, "kff" ], +[ 7601, "kfg" ], +[ 7602, "kfh" ], +[ 7603, "kfi" ], +[ 7604, "kfj" ], +[ 7605, "kfk" ], +[ 7606, "kfl" ], +[ 7607, "kfm" ], +[ 7608, "kfn" ], +[ 7609, "kfo" ], +[ 7610, "kfp" ], +[ 7611, "kfq" ], +[ 7612, "kfr" ], +[ 7613, "kfs" ], +[ 7614, "kft" ], +[ 7615, "kfu" ], +[ 7616, "kfv" ], +[ 7617, "kfw" ], +[ 7618, "kfx" ], +[ 7619, "kfy" ], +[ 7620, "kfz" ], +[ 7621, "kga" ], +[ 7622, "kgb" ], +[ 7623, "kgc" ], +[ 7624, "kgd" ], +[ 7625, "kge" ], +[ 7626, "kgf" ], +[ 7627, "kgg" ], +[ 7628, "kgh" ], +[ 7629, "kgi" ], +[ 7630, "kgj" ], +[ 7631, "kgk" ], +[ 7632, "kgl" ], +[ 7633, "kgm" ], +[ 7634, "kgn" ], +[ 7635, "kgo" ], +[ 7636, "kgp" ], +[ 7637, "kgq" ], +[ 7638, "kgr" ], +[ 7639, "kgs" ], +[ 7640, "kgt" ], +[ 7641, "kgu" ], +[ 7642, "kgv" ], +[ 7643, "kgw" ], +[ 7644, "kgx" ], +[ 7645, "kgy" ], +[ 7646, "kgz" ], +[ 7647, "kha" ], +[ 7648, "khb" ], +[ 7649, "khc" ], +[ 7650, "khd" ], +[ 7651, "khe" ], +[ 7652, "khf" ], +[ 7653, "khg" ], +[ 7654, "khh" ], +[ 7655, "khi" ], +[ 7656, "khj" ], +[ 7657, "khk" ], +[ 7658, "khl" ], +[ 7659, "khm" ], +[ 7660, "khn" ], +[ 7661, "kho" ], +[ 7662, "khp" ], +[ 7663, "khq" ], +[ 7664, "khr" ], +[ 7665, "khs" ], +[ 7666, "kht" ], +[ 7667, "khu" ], +[ 7668, "khv" ], +[ 7669, "khw" ], +[ 7670, "khx" ], +[ 7671, "khy" ], +[ 7672, "khz" ], +[ 7673, "kia" ], +[ 7674, "kib" ], +[ 7675, "kic" ], +[ 7676, "kid" ], +[ 7677, "kie" ], +[ 7678, "kif" ], +[ 7679, "kig" ], +[ 7680, "kih" ], +[ 7681, "kii" ], +[ 7682, "kij" ], +[ 7683, "kik" ], +[ 7684, "kil" ], +[ 7685, "kim" ], +[ 7686, "kin" ], +[ 7687, "kio" ], +[ 7688, "kip" ], +[ 7689, "kiq" ], +[ 7690, "kir" ], +[ 7691, "kis" ], +[ 7692, "kit" ], +[ 7693, "kiu" ], +[ 7694, "kiv" ], +[ 7695, "kiw" ], +[ 7696, "kix" ], +[ 7697, "kiy" ], +[ 7698, "kiz" ], +[ 7699, "kja" ], +[ 7700, "kjb" ], +[ 7701, "kjc" ], +[ 7702, "kjd" ], +[ 7703, "kje" ], +[ 7704, "kjf" ], +[ 7705, "kjg" ], +[ 7706, "kjh" ], +[ 7707, "kji" ], +[ 7708, "kjj" ], +[ 7709, "kjk" ], +[ 7710, "kjl" ], +[ 7711, "kjm" ], +[ 7712, "kjn" ], +[ 7713, "kjo" ], +[ 7714, "kjp" ], +[ 7715, "kjq" ], +[ 7716, "kjr" ], +[ 7717, "kjs" ], +[ 7718, "kjt" ], +[ 7719, "kju" ], +[ 7720, "kjv" ], +[ 7721, "kjw" ], +[ 7722, "kjx" ], +[ 7723, "kjy" ], +[ 7724, "kjz" ], +[ 7725, "kka" ], +[ 7726, "kkb" ], +[ 7727, "kkc" ], +[ 7728, "kkd" ], +[ 7729, "kke" ], +[ 7730, "kkf" ], +[ 7731, "kkg" ], +[ 7732, "kkh" ], +[ 7733, "kki" ], +[ 7734, "kkj" ], +[ 7735, "kkk" ], +[ 7736, "kkl" ], +[ 7737, "kkm" ], +[ 7738, "kkn" ], +[ 7739, "kko" ], +[ 7740, "kkp" ], +[ 7741, "kkq" ], +[ 7742, "kkr" ], +[ 7743, "kks" ], +[ 7744, "kkt" ], +[ 7745, "kku" ], +[ 7746, "kkv" ], +[ 7747, "kkw" ], +[ 7748, "kkx" ], +[ 7749, "kky" ], +[ 7750, "kkz" ], +[ 7751, "kla" ], +[ 7752, "klb" ], +[ 7753, "klc" ], +[ 7754, "kld" ], +[ 7755, "kle" ], +[ 7756, "klf" ], +[ 7757, "klg" ], +[ 7758, "klh" ], +[ 7759, "kli" ], +[ 7760, "klj" ], +[ 7761, "klk" ], +[ 7762, "kll" ], +[ 7763, "klm" ], +[ 7764, "kln" ], +[ 7765, "klo" ], +[ 7766, "klp" ], +[ 7767, "klq" ], +[ 7768, "klr" ], +[ 7769, "kls" ], +[ 7770, "klt" ], +[ 7771, "klu" ], +[ 7772, "klv" ], +[ 7773, "klw" ], +[ 7774, "klx" ], +[ 7775, "kly" ], +[ 7776, "klz" ], +[ 7777, "kma" ], +[ 7778, "kmb" ], +[ 7779, "kmc" ], +[ 7780, "kmd" ], +[ 7781, "kme" ], +[ 7782, "kmf" ], +[ 7783, "kmg" ], +[ 7784, "kmh" ], +[ 7785, "kmi" ], +[ 7786, "kmj" ], +[ 7787, "kmk" ], +[ 7788, "kml" ], +[ 7789, "kmm" ], +[ 7790, "kmn" ], +[ 7791, "kmo" ], +[ 7792, "kmp" ], +[ 7793, "kmq" ], +[ 7794, "kmr" ], +[ 7795, "kms" ], +[ 7796, "kmt" ], +[ 7797, "kmu" ], +[ 7798, "kmv" ], +[ 7799, "kmw" ], +[ 7800, "kmx" ], +[ 7801, "kmy" ], +[ 7802, "kmz" ], +[ 7803, "kna" ], +[ 7804, "knb" ], +[ 7805, "knc" ], +[ 7806, "knd" ], +[ 7807, "kne" ], +[ 7808, "knf" ], +[ 7809, "kng" ], +[ 7810, "knh" ], +[ 7811, "kni" ], +[ 7812, "knj" ], +[ 7813, "knk" ], +[ 7814, "knl" ], +[ 7815, "knm" ], +[ 7816, "knn" ], +[ 7817, "kno" ], +[ 7818, "knp" ], +[ 7819, "knq" ], +[ 7820, "knr" ], +[ 7821, "kns" ], +[ 7822, "knt" ], +[ 7823, "knu" ], +[ 7824, "knv" ], +[ 7825, "knw" ], +[ 7826, "knx" ], +[ 7827, "kny" ], +[ 7828, "knz" ], +[ 7829, "koa" ], +[ 7830, "kob" ], +[ 7831, "koc" ], +[ 7832, "kod" ], +[ 7833, "koe" ], +[ 7834, "kof" ], +[ 7835, "kog" ], +[ 7836, "koh" ], +[ 7837, "koi" ], +[ 7838, "koj" ], +[ 7839, "kok" ], +[ 7840, "kol" ], +[ 7841, "kom" ], +[ 7842, "kon" ], +[ 7843, "koo" ], +[ 7844, "kop" ], +[ 7845, "koq" ], +[ 7846, "kor" ], +[ 7847, "kos" ], +[ 7848, "kot" ], +[ 7849, "kou" ], +[ 7850, "kov" ], +[ 7851, "kow" ], +[ 7852, "kox" ], +[ 7853, "koy" ], +[ 7854, "koz" ], +[ 7855, "kpa" ], +[ 7856, "kpb" ], +[ 7857, "kpc" ], +[ 7858, "kpd" ], +[ 7859, "kpe" ], +[ 7860, "kpf" ], +[ 7861, "kpg" ], +[ 7862, "kph" ], +[ 7863, "kpi" ], +[ 7864, "kpj" ], +[ 7865, "kpk" ], +[ 7866, "kpl" ], +[ 7867, "kpm" ], +[ 7868, "kpn" ], +[ 7869, "kpo" ], +[ 7870, "kpp" ], +[ 7871, "kpq" ], +[ 7872, "kpr" ], +[ 7873, "kps" ], +[ 7874, "kpt" ], +[ 7875, "kpu" ], +[ 7876, "kpv" ], +[ 7877, "kpw" ], +[ 7878, "kpx" ], +[ 7879, "kpy" ], +[ 7880, "kpz" ], +[ 7881, "kqa" ], +[ 7882, "kqb" ], +[ 7883, "kqc" ], +[ 7884, "kqd" ], +[ 7885, "kqe" ], +[ 7886, "kqf" ], +[ 7887, "kqg" ], +[ 7888, "kqh" ], +[ 7889, "kqi" ], +[ 7890, "kqj" ], +[ 7891, "kqk" ], +[ 7892, "kql" ], +[ 7893, "kqm" ], +[ 7894, "kqn" ], +[ 7895, "kqo" ], +[ 7896, "kqp" ], +[ 7897, "kqq" ], +[ 7898, "kqr" ], +[ 7899, "kqs" ], +[ 7900, "kqt" ], +[ 7901, "kqu" ], +[ 7902, "kqv" ], +[ 7903, "kqw" ], +[ 7904, "kqx" ], +[ 7905, "kqy" ], +[ 7906, "kqz" ], +[ 7907, "kra" ], +[ 7908, "krb" ], +[ 7909, "krc" ], +[ 7910, "krd" ], +[ 7911, "kre" ], +[ 7912, "krf" ], +[ 7913, "krg" ], +[ 7914, "krh" ], +[ 7915, "kri" ], +[ 7916, "krj" ], +[ 7917, "krk" ], +[ 7918, "krl" ], +[ 7919, "krm" ], +[ 7920, "krn" ], +[ 7921, "kro" ], +[ 7922, "krp" ], +[ 7923, "krq" ], +[ 7924, "krr" ], +[ 7925, "krs" ], +[ 7926, "krt" ], +[ 7927, "kru" ], +[ 7928, "krv" ], +[ 7929, "krw" ], +[ 7930, "krx" ], +[ 7931, "kry" ], +[ 7932, "krz" ], +[ 7933, "ksa" ], +[ 7934, "ksb" ], +[ 7935, "ksc" ], +[ 7936, "ksd" ], +[ 7937, "kse" ], +[ 7938, "ksf" ], +[ 7939, "ksg" ], +[ 7940, "ksh" ], +[ 7941, "ksi" ], +[ 7942, "ksj" ], +[ 7943, "ksk" ], +[ 7944, "ksl" ], +[ 7945, "ksm" ], +[ 7946, "ksn" ], +[ 7947, "kso" ], +[ 7948, "ksp" ], +[ 7949, "ksq" ], +[ 7950, "ksr" ], +[ 7951, "kss" ], +[ 7952, "kst" ], +[ 7953, "ksu" ], +[ 7954, "ksv" ], +[ 7955, "ksw" ], +[ 7956, "ksx" ], +[ 7957, "ksy" ], +[ 7958, "ksz" ], +[ 7959, "kta" ], +[ 7960, "ktb" ], +[ 7961, "ktc" ], +[ 7962, "ktd" ], +[ 7963, "kte" ], +[ 7964, "ktf" ], +[ 7965, "ktg" ], +[ 7966, "kth" ], +[ 7967, "kti" ], +[ 7968, "ktj" ], +[ 7969, "ktk" ], +[ 7970, "ktl" ], +[ 7971, "ktm" ], +[ 7972, "ktn" ], +[ 7973, "kto" ], +[ 7974, "ktp" ], +[ 7975, "ktq" ], +[ 7976, "ktr" ], +[ 7977, "kts" ], +[ 7978, "ktt" ], +[ 7979, "ktu" ], +[ 7980, "ktv" ], +[ 7981, "ktw" ], +[ 7982, "ktx" ], +[ 7983, "kty" ], +[ 7984, "ktz" ], +[ 7985, "kua" ], +[ 7986, "kub" ], +[ 7987, "kuc" ], +[ 7988, "kud" ], +[ 7989, "kue" ], +[ 7990, "kuf" ], +[ 7991, "kug" ], +[ 7992, "kuh" ], +[ 7993, "kui" ], +[ 7994, "kuj" ], +[ 7995, "kuk" ], +[ 7996, "kul" ], +[ 7997, "kum" ], +[ 7998, "kun" ], +[ 7999, "kuo" ], +[ 8000, "kup" ], +[ 8001, "kuq" ], +[ 8002, "kur" ], +[ 8003, "kus" ], +[ 8004, "kut" ], +[ 8005, "kuu" ], +[ 8006, "kuv" ], +[ 8007, "kuw" ], +[ 8008, "kux" ], +[ 8009, "kuy" ], +[ 8010, "kuz" ], +[ 8011, "kva" ], +[ 8012, "kvb" ], +[ 8013, "kvc" ], +[ 8014, "kvd" ], +[ 8015, "kve" ], +[ 8016, "kvf" ], +[ 8017, "kvg" ], +[ 8018, "kvh" ], +[ 8019, "kvi" ], +[ 8020, "kvj" ], +[ 8021, "kvk" ], +[ 8022, "kvl" ], +[ 8023, "kvm" ], +[ 8024, "kvn" ], +[ 8025, "kvo" ], +[ 8026, "kvp" ], +[ 8027, "kvq" ], +[ 8028, "kvr" ], +[ 8029, "kvs" ], +[ 8030, "kvt" ], +[ 8031, "kvu" ], +[ 8032, "kvv" ], +[ 8033, "kvw" ], +[ 8034, "kvx" ], +[ 8035, "kvy" ], +[ 8036, "kvz" ], +[ 8037, "kwa" ], +[ 8038, "kwb" ], +[ 8039, "kwc" ], +[ 8040, "kwd" ], +[ 8041, "kwe" ], +[ 8042, "kwf" ], +[ 8043, "kwg" ], +[ 8044, "kwh" ], +[ 8045, "kwi" ], +[ 8046, "kwj" ], +[ 8047, "kwk" ], +[ 8048, "kwl" ], +[ 8049, "kwm" ], +[ 8050, "kwn" ], +[ 8051, "kwo" ], +[ 8052, "kwp" ], +[ 8053, "kwq" ], +[ 8054, "kwr" ], +[ 8055, "kws" ], +[ 8056, "kwt" ], +[ 8057, "kwu" ], +[ 8058, "kwv" ], +[ 8059, "kww" ], +[ 8060, "kwx" ], +[ 8061, "kwy" ], +[ 8062, "kwz" ], +[ 8063, "kxa" ], +[ 8064, "kxb" ], +[ 8065, "kxc" ], +[ 8066, "kxd" ], +[ 8067, "kxe" ], +[ 8068, "kxf" ], +[ 8069, "kxg" ], +[ 8070, "kxh" ], +[ 8071, "kxi" ], +[ 8072, "kxj" ], +[ 8073, "kxk" ], +[ 8074, "kxl" ], +[ 8075, "kxm" ], +[ 8076, "kxn" ], +[ 8077, "kxo" ], +[ 8078, "kxp" ], +[ 8079, "kxq" ], +[ 8080, "kxr" ], +[ 8081, "kxs" ], +[ 8082, "kxt" ], +[ 8083, "kxu" ], +[ 8084, "kxv" ], +[ 8085, "kxw" ], +[ 8086, "kxx" ], +[ 8087, "kxy" ], +[ 8088, "kxz" ], +[ 8089, "kya" ], +[ 8090, "kyb" ], +[ 8091, "kyc" ], +[ 8092, "kyd" ], +[ 8093, "kye" ], +[ 8094, "kyf" ], +[ 8095, "kyg" ], +[ 8096, "kyh" ], +[ 8097, "kyi" ], +[ 8098, "kyj" ], +[ 8099, "kyk" ], +[ 8100, "kyl" ], +[ 8101, "kym" ], +[ 8102, "kyn" ], +[ 8103, "kyo" ], +[ 8104, "kyp" ], +[ 8105, "kyq" ], +[ 8106, "kyr" ], +[ 8107, "kys" ], +[ 8108, "kyt" ], +[ 8109, "kyu" ], +[ 8110, "kyv" ], +[ 8111, "kyw" ], +[ 8112, "kyx" ], +[ 8113, "kyy" ], +[ 8114, "kyz" ], +[ 8115, "kza" ], +[ 8116, "kzb" ], +[ 8117, "kzc" ], +[ 8118, "kzd" ], +[ 8119, "kze" ], +[ 8120, "kzf" ], +[ 8121, "kzg" ], +[ 8122, "kzh" ], +[ 8123, "kzi" ], +[ 8124, "kzj" ], +[ 8125, "kzk" ], +[ 8126, "kzl" ], +[ 8127, "kzm" ], +[ 8128, "kzn" ], +[ 8129, "kzo" ], +[ 8130, "kzp" ], +[ 8131, "kzq" ], +[ 8132, "kzr" ], +[ 8133, "kzs" ], +[ 8134, "kzt" ], +[ 8135, "kzu" ], +[ 8136, "kzv" ], +[ 8137, "kzw" ], +[ 8138, "kzx" ], +[ 8139, "kzy" ], +[ 8140, "kzz" ], +[ 8141, "laa" ], +[ 8142, "lab" ], +[ 8143, "lac" ], +[ 8144, "lad" ], +[ 8145, "lae" ], +[ 8146, "laf" ], +[ 8147, "lag" ], +[ 8148, "lah" ], +[ 8149, "lai" ], +[ 8150, "laj" ], +[ 8151, "lak" ], +[ 8152, "lal" ], +[ 8153, "lam" ], +[ 8154, "lan" ], +[ 8155, "lao" ], +[ 8156, "lap" ], +[ 8157, "laq" ], +[ 8158, "lar" ], +[ 8159, "las" ], +[ 8160, "lat" ], +[ 8161, "lau" ], +[ 8162, "lav" ], +[ 8163, "law" ], +[ 8164, "lax" ], +[ 8165, "lay" ], +[ 8166, "laz" ], +[ 8167, "lba" ], +[ 8168, "lbb" ], +[ 8169, "lbc" ], +[ 8170, "lbd" ], +[ 8171, "lbe" ], +[ 8172, "lbf" ], +[ 8173, "lbg" ], +[ 8174, "lbh" ], +[ 8175, "lbi" ], +[ 8176, "lbj" ], +[ 8177, "lbk" ], +[ 8178, "lbl" ], +[ 8179, "lbm" ], +[ 8180, "lbn" ], +[ 8181, "lbo" ], +[ 8182, "lbp" ], +[ 8183, "lbq" ], +[ 8184, "lbr" ], +[ 8185, "lbs" ], +[ 8186, "lbt" ], +[ 8187, "lbu" ], +[ 8188, "lbv" ], +[ 8189, "lbw" ], +[ 8190, "lbx" ], +[ 8191, "lby" ], +[ 8192, "lbz" ], +[ 8193, "lca" ], +[ 8194, "lcb" ], +[ 8195, "lcc" ], +[ 8196, "lcd" ], +[ 8197, "lce" ], +[ 8198, "lcf" ], +[ 8199, "lcg" ], +[ 8200, "lch" ], +[ 8201, "lci" ], +[ 8202, "lcj" ], +[ 8203, "lck" ], +[ 8204, "lcl" ], +[ 8205, "lcm" ], +[ 8206, "lcn" ], +[ 8207, "lco" ], +[ 8208, "lcp" ], +[ 8209, "lcq" ], +[ 8210, "lcr" ], +[ 8211, "lcs" ], +[ 8212, "lct" ], +[ 8213, "lcu" ], +[ 8214, "lcv" ], +[ 8215, "lcw" ], +[ 8216, "lcx" ], +[ 8217, "lcy" ], +[ 8218, "lcz" ], +[ 8219, "lda" ], +[ 8220, "ldb" ], +[ 8221, "ldc" ], +[ 8222, "ldd" ], +[ 8223, "lde" ], +[ 8224, "ldf" ], +[ 8225, "ldg" ], +[ 8226, "ldh" ], +[ 8227, "ldi" ], +[ 8228, "ldj" ], +[ 8229, "ldk" ], +[ 8230, "ldl" ], +[ 8231, "ldm" ], +[ 8232, "ldn" ], +[ 8233, "ldo" ], +[ 8234, "ldp" ], +[ 8235, "ldq" ], +[ 8236, "ldr" ], +[ 8237, "lds" ], +[ 8238, "ldt" ], +[ 8239, "ldu" ], +[ 8240, "ldv" ], +[ 8241, "ldw" ], +[ 8242, "ldx" ], +[ 8243, "ldy" ], +[ 8244, "ldz" ], +[ 8245, "lea" ], +[ 8246, "leb" ], +[ 8247, "lec" ], +[ 8248, "led" ], +[ 8249, "lee" ], +[ 8250, "lef" ], +[ 8251, "leg" ], +[ 8252, "leh" ], +[ 8253, "lei" ], +[ 8254, "lej" ], +[ 8255, "lek" ], +[ 8256, "lel" ], +[ 8257, "lem" ], +[ 8258, "len" ], +[ 8259, "leo" ], +[ 8260, "lep" ], +[ 8261, "leq" ], +[ 8262, "ler" ], +[ 8263, "les" ], +[ 8264, "let" ], +[ 8265, "leu" ], +[ 8266, "lev" ], +[ 8267, "lew" ], +[ 8268, "lex" ], +[ 8269, "ley" ], +[ 8270, "lez" ], +[ 8271, "lfa" ], +[ 8272, "lfb" ], +[ 8273, "lfc" ], +[ 8274, "lfd" ], +[ 8275, "lfe" ], +[ 8276, "lff" ], +[ 8277, "lfg" ], +[ 8278, "lfh" ], +[ 8279, "lfi" ], +[ 8280, "lfj" ], +[ 8281, "lfk" ], +[ 8282, "lfl" ], +[ 8283, "lfm" ], +[ 8284, "lfn" ], +[ 8285, "lfo" ], +[ 8286, "lfp" ], +[ 8287, "lfq" ], +[ 8288, "lfr" ], +[ 8289, "lfs" ], +[ 8290, "lft" ], +[ 8291, "lfu" ], +[ 8292, "lfv" ], +[ 8293, "lfw" ], +[ 8294, "lfx" ], +[ 8295, "lfy" ], +[ 8296, "lfz" ], +[ 8297, "lga" ], +[ 8298, "lgb" ], +[ 8299, "lgc" ], +[ 8300, "lgd" ], +[ 8301, "lge" ], +[ 8302, "lgf" ], +[ 8303, "lgg" ], +[ 8304, "lgh" ], +[ 8305, "lgi" ], +[ 8306, "lgj" ], +[ 8307, "lgk" ], +[ 8308, "lgl" ], +[ 8309, "lgm" ], +[ 8310, "lgn" ], +[ 8311, "lgo" ], +[ 8312, "lgp" ], +[ 8313, "lgq" ], +[ 8314, "lgr" ], +[ 8315, "lgs" ], +[ 8316, "lgt" ], +[ 8317, "lgu" ], +[ 8318, "lgv" ], +[ 8319, "lgw" ], +[ 8320, "lgx" ], +[ 8321, "lgy" ], +[ 8322, "lgz" ], +[ 8323, "lha" ], +[ 8324, "lhb" ], +[ 8325, "lhc" ], +[ 8326, "lhd" ], +[ 8327, "lhe" ], +[ 8328, "lhf" ], +[ 8329, "lhg" ], +[ 8330, "lhh" ], +[ 8331, "lhi" ], +[ 8332, "lhj" ], +[ 8333, "lhk" ], +[ 8334, "lhl" ], +[ 8335, "lhm" ], +[ 8336, "lhn" ], +[ 8337, "lho" ], +[ 8338, "lhp" ], +[ 8339, "lhq" ], +[ 8340, "lhr" ], +[ 8341, "lhs" ], +[ 8342, "lht" ], +[ 8343, "lhu" ], +[ 8344, "lhv" ], +[ 8345, "lhw" ], +[ 8346, "lhx" ], +[ 8347, "lhy" ], +[ 8348, "lhz" ], +[ 8349, "lia" ], +[ 8350, "lib" ], +[ 8351, "lic" ], +[ 8352, "lid" ], +[ 8353, "lie" ], +[ 8354, "lif" ], +[ 8355, "lig" ], +[ 8356, "lih" ], +[ 8357, "lii" ], +[ 8358, "lij" ], +[ 8359, "lik" ], +[ 8360, "lil" ], +[ 8361, "lim" ], +[ 8362, "lin" ], +[ 8363, "lio" ], +[ 8364, "lip" ], +[ 8365, "liq" ], +[ 8366, "lir" ], +[ 8367, "lis" ], +[ 8368, "lit" ], +[ 8369, "liu" ], +[ 8370, "liv" ], +[ 8371, "liw" ], +[ 8372, "lix" ], +[ 8373, "liy" ], +[ 8374, "liz" ], +[ 8375, "lja" ], +[ 8376, "ljb" ], +[ 8377, "ljc" ], +[ 8378, "ljd" ], +[ 8379, "lje" ], +[ 8380, "ljf" ], +[ 8381, "ljg" ], +[ 8382, "ljh" ], +[ 8383, "lji" ], +[ 8384, "ljj" ], +[ 8385, "ljk" ], +[ 8386, "ljl" ], +[ 8387, "ljm" ], +[ 8388, "ljn" ], +[ 8389, "ljo" ], +[ 8390, "ljp" ], +[ 8391, "ljq" ], +[ 8392, "ljr" ], +[ 8393, "ljs" ], +[ 8394, "ljt" ], +[ 8395, "lju" ], +[ 8396, "ljv" ], +[ 8397, "ljw" ], +[ 8398, "ljx" ], +[ 8399, "ljy" ], +[ 8400, "ljz" ], +[ 8401, "lka" ], +[ 8402, "lkb" ], +[ 8403, "lkc" ], +[ 8404, "lkd" ], +[ 8405, "lke" ], +[ 8406, "lkf" ], +[ 8407, "lkg" ], +[ 8408, "lkh" ], +[ 8409, "lki" ], +[ 8410, "lkj" ], +[ 8411, "lkk" ], +[ 8412, "lkl" ], +[ 8413, "lkm" ], +[ 8414, "lkn" ], +[ 8415, "lko" ], +[ 8416, "lkp" ], +[ 8417, "lkq" ], +[ 8418, "lkr" ], +[ 8419, "lks" ], +[ 8420, "lkt" ], +[ 8421, "lku" ], +[ 8422, "lkv" ], +[ 8423, "lkw" ], +[ 8424, "lkx" ], +[ 8425, "lky" ], +[ 8426, "lkz" ], +[ 8427, "lla" ], +[ 8428, "llb" ], +[ 8429, "llc" ], +[ 8430, "lld" ], +[ 8431, "lle" ], +[ 8432, "llf" ], +[ 8433, "llg" ], +[ 8434, "llh" ], +[ 8435, "lli" ], +[ 8436, "llj" ], +[ 8437, "llk" ], +[ 8438, "lll" ], +[ 8439, "llm" ], +[ 8440, "lln" ], +[ 8441, "llo" ], +[ 8442, "llp" ], +[ 8443, "llq" ], +[ 8444, "llr" ], +[ 8445, "lls" ], +[ 8446, "llt" ], +[ 8447, "llu" ], +[ 8448, "llv" ], +[ 8449, "llw" ], +[ 8450, "llx" ], +[ 8451, "lly" ], +[ 8452, "llz" ], +[ 8453, "lma" ], +[ 8454, "lmb" ], +[ 8455, "lmc" ], +[ 8456, "lmd" ], +[ 8457, "lme" ], +[ 8458, "lmf" ], +[ 8459, "lmg" ], +[ 8460, "lmh" ], +[ 8461, "lmi" ], +[ 8462, "lmj" ], +[ 8463, "lmk" ], +[ 8464, "lml" ], +[ 8465, "lmm" ], +[ 8466, "lmn" ], +[ 8467, "lmo" ], +[ 8468, "lmp" ], +[ 8469, "lmq" ], +[ 8470, "lmr" ], +[ 8471, "lms" ], +[ 8472, "lmt" ], +[ 8473, "lmu" ], +[ 8474, "lmv" ], +[ 8475, "lmw" ], +[ 8476, "lmx" ], +[ 8477, "lmy" ], +[ 8478, "lmz" ], +[ 8479, "lna" ], +[ 8480, "lnb" ], +[ 8481, "lnc" ], +[ 8482, "lnd" ], +[ 8483, "lne" ], +[ 8484, "lnf" ], +[ 8485, "lng" ], +[ 8486, "lnh" ], +[ 8487, "lni" ], +[ 8488, "lnj" ], +[ 8489, "lnk" ], +[ 8490, "lnl" ], +[ 8491, "lnm" ], +[ 8492, "lnn" ], +[ 8493, "lno" ], +[ 8494, "lnp" ], +[ 8495, "lnq" ], +[ 8496, "lnr" ], +[ 8497, "lns" ], +[ 8498, "lnt" ], +[ 8499, "lnu" ], +[ 8500, "lnv" ], +[ 8501, "lnw" ], +[ 8502, "lnx" ], +[ 8503, "lny" ], +[ 8504, "lnz" ], +[ 8505, "loa" ], +[ 8506, "lob" ], +[ 8507, "loc" ], +[ 8508, "lod" ], +[ 8509, "loe" ], +[ 8510, "lof" ], +[ 8511, "log" ], +[ 8512, "loh" ], +[ 8513, "loi" ], +[ 8514, "loj" ], +[ 8515, "lok" ], +[ 8516, "lol" ], +[ 8517, "lom" ], +[ 8518, "lon" ], +[ 8519, "loo" ], +[ 8520, "lop" ], +[ 8521, "loq" ], +[ 8522, "lor" ], +[ 8523, "los" ], +[ 8524, "lot" ], +[ 8525, "lou" ], +[ 8526, "lov" ], +[ 8527, "low" ], +[ 8528, "lox" ], +[ 8529, "loy" ], +[ 8530, "loz" ], +[ 8531, "lpa" ], +[ 8532, "lpb" ], +[ 8533, "lpc" ], +[ 8534, "lpd" ], +[ 8535, "lpe" ], +[ 8536, "lpf" ], +[ 8537, "lpg" ], +[ 8538, "lph" ], +[ 8539, "lpi" ], +[ 8540, "lpj" ], +[ 8541, "lpk" ], +[ 8542, "lpl" ], +[ 8543, "lpm" ], +[ 8544, "lpn" ], +[ 8545, "lpo" ], +[ 8546, "lpp" ], +[ 8547, "lpq" ], +[ 8548, "lpr" ], +[ 8549, "lps" ], +[ 8550, "lpt" ], +[ 8551, "lpu" ], +[ 8552, "lpv" ], +[ 8553, "lpw" ], +[ 8554, "lpx" ], +[ 8555, "lpy" ], +[ 8556, "lpz" ], +[ 8557, "lqa" ], +[ 8558, "lqb" ], +[ 8559, "lqc" ], +[ 8560, "lqd" ], +[ 8561, "lqe" ], +[ 8562, "lqf" ], +[ 8563, "lqg" ], +[ 8564, "lqh" ], +[ 8565, "lqi" ], +[ 8566, "lqj" ], +[ 8567, "lqk" ], +[ 8568, "lql" ], +[ 8569, "lqm" ], +[ 8570, "lqn" ], +[ 8571, "lqo" ], +[ 8572, "lqp" ], +[ 8573, "lqq" ], +[ 8574, "lqr" ], +[ 8575, "lqs" ], +[ 8576, "lqt" ], +[ 8577, "lqu" ], +[ 8578, "lqv" ], +[ 8579, "lqw" ], +[ 8580, "lqx" ], +[ 8581, "lqy" ], +[ 8582, "lqz" ], +[ 8583, "lra" ], +[ 8584, "lrb" ], +[ 8585, "lrc" ], +[ 8586, "lrd" ], +[ 8587, "lre" ], +[ 8588, "lrf" ], +[ 8589, "lrg" ], +[ 8590, "lrh" ], +[ 8591, "lri" ], +[ 8592, "lrj" ], +[ 8593, "lrk" ], +[ 8594, "lrl" ], +[ 8595, "lrm" ], +[ 8596, "lrn" ], +[ 8597, "lro" ], +[ 8598, "lrp" ], +[ 8599, "lrq" ], +[ 8600, "lrr" ], +[ 8601, "lrs" ], +[ 8602, "lrt" ], +[ 8603, "lru" ], +[ 8604, "lrv" ], +[ 8605, "lrw" ], +[ 8606, "lrx" ], +[ 8607, "lry" ], +[ 8608, "lrz" ], +[ 8609, "lsa" ], +[ 8610, "lsb" ], +[ 8611, "lsc" ], +[ 8612, "lsd" ], +[ 8613, "lse" ], +[ 8614, "lsf" ], +[ 8615, "lsg" ], +[ 8616, "lsh" ], +[ 8617, "lsi" ], +[ 8618, "lsj" ], +[ 8619, "lsk" ], +[ 8620, "lsl" ], +[ 8621, "lsm" ], +[ 8622, "lsn" ], +[ 8623, "lso" ], +[ 8624, "lsp" ], +[ 8625, "lsq" ], +[ 8626, "lsr" ], +[ 8627, "lss" ], +[ 8628, "lst" ], +[ 8629, "lsu" ], +[ 8630, "lsv" ], +[ 8631, "lsw" ], +[ 8632, "lsx" ], +[ 8633, "lsy" ], +[ 8634, "lsz" ], +[ 8635, "lta" ], +[ 8636, "ltb" ], +[ 8637, "ltc" ], +[ 8638, "ltd" ], +[ 8639, "lte" ], +[ 8640, "ltf" ], +[ 8641, "ltg" ], +[ 8642, "lth" ], +[ 8643, "lti" ], +[ 8644, "ltj" ], +[ 8645, "ltk" ], +[ 8646, "ltl" ], +[ 8647, "ltm" ], +[ 8648, "ltn" ], +[ 8649, "lto" ], +[ 8650, "ltp" ], +[ 8651, "ltq" ], +[ 8652, "ltr" ], +[ 8653, "lts" ], +[ 8654, "ltt" ], +[ 8655, "ltu" ], +[ 8656, "ltv" ], +[ 8657, "ltw" ], +[ 8658, "ltx" ], +[ 8659, "lty" ], +[ 8660, "ltz" ], +[ 8661, "lua" ], +[ 8662, "lub" ], +[ 8663, "luc" ], +[ 8664, "lud" ], +[ 8665, "lue" ], +[ 8666, "luf" ], +[ 8667, "lug" ], +[ 8668, "luh" ], +[ 8669, "lui" ], +[ 8670, "luj" ], +[ 8671, "luk" ], +[ 8672, "lul" ], +[ 8673, "lum" ], +[ 8674, "lun" ], +[ 8675, "luo" ], +[ 8676, "lup" ], +[ 8677, "luq" ], +[ 8678, "lur" ], +[ 8679, "lus" ], +[ 8680, "lut" ], +[ 8681, "luu" ], +[ 8682, "luv" ], +[ 8683, "luw" ], +[ 8684, "lux" ], +[ 8685, "luy" ], +[ 8686, "luz" ], +[ 8687, "lva" ], +[ 8688, "lvb" ], +[ 8689, "lvc" ], +[ 8690, "lvd" ], +[ 8691, "lve" ], +[ 8692, "lvf" ], +[ 8693, "lvg" ], +[ 8694, "lvh" ], +[ 8695, "lvi" ], +[ 8696, "lvj" ], +[ 8697, "lvk" ], +[ 8698, "lvl" ], +[ 8699, "lvm" ], +[ 8700, "lvn" ], +[ 8701, "lvo" ], +[ 8702, "lvp" ], +[ 8703, "lvq" ], +[ 8704, "lvr" ], +[ 8705, "lvs" ], +[ 8706, "lvt" ], +[ 8707, "lvu" ], +[ 8708, "lvv" ], +[ 8709, "lvw" ], +[ 8710, "lvx" ], +[ 8711, "lvy" ], +[ 8712, "lvz" ], +[ 8713, "lwa" ], +[ 8714, "lwb" ], +[ 8715, "lwc" ], +[ 8716, "lwd" ], +[ 8717, "lwe" ], +[ 8718, "lwf" ], +[ 8719, "lwg" ], +[ 8720, "lwh" ], +[ 8721, "lwi" ], +[ 8722, "lwj" ], +[ 8723, "lwk" ], +[ 8724, "lwl" ], +[ 8725, "lwm" ], +[ 8726, "lwn" ], +[ 8727, "lwo" ], +[ 8728, "lwp" ], +[ 8729, "lwq" ], +[ 8730, "lwr" ], +[ 8731, "lws" ], +[ 8732, "lwt" ], +[ 8733, "lwu" ], +[ 8734, "lwv" ], +[ 8735, "lww" ], +[ 8736, "lwx" ], +[ 8737, "lwy" ], +[ 8738, "lwz" ], +[ 8739, "lxa" ], +[ 8740, "lxb" ], +[ 8741, "lxc" ], +[ 8742, "lxd" ], +[ 8743, "lxe" ], +[ 8744, "lxf" ], +[ 8745, "lxg" ], +[ 8746, "lxh" ], +[ 8747, "lxi" ], +[ 8748, "lxj" ], +[ 8749, "lxk" ], +[ 8750, "lxl" ], +[ 8751, "lxm" ], +[ 8752, "lxn" ], +[ 8753, "lxo" ], +[ 8754, "lxp" ], +[ 8755, "lxq" ], +[ 8756, "lxr" ], +[ 8757, "lxs" ], +[ 8758, "lxt" ], +[ 8759, "lxu" ], +[ 8760, "lxv" ], +[ 8761, "lxw" ], +[ 8762, "lxx" ], +[ 8763, "lxy" ], +[ 8764, "lxz" ], +[ 8765, "lya" ], +[ 8766, "lyb" ], +[ 8767, "lyc" ], +[ 8768, "lyd" ], +[ 8769, "lye" ], +[ 8770, "lyf" ], +[ 8771, "lyg" ], +[ 8772, "lyh" ], +[ 8773, "lyi" ], +[ 8774, "lyj" ], +[ 8775, "lyk" ], +[ 8776, "lyl" ], +[ 8777, "lym" ], +[ 8778, "lyn" ], +[ 8779, "lyo" ], +[ 8780, "lyp" ], +[ 8781, "lyq" ], +[ 8782, "lyr" ], +[ 8783, "lys" ], +[ 8784, "lyt" ], +[ 8785, "lyu" ], +[ 8786, "lyv" ], +[ 8787, "lyw" ], +[ 8788, "lyx" ], +[ 8789, "lyy" ], +[ 8790, "lyz" ], +[ 8791, "lza" ], +[ 8792, "lzb" ], +[ 8793, "lzc" ], +[ 8794, "lzd" ], +[ 8795, "lze" ], +[ 8796, "lzf" ], +[ 8797, "lzg" ], +[ 8798, "lzh" ], +[ 8799, "lzi" ], +[ 8800, "lzj" ], +[ 8801, "lzk" ], +[ 8802, "lzl" ], +[ 8803, "lzm" ], +[ 8804, "lzn" ], +[ 8805, "lzo" ], +[ 8806, "lzp" ], +[ 8807, "lzq" ], +[ 8808, "lzr" ], +[ 8809, "lzs" ], +[ 8810, "lzt" ], +[ 8811, "lzu" ], +[ 8812, "lzv" ], +[ 8813, "lzw" ], +[ 8814, "lzx" ], +[ 8815, "lzy" ], +[ 8816, "lzz" ], +[ 8817, "maa" ], +[ 8818, "mab" ], +[ 8819, "mac" ], +[ 8820, "mad" ], +[ 8821, "mae" ], +[ 8822, "maf" ], +[ 8823, "mag" ], +[ 8824, "mah" ], +[ 8825, "mai" ], +[ 8826, "maj" ], +[ 8827, "mak" ], +[ 8828, "mal" ], +[ 8829, "mam" ], +[ 8830, "man" ], +[ 8831, "mao" ], +[ 8832, "map" ], +[ 8833, "maq" ], +[ 8834, "mar" ], +[ 8835, "mas" ], +[ 8836, "mat" ], +[ 8837, "mau" ], +[ 8838, "mav" ], +[ 8839, "maw" ], +[ 8840, "max" ], +[ 8841, "may" ], +[ 8842, "maz" ], +[ 8843, "mba" ], +[ 8844, "mbb" ], +[ 8845, "mbc" ], +[ 8846, "mbd" ], +[ 8847, "mbe" ], +[ 8848, "mbf" ], +[ 8849, "mbg" ], +[ 8850, "mbh" ], +[ 8851, "mbi" ], +[ 8852, "mbj" ], +[ 8853, "mbk" ], +[ 8854, "mbl" ], +[ 8855, "mbm" ], +[ 8856, "mbn" ], +[ 8857, "mbo" ], +[ 8858, "mbp" ], +[ 8859, "mbq" ], +[ 8860, "mbr" ], +[ 8861, "mbs" ], +[ 8862, "mbt" ], +[ 8863, "mbu" ], +[ 8864, "mbv" ], +[ 8865, "mbw" ], +[ 8866, "mbx" ], +[ 8867, "mby" ], +[ 8868, "mbz" ], +[ 8869, "mca" ], +[ 8870, "mcb" ], +[ 8871, "mcc" ], +[ 8872, "mcd" ], +[ 8873, "mce" ], +[ 8874, "mcf" ], +[ 8875, "mcg" ], +[ 8876, "mch" ], +[ 8877, "mci" ], +[ 8878, "mcj" ], +[ 8879, "mck" ], +[ 8880, "mcl" ], +[ 8881, "mcm" ], +[ 8882, "mcn" ], +[ 8883, "mco" ], +[ 8884, "mcp" ], +[ 8885, "mcq" ], +[ 8886, "mcr" ], +[ 8887, "mcs" ], +[ 8888, "mct" ], +[ 8889, "mcu" ], +[ 8890, "mcv" ], +[ 8891, "mcw" ], +[ 8892, "mcx" ], +[ 8893, "mcy" ], +[ 8894, "mcz" ], +[ 8895, "mda" ], +[ 8896, "mdb" ], +[ 8897, "mdc" ], +[ 8898, "mdd" ], +[ 8899, "mde" ], +[ 8900, "mdf" ], +[ 8901, "mdg" ], +[ 8902, "mdh" ], +[ 8903, "mdi" ], +[ 8904, "mdj" ], +[ 8905, "mdk" ], +[ 8906, "mdl" ], +[ 8907, "mdm" ], +[ 8908, "mdn" ], +[ 8909, "mdo" ], +[ 8910, "mdp" ], +[ 8911, "mdq" ], +[ 8912, "mdr" ], +[ 8913, "mds" ], +[ 8914, "mdt" ], +[ 8915, "mdu" ], +[ 8916, "mdv" ], +[ 8917, "mdw" ], +[ 8918, "mdx" ], +[ 8919, "mdy" ], +[ 8920, "mdz" ], +[ 8921, "mea" ], +[ 8922, "meb" ], +[ 8923, "mec" ], +[ 8924, "med" ], +[ 8925, "mee" ], +[ 8926, "mef" ], +[ 8927, "meg" ], +[ 8928, "meh" ], +[ 8929, "mei" ], +[ 8930, "mej" ], +[ 8931, "mek" ], +[ 8932, "mel" ], +[ 8933, "mem" ], +[ 8934, "men" ], +[ 8935, "meo" ], +[ 8936, "mep" ], +[ 8937, "meq" ], +[ 8938, "mer" ], +[ 8939, "mes" ], +[ 8940, "met" ], +[ 8941, "meu" ], +[ 8942, "mev" ], +[ 8943, "mew" ], +[ 8944, "mex" ], +[ 8945, "mey" ], +[ 8946, "mez" ], +[ 8947, "mfa" ], +[ 8948, "mfb" ], +[ 8949, "mfc" ], +[ 8950, "mfd" ], +[ 8951, "mfe" ], +[ 8952, "mff" ], +[ 8953, "mfg" ], +[ 8954, "mfh" ], +[ 8955, "mfi" ], +[ 8956, "mfj" ], +[ 8957, "mfk" ], +[ 8958, "mfl" ], +[ 8959, "mfm" ], +[ 8960, "mfn" ], +[ 8961, "mfo" ], +[ 8962, "mfp" ], +[ 8963, "mfq" ], +[ 8964, "mfr" ], +[ 8965, "mfs" ], +[ 8966, "mft" ], +[ 8967, "mfu" ], +[ 8968, "mfv" ], +[ 8969, "mfw" ], +[ 8970, "mfx" ], +[ 8971, "mfy" ], +[ 8972, "mfz" ], +[ 8973, "mga" ], +[ 8974, "mgb" ], +[ 8975, "mgc" ], +[ 8976, "mgd" ], +[ 8977, "mge" ], +[ 8978, "mgf" ], +[ 8979, "mgg" ], +[ 8980, "mgh" ], +[ 8981, "mgi" ], +[ 8982, "mgj" ], +[ 8983, "mgk" ], +[ 8984, "mgl" ], +[ 8985, "mgm" ], +[ 8986, "mgn" ], +[ 8987, "mgo" ], +[ 8988, "mgp" ], +[ 8989, "mgq" ], +[ 8990, "mgr" ], +[ 8991, "mgs" ], +[ 8992, "mgt" ], +[ 8993, "mgu" ], +[ 8994, "mgv" ], +[ 8995, "mgw" ], +[ 8996, "mgx" ], +[ 8997, "mgy" ], +[ 8998, "mgz" ], +[ 8999, "mha" ], +[ 9000, "mhb" ], +[ 9001, "mhc" ], +[ 9002, "mhd" ], +[ 9003, "mhe" ], +[ 9004, "mhf" ], +[ 9005, "mhg" ], +[ 9006, "mhh" ], +[ 9007, "mhi" ], +[ 9008, "mhj" ], +[ 9009, "mhk" ], +[ 9010, "mhl" ], +[ 9011, "mhm" ], +[ 9012, "mhn" ], +[ 9013, "mho" ], +[ 9014, "mhp" ], +[ 9015, "mhq" ], +[ 9016, "mhr" ], +[ 9017, "mhs" ], +[ 9018, "mht" ], +[ 9019, "mhu" ], +[ 9020, "mhv" ], +[ 9021, "mhw" ], +[ 9022, "mhx" ], +[ 9023, "mhy" ], +[ 9024, "mhz" ], +[ 9025, "mia" ], +[ 9026, "mib" ], +[ 9027, "mic" ], +[ 9028, "mid" ], +[ 9029, "mie" ], +[ 9030, "mif" ], +[ 9031, "mig" ], +[ 9032, "mih" ], +[ 9033, "mii" ], +[ 9034, "mij" ], +[ 9035, "mik" ], +[ 9036, "mil" ], +[ 9037, "mim" ], +[ 9038, "min" ], +[ 9039, "mio" ], +[ 9040, "mip" ], +[ 9041, "miq" ], +[ 9042, "mir" ], +[ 9043, "mis" ], +[ 9044, "mit" ], +[ 9045, "miu" ], +[ 9046, "miv" ], +[ 9047, "miw" ], +[ 9048, "mix" ], +[ 9049, "miy" ], +[ 9050, "miz" ], +[ 9051, "mja" ], +[ 9052, "mjb" ], +[ 9053, "mjc" ], +[ 9054, "mjd" ], +[ 9055, "mje" ], +[ 9056, "mjf" ], +[ 9057, "mjg" ], +[ 9058, "mjh" ], +[ 9059, "mji" ], +[ 9060, "mjj" ], +[ 9061, "mjk" ], +[ 9062, "mjl" ], +[ 9063, "mjm" ], +[ 9064, "mjn" ], +[ 9065, "mjo" ], +[ 9066, "mjp" ], +[ 9067, "mjq" ], +[ 9068, "mjr" ], +[ 9069, "mjs" ], +[ 9070, "mjt" ], +[ 9071, "mju" ], +[ 9072, "mjv" ], +[ 9073, "mjw" ], +[ 9074, "mjx" ], +[ 9075, "mjy" ], +[ 9076, "mjz" ], +[ 9077, "mka" ], +[ 9078, "mkb" ], +[ 9079, "mkc" ], +[ 9080, "mkd" ], +[ 9081, "mke" ], +[ 9082, "mkf" ], +[ 9083, "mkg" ], +[ 9084, "mkh" ], +[ 9085, "mki" ], +[ 9086, "mkj" ], +[ 9087, "mkk" ], +[ 9088, "mkl" ], +[ 9089, "mkm" ], +[ 9090, "mkn" ], +[ 9091, "mko" ], +[ 9092, "mkp" ], +[ 9093, "mkq" ], +[ 9094, "mkr" ], +[ 9095, "mks" ], +[ 9096, "mkt" ], +[ 9097, "mku" ], +[ 9098, "mkv" ], +[ 9099, "mkw" ], +[ 9100, "mkx" ], +[ 9101, "mky" ], +[ 9102, "mkz" ], +[ 9103, "mla" ], +[ 9104, "mlb" ], +[ 9105, "mlc" ], +[ 9106, "mld" ], +[ 9107, "mle" ], +[ 9108, "mlf" ], +[ 9109, "mlg" ], +[ 9110, "mlh" ], +[ 9111, "mli" ], +[ 9112, "mlj" ], +[ 9113, "mlk" ], +[ 9114, "mll" ], +[ 9115, "mlm" ], +[ 9116, "mln" ], +[ 9117, "mlo" ], +[ 9118, "mlp" ], +[ 9119, "mlq" ], +[ 9120, "mlr" ], +[ 9121, "mls" ], +[ 9122, "mlt" ], +[ 9123, "mlu" ], +[ 9124, "mlv" ], +[ 9125, "mlw" ], +[ 9126, "mlx" ], +[ 9127, "mly" ], +[ 9128, "mlz" ], +[ 9129, "mma" ], +[ 9130, "mmb" ], +[ 9131, "mmc" ], +[ 9132, "mmd" ], +[ 9133, "mme" ], +[ 9134, "mmf" ], +[ 9135, "mmg" ], +[ 9136, "mmh" ], +[ 9137, "mmi" ], +[ 9138, "mmj" ], +[ 9139, "mmk" ], +[ 9140, "mml" ], +[ 9141, "mmm" ], +[ 9142, "mmn" ], +[ 9143, "mmo" ], +[ 9144, "mmp" ], +[ 9145, "mmq" ], +[ 9146, "mmr" ], +[ 9147, "mms" ], +[ 9148, "mmt" ], +[ 9149, "mmu" ], +[ 9150, "mmv" ], +[ 9151, "mmw" ], +[ 9152, "mmx" ], +[ 9153, "mmy" ], +[ 9154, "mmz" ], +[ 9155, "mna" ], +[ 9156, "mnb" ], +[ 9157, "mnc" ], +[ 9158, "mnd" ], +[ 9159, "mne" ], +[ 9160, "mnf" ], +[ 9161, "mng" ], +[ 9162, "mnh" ], +[ 9163, "mni" ], +[ 9164, "mnj" ], +[ 9165, "mnk" ], +[ 9166, "mnl" ], +[ 9167, "mnm" ], +[ 9168, "mnn" ], +[ 9169, "mno" ], +[ 9170, "mnp" ], +[ 9171, "mnq" ], +[ 9172, "mnr" ], +[ 9173, "mns" ], +[ 9174, "mnt" ], +[ 9175, "mnu" ], +[ 9176, "mnv" ], +[ 9177, "mnw" ], +[ 9178, "mnx" ], +[ 9179, "mny" ], +[ 9180, "mnz" ], +[ 9181, "moa" ], +[ 9182, "mob" ], +[ 9183, "moc" ], +[ 9184, "mod" ], +[ 9185, "moe" ], +[ 9186, "mof" ], +[ 9187, "mog" ], +[ 9188, "moh" ], +[ 9189, "moi" ], +[ 9190, "moj" ], +[ 9191, "mok" ], +[ 9192, "mol" ], +[ 9193, "mom" ], +[ 9194, "mon" ], +[ 9195, "moo" ], +[ 9196, "mop" ], +[ 9197, "moq" ], +[ 9198, "mor" ], +[ 9199, "mos" ], +[ 9200, "mot" ], +[ 9201, "mou" ], +[ 9202, "mov" ], +[ 9203, "mow" ], +[ 9204, "mox" ], +[ 9205, "moy" ], +[ 9206, "moz" ], +[ 9207, "mpa" ], +[ 9208, "mpb" ], +[ 9209, "mpc" ], +[ 9210, "mpd" ], +[ 9211, "mpe" ], +[ 9212, "mpf" ], +[ 9213, "mpg" ], +[ 9214, "mph" ], +[ 9215, "mpi" ], +[ 9216, "mpj" ], +[ 9217, "mpk" ], +[ 9218, "mpl" ], +[ 9219, "mpm" ], +[ 9220, "mpn" ], +[ 9221, "mpo" ], +[ 9222, "mpp" ], +[ 9223, "mpq" ], +[ 9224, "mpr" ], +[ 9225, "mps" ], +[ 9226, "mpt" ], +[ 9227, "mpu" ], +[ 9228, "mpv" ], +[ 9229, "mpw" ], +[ 9230, "mpx" ], +[ 9231, "mpy" ], +[ 9232, "mpz" ], +[ 9233, "mqa" ], +[ 9234, "mqb" ], +[ 9235, "mqc" ], +[ 9236, "mqd" ], +[ 9237, "mqe" ], +[ 9238, "mqf" ], +[ 9239, "mqg" ], +[ 9240, "mqh" ], +[ 9241, "mqi" ], +[ 9242, "mqj" ], +[ 9243, "mqk" ], +[ 9244, "mql" ], +[ 9245, "mqm" ], +[ 9246, "mqn" ], +[ 9247, "mqo" ], +[ 9248, "mqp" ], +[ 9249, "mqq" ], +[ 9250, "mqr" ], +[ 9251, "mqs" ], +[ 9252, "mqt" ], +[ 9253, "mqu" ], +[ 9254, "mqv" ], +[ 9255, "mqw" ], +[ 9256, "mqx" ], +[ 9257, "mqy" ], +[ 9258, "mqz" ], +[ 9259, "mra" ], +[ 9260, "mrb" ], +[ 9261, "mrc" ], +[ 9262, "mrd" ], +[ 9263, "mre" ], +[ 9264, "mrf" ], +[ 9265, "mrg" ], +[ 9266, "mrh" ], +[ 9267, "mri" ], +[ 9268, "mrj" ], +[ 9269, "mrk" ], +[ 9270, "mrl" ], +[ 9271, "mrm" ], +[ 9272, "mrn" ], +[ 9273, "mro" ], +[ 9274, "mrp" ], +[ 9275, "mrq" ], +[ 9276, "mrr" ], +[ 9277, "mrs" ], +[ 9278, "mrt" ], +[ 9279, "mru" ], +[ 9280, "mrv" ], +[ 9281, "mrw" ], +[ 9282, "mrx" ], +[ 9283, "mry" ], +[ 9284, "mrz" ], +[ 9285, "msa" ], +[ 9286, "msb" ], +[ 9287, "msc" ], +[ 9288, "msd" ], +[ 9289, "mse" ], +[ 9290, "msf" ], +[ 9291, "msg" ], +[ 9292, "msh" ], +[ 9293, "msi" ], +[ 9294, "msj" ], +[ 9295, "msk" ], +[ 9296, "msl" ], +[ 9297, "msm" ], +[ 9298, "msn" ], +[ 9299, "mso" ], +[ 9300, "msp" ], +[ 9301, "msq" ], +[ 9302, "msr" ], +[ 9303, "mss" ], +[ 9304, "mst" ], +[ 9305, "msu" ], +[ 9306, "msv" ], +[ 9307, "msw" ], +[ 9308, "msx" ], +[ 9309, "msy" ], +[ 9310, "msz" ], +[ 9311, "mta" ], +[ 9312, "mtb" ], +[ 9313, "mtc" ], +[ 9314, "mtd" ], +[ 9315, "mte" ], +[ 9316, "mtf" ], +[ 9317, "mtg" ], +[ 9318, "mth" ], +[ 9319, "mti" ], +[ 9320, "mtj" ], +[ 9321, "mtk" ], +[ 9322, "mtl" ], +[ 9323, "mtm" ], +[ 9324, "mtn" ], +[ 9325, "mto" ], +[ 9326, "mtp" ], +[ 9327, "mtq" ], +[ 9328, "mtr" ], +[ 9329, "mts" ], +[ 9330, "mtt" ], +[ 9331, "mtu" ], +[ 9332, "mtv" ], +[ 9333, "mtw" ], +[ 9334, "mtx" ], +[ 9335, "mty" ], +[ 9336, "mtz" ], +[ 9337, "mua" ], +[ 9338, "mub" ], +[ 9339, "muc" ], +[ 9340, "mud" ], +[ 9341, "mue" ], +[ 9342, "muf" ], +[ 9343, "mug" ], +[ 9344, "muh" ], +[ 9345, "mui" ], +[ 9346, "muj" ], +[ 9347, "muk" ], +[ 9348, "mul" ], +[ 9349, "mum" ], +[ 9350, "mun" ], +[ 9351, "muo" ], +[ 9352, "mup" ], +[ 9353, "muq" ], +[ 9354, "mur" ], +[ 9355, "mus" ], +[ 9356, "mut" ], +[ 9357, "muu" ], +[ 9358, "muv" ], +[ 9359, "muw" ], +[ 9360, "mux" ], +[ 9361, "muy" ], +[ 9362, "muz" ], +[ 9363, "mva" ], +[ 9364, "mvb" ], +[ 9365, "mvc" ], +[ 9366, "mvd" ], +[ 9367, "mve" ], +[ 9368, "mvf" ], +[ 9369, "mvg" ], +[ 9370, "mvh" ], +[ 9371, "mvi" ], +[ 9372, "mvj" ], +[ 9373, "mvk" ], +[ 9374, "mvl" ], +[ 9375, "mvm" ], +[ 9376, "mvn" ], +[ 9377, "mvo" ], +[ 9378, "mvp" ], +[ 9379, "mvq" ], +[ 9380, "mvr" ], +[ 9381, "mvs" ], +[ 9382, "mvt" ], +[ 9383, "mvu" ], +[ 9384, "mvv" ], +[ 9385, "mvw" ], +[ 9386, "mvx" ], +[ 9387, "mvy" ], +[ 9388, "mvz" ], +[ 9389, "mwa" ], +[ 9390, "mwb" ], +[ 9391, "mwc" ], +[ 9392, "mwd" ], +[ 9393, "mwe" ], +[ 9394, "mwf" ], +[ 9395, "mwg" ], +[ 9396, "mwh" ], +[ 9397, "mwi" ], +[ 9398, "mwj" ], +[ 9399, "mwk" ], +[ 9400, "mwl" ], +[ 9401, "mwm" ], +[ 9402, "mwn" ], +[ 9403, "mwo" ], +[ 9404, "mwp" ], +[ 9405, "mwq" ], +[ 9406, "mwr" ], +[ 9407, "mws" ], +[ 9408, "mwt" ], +[ 9409, "mwu" ], +[ 9410, "mwv" ], +[ 9411, "mww" ], +[ 9412, "mwx" ], +[ 9413, "mwy" ], +[ 9414, "mwz" ], +[ 9415, "mxa" ], +[ 9416, "mxb" ], +[ 9417, "mxc" ], +[ 9418, "mxd" ], +[ 9419, "mxe" ], +[ 9420, "mxf" ], +[ 9421, "mxg" ], +[ 9422, "mxh" ], +[ 9423, "mxi" ], +[ 9424, "mxj" ], +[ 9425, "mxk" ], +[ 9426, "mxl" ], +[ 9427, "mxm" ], +[ 9428, "mxn" ], +[ 9429, "mxo" ], +[ 9430, "mxp" ], +[ 9431, "mxq" ], +[ 9432, "mxr" ], +[ 9433, "mxs" ], +[ 9434, "mxt" ], +[ 9435, "mxu" ], +[ 9436, "mxv" ], +[ 9437, "mxw" ], +[ 9438, "mxx" ], +[ 9439, "mxy" ], +[ 9440, "mxz" ], +[ 9441, "mya" ], +[ 9442, "myb" ], +[ 9443, "myc" ], +[ 9444, "myd" ], +[ 9445, "mye" ], +[ 9446, "myf" ], +[ 9447, "myg" ], +[ 9448, "myh" ], +[ 9449, "myi" ], +[ 9450, "myj" ], +[ 9451, "myk" ], +[ 9452, "myl" ], +[ 9453, "mym" ], +[ 9454, "myn" ], +[ 9455, "myo" ], +[ 9456, "myp" ], +[ 9457, "myq" ], +[ 9458, "myr" ], +[ 9459, "mys" ], +[ 9460, "myt" ], +[ 9461, "myu" ], +[ 9462, "myv" ], +[ 9463, "myw" ], +[ 9464, "myx" ], +[ 9465, "myy" ], +[ 9466, "myz" ], +[ 9467, "mza" ], +[ 9468, "mzb" ], +[ 9469, "mzc" ], +[ 9470, "mzd" ], +[ 9471, "mze" ], +[ 9472, "mzf" ], +[ 9473, "mzg" ], +[ 9474, "mzh" ], +[ 9475, "mzi" ], +[ 9476, "mzj" ], +[ 9477, "mzk" ], +[ 9478, "mzl" ], +[ 9479, "mzm" ], +[ 9480, "mzn" ], +[ 9481, "mzo" ], +[ 9482, "mzp" ], +[ 9483, "mzq" ], +[ 9484, "mzr" ], +[ 9485, "mzs" ], +[ 9486, "mzt" ], +[ 9487, "mzu" ], +[ 9488, "mzv" ], +[ 9489, "mzw" ], +[ 9490, "mzx" ], +[ 9491, "mzy" ], +[ 9492, "mzz" ], +[ 9493, "naa" ], +[ 9494, "nab" ], +[ 9495, "nac" ], +[ 9496, "nad" ], +[ 9497, "nae" ], +[ 9498, "naf" ], +[ 9499, "nag" ], +[ 9500, "nah" ], +[ 9501, "nai" ], +[ 9502, "naj" ], +[ 9503, "nak" ], +[ 9504, "nal" ], +[ 9505, "nam" ], +[ 9506, "nan" ], +[ 9507, "nao" ], +[ 9508, "nap" ], +[ 9509, "naq" ], +[ 9510, "nar" ], +[ 9511, "nas" ], +[ 9512, "nat" ], +[ 9513, "nau" ], +[ 9514, "nav" ], +[ 9515, "naw" ], +[ 9516, "nax" ], +[ 9517, "nay" ], +[ 9518, "naz" ], +[ 9519, "nba" ], +[ 9520, "nbb" ], +[ 9521, "nbc" ], +[ 9522, "nbd" ], +[ 9523, "nbe" ], +[ 9524, "nbf" ], +[ 9525, "nbg" ], +[ 9526, "nbh" ], +[ 9527, "nbi" ], +[ 9528, "nbj" ], +[ 9529, "nbk" ], +[ 9530, "nbl" ], +[ 9531, "nbm" ], +[ 9532, "nbn" ], +[ 9533, "nbo" ], +[ 9534, "nbp" ], +[ 9535, "nbq" ], +[ 9536, "nbr" ], +[ 9537, "nbs" ], +[ 9538, "nbt" ], +[ 9539, "nbu" ], +[ 9540, "nbv" ], +[ 9541, "nbw" ], +[ 9542, "nbx" ], +[ 9543, "nby" ], +[ 9544, "nbz" ], +[ 9545, "nca" ], +[ 9546, "ncb" ], +[ 9547, "ncc" ], +[ 9548, "ncd" ], +[ 9549, "nce" ], +[ 9550, "ncf" ], +[ 9551, "ncg" ], +[ 9552, "nch" ], +[ 9553, "nci" ], +[ 9554, "ncj" ], +[ 9555, "nck" ], +[ 9556, "ncl" ], +[ 9557, "ncm" ], +[ 9558, "ncn" ], +[ 9559, "nco" ], +[ 9560, "ncp" ], +[ 9561, "ncq" ], +[ 9562, "ncr" ], +[ 9563, "ncs" ], +[ 9564, "nct" ], +[ 9565, "ncu" ], +[ 9566, "ncv" ], +[ 9567, "ncw" ], +[ 9568, "ncx" ], +[ 9569, "ncy" ], +[ 9570, "ncz" ], +[ 9571, "nda" ], +[ 9572, "ndb" ], +[ 9573, "ndc" ], +[ 9574, "ndd" ], +[ 9575, "nde" ], +[ 9576, "ndf" ], +[ 9577, "ndg" ], +[ 9578, "ndh" ], +[ 9579, "ndi" ], +[ 9580, "ndj" ], +[ 9581, "ndk" ], +[ 9582, "ndl" ], +[ 9583, "ndm" ], +[ 9584, "ndn" ], +[ 9585, "ndo" ], +[ 9586, "ndp" ], +[ 9587, "ndq" ], +[ 9588, "ndr" ], +[ 9589, "nds" ], +[ 9590, "ndt" ], +[ 9591, "ndu" ], +[ 9592, "ndv" ], +[ 9593, "ndw" ], +[ 9594, "ndx" ], +[ 9595, "ndy" ], +[ 9596, "ndz" ], +[ 9597, "nea" ], +[ 9598, "neb" ], +[ 9599, "nec" ], +[ 9600, "ned" ], +[ 9601, "nee" ], +[ 9602, "nef" ], +[ 9603, "neg" ], +[ 9604, "neh" ], +[ 9605, "nei" ], +[ 9606, "nej" ], +[ 9607, "nek" ], +[ 9608, "nel" ], +[ 9609, "nem" ], +[ 9610, "nen" ], +[ 9611, "neo" ], +[ 9612, "nep" ], +[ 9613, "neq" ], +[ 9614, "ner" ], +[ 9615, "nes" ], +[ 9616, "net" ], +[ 9617, "neu" ], +[ 9618, "nev" ], +[ 9619, "new" ], +[ 9620, "nex" ], +[ 9621, "ney" ], +[ 9622, "nez" ], +[ 9623, "nfa" ], +[ 9624, "nfb" ], +[ 9625, "nfc" ], +[ 9626, "nfd" ], +[ 9627, "nfe" ], +[ 9628, "nff" ], +[ 9629, "nfg" ], +[ 9630, "nfh" ], +[ 9631, "nfi" ], +[ 9632, "nfj" ], +[ 9633, "nfk" ], +[ 9634, "nfl" ], +[ 9635, "nfm" ], +[ 9636, "nfn" ], +[ 9637, "nfo" ], +[ 9638, "nfp" ], +[ 9639, "nfq" ], +[ 9640, "nfr" ], +[ 9641, "nfs" ], +[ 9642, "nft" ], +[ 9643, "nfu" ], +[ 9644, "nfv" ], +[ 9645, "nfw" ], +[ 9646, "nfx" ], +[ 9647, "nfy" ], +[ 9648, "nfz" ], +[ 9649, "nga" ], +[ 9650, "ngb" ], +[ 9651, "ngc" ], +[ 9652, "ngd" ], +[ 9653, "nge" ], +[ 9654, "ngf" ], +[ 9655, "ngg" ], +[ 9656, "ngh" ], +[ 9657, "ngi" ], +[ 9658, "ngj" ], +[ 9659, "ngk" ], +[ 9660, "ngl" ], +[ 9661, "ngm" ], +[ 9662, "ngn" ], +[ 9663, "ngo" ], +[ 9664, "ngp" ], +[ 9665, "ngq" ], +[ 9666, "ngr" ], +[ 9667, "ngs" ], +[ 9668, "ngt" ], +[ 9669, "ngu" ], +[ 9670, "ngv" ], +[ 9671, "ngw" ], +[ 9672, "ngx" ], +[ 9673, "ngy" ], +[ 9674, "ngz" ], +[ 9675, "nha" ], +[ 9676, "nhb" ], +[ 9677, "nhc" ], +[ 9678, "nhd" ], +[ 9679, "nhe" ], +[ 9680, "nhf" ], +[ 9681, "nhg" ], +[ 9682, "nhh" ], +[ 9683, "nhi" ], +[ 9684, "nhj" ], +[ 9685, "nhk" ], +[ 9686, "nhl" ], +[ 9687, "nhm" ], +[ 9688, "nhn" ], +[ 9689, "nho" ], +[ 9690, "nhp" ], +[ 9691, "nhq" ], +[ 9692, "nhr" ], +[ 9693, "nhs" ], +[ 9694, "nht" ], +[ 9695, "nhu" ], +[ 9696, "nhv" ], +[ 9697, "nhw" ], +[ 9698, "nhx" ], +[ 9699, "nhy" ], +[ 9700, "nhz" ], +[ 9701, "nia" ], +[ 9702, "nib" ], +[ 9703, "nic" ], +[ 9704, "nid" ], +[ 9705, "nie" ], +[ 9706, "nif" ], +[ 9707, "nig" ], +[ 9708, "nih" ], +[ 9709, "nii" ], +[ 9710, "nij" ], +[ 9711, "nik" ], +[ 9712, "nil" ], +[ 9713, "nim" ], +[ 9714, "nin" ], +[ 9715, "nio" ], +[ 9716, "nip" ], +[ 9717, "niq" ], +[ 9718, "nir" ], +[ 9719, "nis" ], +[ 9720, "nit" ], +[ 9721, "niu" ], +[ 9722, "niv" ], +[ 9723, "niw" ], +[ 9724, "nix" ], +[ 9725, "niy" ], +[ 9726, "niz" ], +[ 9727, "nja" ], +[ 9728, "njb" ], +[ 9729, "njc" ], +[ 9730, "njd" ], +[ 9731, "nje" ], +[ 9732, "njf" ], +[ 9733, "njg" ], +[ 9734, "njh" ], +[ 9735, "nji" ], +[ 9736, "njj" ], +[ 9737, "njk" ], +[ 9738, "njl" ], +[ 9739, "njm" ], +[ 9740, "njn" ], +[ 9741, "njo" ], +[ 9742, "njp" ], +[ 9743, "njq" ], +[ 9744, "njr" ], +[ 9745, "njs" ], +[ 9746, "njt" ], +[ 9747, "nju" ], +[ 9748, "njv" ], +[ 9749, "njw" ], +[ 9750, "njx" ], +[ 9751, "njy" ], +[ 9752, "njz" ], +[ 9753, "nka" ], +[ 9754, "nkb" ], +[ 9755, "nkc" ], +[ 9756, "nkd" ], +[ 9757, "nke" ], +[ 9758, "nkf" ], +[ 9759, "nkg" ], +[ 9760, "nkh" ], +[ 9761, "nki" ], +[ 9762, "nkj" ], +[ 9763, "nkk" ], +[ 9764, "nkl" ], +[ 9765, "nkm" ], +[ 9766, "nkn" ], +[ 9767, "nko" ], +[ 9768, "nkp" ], +[ 9769, "nkq" ], +[ 9770, "nkr" ], +[ 9771, "nks" ], +[ 9772, "nkt" ], +[ 9773, "nku" ], +[ 9774, "nkv" ], +[ 9775, "nkw" ], +[ 9776, "nkx" ], +[ 9777, "nky" ], +[ 9778, "nkz" ], +[ 9779, "nla" ], +[ 9780, "nlb" ], +[ 9781, "nlc" ], +[ 9782, "nld" ], +[ 9783, "nle" ], +[ 9784, "nlf" ], +[ 9785, "nlg" ], +[ 9786, "nlh" ], +[ 9787, "nli" ], +[ 9788, "nlj" ], +[ 9789, "nlk" ], +[ 9790, "nll" ], +[ 9791, "nlm" ], +[ 9792, "nln" ], +[ 9793, "nlo" ], +[ 9794, "nlp" ], +[ 9795, "nlq" ], +[ 9796, "nlr" ], +[ 9797, "nls" ], +[ 9798, "nlt" ], +[ 9799, "nlu" ], +[ 9800, "nlv" ], +[ 9801, "nlw" ], +[ 9802, "nlx" ], +[ 9803, "nly" ], +[ 9804, "nlz" ], +[ 9805, "nma" ], +[ 9806, "nmb" ], +[ 9807, "nmc" ], +[ 9808, "nmd" ], +[ 9809, "nme" ], +[ 9810, "nmf" ], +[ 9811, "nmg" ], +[ 9812, "nmh" ], +[ 9813, "nmi" ], +[ 9814, "nmj" ], +[ 9815, "nmk" ], +[ 9816, "nml" ], +[ 9817, "nmm" ], +[ 9818, "nmn" ], +[ 9819, "nmo" ], +[ 9820, "nmp" ], +[ 9821, "nmq" ], +[ 9822, "nmr" ], +[ 9823, "nms" ], +[ 9824, "nmt" ], +[ 9825, "nmu" ], +[ 9826, "nmv" ], +[ 9827, "nmw" ], +[ 9828, "nmx" ], +[ 9829, "nmy" ], +[ 9830, "nmz" ], +[ 9831, "nna" ], +[ 9832, "nnb" ], +[ 9833, "nnc" ], +[ 9834, "nnd" ], +[ 9835, "nne" ], +[ 9836, "nnf" ], +[ 9837, "nng" ], +[ 9838, "nnh" ], +[ 9839, "nni" ], +[ 9840, "nnj" ], +[ 9841, "nnk" ], +[ 9842, "nnl" ], +[ 9843, "nnm" ], +[ 9844, "nnn" ], +[ 9845, "nno" ], +[ 9846, "nnp" ], +[ 9847, "nnq" ], +[ 9848, "nnr" ], +[ 9849, "nns" ], +[ 9850, "nnt" ], +[ 9851, "nnu" ], +[ 9852, "nnv" ], +[ 9853, "nnw" ], +[ 9854, "nnx" ], +[ 9855, "nny" ], +[ 9856, "nnz" ], +[ 9857, "noa" ], +[ 9858, "nob" ], +[ 9859, "noc" ], +[ 9860, "nod" ], +[ 9861, "noe" ], +[ 9862, "nof" ], +[ 9863, "nog" ], +[ 9864, "noh" ], +[ 9865, "noi" ], +[ 9866, "noj" ], +[ 9867, "nok" ], +[ 9868, "nol" ], +[ 9869, "nom" ], +[ 9870, "non" ], +[ 9871, "noo" ], +[ 9872, "nop" ], +[ 9873, "noq" ], +[ 9874, "nor" ], +[ 9875, "nos" ], +[ 9876, "not" ], +[ 9877, "nou" ], +[ 9878, "nov" ], +[ 9879, "now" ], +[ 9880, "nox" ], +[ 9881, "noy" ], +[ 9882, "noz" ], +[ 9883, "npa" ], +[ 9884, "npb" ], +[ 9885, "npc" ], +[ 9886, "npd" ], +[ 9887, "npe" ], +[ 9888, "npf" ], +[ 9889, "npg" ], +[ 9890, "nph" ], +[ 9891, "npi" ], +[ 9892, "npj" ], +[ 9893, "npk" ], +[ 9894, "npl" ], +[ 9895, "npm" ], +[ 9896, "npn" ], +[ 9897, "npo" ], +[ 9898, "npp" ], +[ 9899, "npq" ], +[ 9900, "npr" ], +[ 9901, "nps" ], +[ 9902, "npt" ], +[ 9903, "npu" ], +[ 9904, "npv" ], +[ 9905, "npw" ], +[ 9906, "npx" ], +[ 9907, "npy" ], +[ 9908, "npz" ], +[ 9909, "nqa" ], +[ 9910, "nqb" ], +[ 9911, "nqc" ], +[ 9912, "nqd" ], +[ 9913, "nqe" ], +[ 9914, "nqf" ], +[ 9915, "nqg" ], +[ 9916, "nqh" ], +[ 9917, "nqi" ], +[ 9918, "nqj" ], +[ 9919, "nqk" ], +[ 9920, "nql" ], +[ 9921, "nqm" ], +[ 9922, "nqn" ], +[ 9923, "nqo" ], +[ 9924, "nqp" ], +[ 9925, "nqq" ], +[ 9926, "nqr" ], +[ 9927, "nqs" ], +[ 9928, "nqt" ], +[ 9929, "nqu" ], +[ 9930, "nqv" ], +[ 9931, "nqw" ], +[ 9932, "nqx" ], +[ 9933, "nqy" ], +[ 9934, "nqz" ], +[ 9935, "nra" ], +[ 9936, "nrb" ], +[ 9937, "nrc" ], +[ 9938, "nrd" ], +[ 9939, "nre" ], +[ 9940, "nrf" ], +[ 9941, "nrg" ], +[ 9942, "nrh" ], +[ 9943, "nri" ], +[ 9944, "nrj" ], +[ 9945, "nrk" ], +[ 9946, "nrl" ], +[ 9947, "nrm" ], +[ 9948, "nrn" ], +[ 9949, "nro" ], +[ 9950, "nrp" ], +[ 9951, "nrq" ], +[ 9952, "nrr" ], +[ 9953, "nrs" ], +[ 9954, "nrt" ], +[ 9955, "nru" ], +[ 9956, "nrv" ], +[ 9957, "nrw" ], +[ 9958, "nrx" ], +[ 9959, "nry" ], +[ 9960, "nrz" ], +[ 9961, "nsa" ], +[ 9962, "nsb" ], +[ 9963, "nsc" ], +[ 9964, "nsd" ], +[ 9965, "nse" ], +[ 9966, "nsf" ], +[ 9967, "nsg" ], +[ 9968, "nsh" ], +[ 9969, "nsi" ], +[ 9970, "nsj" ], +[ 9971, "nsk" ], +[ 9972, "nsl" ], +[ 9973, "nsm" ], +[ 9974, "nsn" ], +[ 9975, "nso" ], +[ 9976, "nsp" ], +[ 9977, "nsq" ], +[ 9978, "nsr" ], +[ 9979, "nss" ], +[ 9980, "nst" ], +[ 9981, "nsu" ], +[ 9982, "nsv" ], +[ 9983, "nsw" ], +[ 9984, "nsx" ], +[ 9985, "nsy" ], +[ 9986, "nsz" ], +[ 9987, "nta" ], +[ 9988, "ntb" ], +[ 9989, "ntc" ], +[ 9990, "ntd" ], +[ 9991, "nte" ], +[ 9992, "ntf" ], +[ 9993, "ntg" ], +[ 9994, "nth" ], +[ 9995, "nti" ], +[ 9996, "ntj" ], +[ 9997, "ntk" ], +[ 9998, "ntl" ], +[ 9999, "ntm" ], +[ 10000, "ntn" ], + ]); + + +## make sure populate honors fields/orders in list context +## schema order +my @links = $schema->populate('Link', [ +[ qw/id url title/ ], +[ qw/2 burl btitle/ ] +]); +is(scalar @links, 1); + +my $link2 = shift @links; +is($link2->id, 2, 'Link 2 id'); +is($link2->url, 'burl', 'Link 2 url'); +is($link2->title, 'btitle', 'Link 2 title'); + +## non-schema order +@links = $schema->populate('Link', [ +[ qw/id title url/ ], +[ qw/3 ctitle curl/ ] +]); +is(scalar @links, 1); + +my $link3 = shift @links; +is($link3->id, 3, 'Link 3 id'); +is($link3->url, 'curl', 'Link 3 url'); +is($link3->title, 'ctitle', 'Link 3 title'); + +## not all physical columns +@links = $schema->populate('Link', [ +[ qw/id title/ ], +[ qw/4 dtitle/ ] +]); +is(scalar @links, 1); + +my $link4 = shift @links; +is($link4->id, 4, 'Link 4 id'); +is($link4->url, undef, 'Link 4 url'); +is($link4->title, 'dtitle', 'Link 4 title'); + + +## make sure populate -> insert_bulk honors fields/orders in void context +## schema order +$schema->populate('Link', [ +[ qw/id url title/ ], +[ qw/5 eurl etitle/ ] +]); +my $link5 = $schema->resultset('Link')->find(5); +is($link5->id, 5, 'Link 5 id'); +is($link5->url, 'eurl', 'Link 5 url'); +is($link5->title, 'etitle', 'Link 5 title'); + +## non-schema order +$schema->populate('Link', [ +[ qw/id title url/ ], +[ qw/6 ftitle furl/ ] +]); +my $link6 = $schema->resultset('Link')->find(6); +is($link6->id, 6, 'Link 6 id'); +is($link6->url, 'furl', 'Link 6 url'); +is($link6->title, 'ftitle', 'Link 6 title'); + +## not all physical columns +$schema->populate('Link', [ +[ qw/id title/ ], +[ qw/7 gtitle/ ] +]); +my $link7 = $schema->resultset('Link')->find(7); +is($link7->id, 7, 'Link 7 id'); +is($link7->url, undef, 'Link 7 url'); +is($link7->title, 'gtitle', 'Link 7 title'); + + +ok(-f "t/var/DBIxClass.db", 'Database created'); diff --git a/t/19quotes.t b/t/19quotes.t index 303f297..646131b 100644 --- a/t/19quotes.t +++ b/t/19quotes.t @@ -14,44 +14,44 @@ BEGIN { use lib qw(t/lib); use_ok('DBICTest'); -DBICTest->init_schema(); +my $schema = DBICTest->init_schema(); -my $orig_debugcb = DBICTest->schema->storage->debugcb; -my $orig_debug = DBICTest->schema->storage->debug; +my $orig_debugcb = $schema->storage->debugcb; +my $orig_debug = $schema->storage->debug; -diag('Testing against ' . join(' ', map { DBICTest->schema->storage->dbh->get_info($_) } qw/17 18/)); +diag('Testing against ' . join(' ', map { $schema->storage->dbh->get_info($_) } qw/17 18/)); -DBICTest->schema->storage->sql_maker->quote_char('`'); -DBICTest->schema->storage->sql_maker->name_sep('.'); +$schema->storage->sql_maker->quote_char('`'); +$schema->storage->sql_maker->name_sep('.'); my $sql = ''; -DBICTest->schema->storage->debugcb(sub { $sql = $_[1] }); -DBICTest->schema->storage->debug(1); +$schema->storage->debugcb(sub { $sql = $_[1] }); +$schema->storage->debug(1); my $rs; -$rs = DBICTest::CD->search( +$rs = $schema->resultset('CD')->search( { 'me.year' => 2001, 'artist.name' => 'Caterwauler McCrae' }, { join => 'artist' }); eval { $rs->count }; like($sql, qr/\QSELECT COUNT( * ) FROM `cd` `me` JOIN `artist` `artist` ON ( `artist`.`artistid` = `me`.`artist` ) WHERE ( `artist`.`name` = ? AND `me`.`year` = ? )\E/, 'got correct SQL for count query with quoting'); my $order = 'year DESC'; -$rs = DBICTest::CD->search({}, +$rs = $schema->resultset('CD')->search({}, { 'order_by' => $order }); eval { $rs->first }; like($sql, qr/ORDER BY `\Q${order}\E`/, 'quoted ORDER BY with DESC (should use a scalarref anyway)'); -$rs = DBICTest::CD->search({}, +$rs = $schema->resultset('CD')->search({}, { 'order_by' => \$order }); eval { $rs->first }; like($sql, qr/ORDER BY \Q${order}\E/, 'did not quote ORDER BY with scalarref'); -DBICTest->schema->storage->sql_maker->quote_char([qw/[ ]/]); -DBICTest->schema->storage->sql_maker->name_sep('.'); +$schema->storage->sql_maker->quote_char([qw/[ ]/]); +$schema->storage->sql_maker->name_sep('.'); -$rs = DBICTest::CD->search( +$rs = $schema->resultset('CD')->search( { 'me.year' => 2001, 'artist.name' => 'Caterwauler McCrae' }, { join => 'artist' }); eval { $rs->count }; @@ -62,10 +62,10 @@ my %data = ( order => '12' ); -DBICTest->schema->storage->sql_maker->quote_char('`'); -DBICTest->schema->storage->sql_maker->name_sep('.'); +$schema->storage->sql_maker->quote_char('`'); +$schema->storage->sql_maker->name_sep('.'); -is(DBICTest->schema->storage->sql_maker->update('group', \%data), 'UPDATE `group` SET `name` = ?, `order` = ?', 'quoted table names for UPDATE'); +is($schema->storage->sql_maker->update('group', \%data), 'UPDATE `group` SET `name` = ?, `order` = ?', 'quoted table names for UPDATE'); -DBICTest->schema->storage->debugcb($orig_debugcb); -DBICTest->schema->storage->debug($orig_debug); +$schema->storage->debugcb($orig_debugcb); +$schema->storage->debug($orig_debug); diff --git a/t/19quotes_newstyle.t b/t/19quotes_newstyle.t index 31feaa3..b9d7411 100644 --- a/t/19quotes_newstyle.t +++ b/t/19quotes_newstyle.t @@ -14,44 +14,44 @@ BEGIN { use lib qw(t/lib); use_ok('DBICTest'); -DBICTest->init_schema(); +my $schema = DBICTest->init_schema(); -my $orig_debugcb = DBICTest->schema->storage->debugcb; -my $orig_debug = DBICTest->schema->storage->debug; +my $orig_debugcb = $schema->storage->debugcb; +my $orig_debug = $schema->storage->debug; -diag('Testing against ' . join(' ', map { DBICTest->schema->storage->dbh->get_info($_) } qw/17 18/)); +diag('Testing against ' . join(' ', map { $schema->storage->dbh->get_info($_) } qw/17 18/)); -my $dsn = DBICTest->schema->storage->connect_info->[0]; -DBICTest->schema->connection($dsn, { quote_char => '`', name_sep => '.' }); +my $dsn = $schema->storage->connect_info->[0]; +$schema->connection($dsn, { quote_char => '`', name_sep => '.' }); my $sql = ''; -DBICTest->schema->storage->debugcb(sub { $sql = $_[1] }); -DBICTest->schema->storage->debug(1); +$schema->storage->debugcb(sub { $sql = $_[1] }); +$schema->storage->debug(1); my $rs; -$rs = DBICTest::CD->search( +$rs = $schema->resultset('CD')->search( { 'me.year' => 2001, 'artist.name' => 'Caterwauler McCrae' }, { join => 'artist' }); eval { $rs->count }; like($sql, qr/\QSELECT COUNT( * ) FROM `cd` `me` JOIN `artist` `artist` ON ( `artist`.`artistid` = `me`.`artist` ) WHERE ( `artist`.`name` = ? AND `me`.`year` = ? )\E/, 'got correct SQL for count query with quoting'); my $order = 'year DESC'; -$rs = DBICTest::CD->search({}, +$rs = $schema->resultset('CD')->search({}, { 'order_by' => $order }); eval { $rs->first }; like($sql, qr/ORDER BY `\Q${order}\E`/, 'quoted ORDER BY with DESC (should use a scalarref anyway)'); -$rs = DBICTest::CD->search({}, +$rs = $schema->resultset('CD')->search({}, { 'order_by' => \$order }); eval { $rs->first }; like($sql, qr/ORDER BY \Q${order}\E/, 'did not quote ORDER BY with scalarref'); -DBICTest->schema->connection($dsn, { quote_char => [qw/[ ]/], name_sep => '.' }); -DBICTest->schema->storage->debugcb(sub { $sql = $_[1] }); -DBICTest->schema->storage->debug(1); +$schema->connection($dsn, { quote_char => [qw/[ ]/], name_sep => '.' }); +$schema->storage->debugcb(sub { $sql = $_[1] }); +$schema->storage->debug(1); -$rs = DBICTest::CD->search( +$rs = $schema->resultset('CD')->search( { 'me.year' => 2001, 'artist.name' => 'Caterwauler McCrae' }, { join => 'artist' }); eval { $rs->count }; @@ -62,9 +62,9 @@ my %data = ( order => '12' ); -DBICTest->schema->connection($dsn, { quote_char => '`', name_sep => '.' }); +$schema->connection($dsn, { quote_char => '`', name_sep => '.' }); -is(DBICTest->schema->storage->sql_maker->update('group', \%data), 'UPDATE `group` SET `name` = ?, `order` = ?', 'quoted table names for UPDATE'); +is($schema->storage->sql_maker->update('group', \%data), 'UPDATE `group` SET `name` = ?, `order` = ?', 'quoted table names for UPDATE'); -DBICTest->schema->storage->debugcb($orig_debugcb); -DBICTest->schema->storage->debug($orig_debug); +$schema->storage->debugcb($orig_debugcb); +$schema->storage->debug($orig_debug); diff --git a/t/31stats.t b/t/31stats.t index 59aeb9e..9f0da40 100644 --- a/t/31stats.t +++ b/t/31stats.t @@ -14,23 +14,23 @@ BEGIN { use lib qw(t/lib); use_ok('DBICTest'); -DBICTest->init_schema(); +my $schema = DBICTest->init_schema(); my $cbworks = 0; -DBICTest->schema->storage->debugcb(sub { $cbworks = 1; }); -DBICTest->schema->storage->debug(0); -my $rs = DBICTest::CD->search({}); +$schema->storage->debugcb(sub { $cbworks = 1; }); +$schema->storage->debug(0); +my $rs = $schema->resultset('CD')->search({}); $rs->count(); ok(!$cbworks, 'Callback not called with debug disabled'); -DBICTest->schema->storage->debug(1); +$schema->storage->debug(1); $rs->count(); ok($cbworks, 'Debug callback worked.'); my $prof = new DBIx::Test::Profiler(); -DBICTest->schema->storage->debugobj($prof); +$schema->storage->debugobj($prof); # Test non-transaction calls. $rs->count(); @@ -42,27 +42,27 @@ ok(!$prof->{'txn_commit'}, 'txn_commit not called'); $prof->reset(); # Test transaction calls -DBICTest->schema->txn_begin(); +$schema->txn_begin(); ok($prof->{'txn_begin'}, 'txn_begin called'); -$rs = DBICTest::CD->search({}); +$rs = $schema->resultset('CD')->search({}); $rs->count(); ok($prof->{'query_start'}, 'query_start called'); ok($prof->{'query_end'}, 'query_end called'); -DBICTest->schema->txn_commit(); +$schema->txn_commit(); ok($prof->{'txn_commit'}, 'txn_commit called'); $prof->reset(); # Test a rollback -DBICTest->schema->txn_begin(); -$rs = DBICTest::CD->search({}); +$schema->txn_begin(); +$rs = $schema->resultset('CD')->search({}); $rs->count(); -DBICTest->schema->txn_rollback(); +$schema->txn_rollback(); ok($prof->{'txn_rollback'}, 'txn_rollback called'); -DBICTest->schema->storage->debug(0); +$schema->storage->debug(0); package DBIx::Test::Profiler; use strict; diff --git a/t/33storage_reconnect.t b/t/33storage_reconnect.t new file mode 100644 index 0000000..6e82b13 --- /dev/null +++ b/t/33storage_reconnect.t @@ -0,0 +1,26 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; + +plan tests => 2; + +# Set up the "usual" sqlite for DBICTest +my $schema = DBICTest->init_schema; + +# Make sure we're connected by doing something +my @art = $schema->resultset("Artist")->search({ }, { order_by => 'name DESC'}); +cmp_ok(@art, '==', 3, "Three artists returned"); + +# Disconnect the dbh, and be sneaky about it +$schema->storage->_dbh->disconnect; + +# Try the operation again - What should happen here is: +# 1. S::DBI blindly attempts the SELECT, which throws an exception +# 2. It catches the exception, checks ->{Active}/->ping, sees the disconnected state... +# 3. Reconnects, and retries the operation +# 4. Success! +my @art_two = $schema->resultset("Artist")->search({ }, { order_by => 'name DESC'}); +cmp_ok(@art_two, '==', 3, "Three artists returned"); diff --git a/t/34exception_action.t b/t/34exception_action.t new file mode 100644 index 0000000..7fef551 --- /dev/null +++ b/t/34exception_action.t @@ -0,0 +1,64 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; + +plan tests => 6; + +# Set up the "usual" sqlite for DBICTest +my $schema = DBICTest->init_schema; + +# This is how we're generating exceptions in the rest of these tests, +# which might need updating at some future time to be some other +# exception-generating statement: + +sub throwex { $schema->resultset("Artist")->search(1,1,1); } +my $ex_regex = qr/Odd number of arguments to search/; + +# Basic check, normal exception +eval { throwex }; +like($@, $ex_regex); + +# Now lets rethrow via exception_action +$schema->exception_action(sub { die @_ }); +eval { throwex }; +like($@, $ex_regex); + +# Now lets suppress the error +$schema->exception_action(sub { 1 }); +eval { throwex }; +ok(!$@, "Suppress exception"); + +# Now lets fall through and let croak take back over +$schema->exception_action(sub { return }); +eval { throwex }; +like($@, $ex_regex); + +# Whacky useless exception class +{ + package DBICTest::Exception; + use overload '""' => \&stringify, fallback => 1; + sub new { + my $class = shift; + bless { msg => shift }, $class; + } + sub throw { + my $self = shift; + die $self if ref $self eq __PACKAGE__; + die $self->new(shift); + } + sub stringify { + "DBICTest::Exception is handling this: " . shift->{msg}; + } +} + +# Try the exception class +$schema->exception_action(sub { DBICTest::Exception->throw(@_) }); +eval { throwex }; +like($@, qr/DBICTest::Exception is handling this: $ex_regex/); + +# While we're at it, lets throw a custom exception through Storage::DBI +eval { $schema->storage->throw_exception('floob') }; +like($@, qr/DBICTest::Exception is handling this: floob/); diff --git a/t/35disable_sth_caching.t b/t/35disable_sth_caching.t new file mode 100644 index 0000000..5ad4cca --- /dev/null +++ b/t/35disable_sth_caching.t @@ -0,0 +1,19 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; + +plan tests => 2; + +# Set up the "usual" sqlite for DBICTest +my $schema = DBICTest->init_schema; + +my $sth_one = $schema->storage->sth('SELECT 42'); +my $sth_two = $schema->storage->sth('SELECT 42'); +$schema->storage->disable_sth_caching(1); +my $sth_three = $schema->storage->sth('SELECT 42'); + +ok($sth_one == $sth_two, "statement caching works"); +ok($sth_two != $sth_three, "disabling statement caching works"); diff --git a/t/39load_namespaces_1.t b/t/39load_namespaces_1.t new file mode 100644 index 0000000..7911d8d --- /dev/null +++ b/t/39load_namespaces_1.t @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Test::More; + +unshift(@INC, './t/lib'); + +plan tests => 6; + +my $warnings; +eval { + local $SIG{__WARN__} = sub { $warnings .= shift }; + package DBICNSTest; + use base qw/DBIx::Class::Schema/; + __PACKAGE__->load_namespaces; +}; +ok(!$@) or diag $@; +like($warnings, qr/load_namespaces found ResultSet class C with no corresponding Result class/); + +my $source_a = DBICNSTest->source('A'); +isa_ok($source_a, 'DBIx::Class::ResultSource::Table'); +my $rset_a = DBICNSTest->resultset('A'); +isa_ok($rset_a, 'DBICNSTest::ResultSet::A'); + +my $source_b = DBICNSTest->source('B'); +isa_ok($source_b, 'DBIx::Class::ResultSource::Table'); +my $rset_b = DBICNSTest->resultset('B'); +isa_ok($rset_b, 'DBIx::Class::ResultSet'); diff --git a/t/39load_namespaces_2.t b/t/39load_namespaces_2.t new file mode 100644 index 0000000..6daf05f --- /dev/null +++ b/t/39load_namespaces_2.t @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Test::More; + +unshift(@INC, './t/lib'); + +plan tests => 6; + +my $warnings; +eval { + local $SIG{__WARN__} = sub { $warnings .= shift }; + package DBICNSTest; + use base qw/DBIx::Class::Schema/; + __PACKAGE__->load_namespaces( + result_namespace => 'Rslt', + resultset_namespace => 'RSet', + ); +}; +ok(!$@) or diag $@; +like($warnings, qr/load_namespaces found ResultSet class C with no corresponding Result class/); + +my $source_a = DBICNSTest->source('A'); +isa_ok($source_a, 'DBIx::Class::ResultSource::Table'); +my $rset_a = DBICNSTest->resultset('A'); +isa_ok($rset_a, 'DBICNSTest::RSet::A'); + +my $source_b = DBICNSTest->source('B'); +isa_ok($source_b, 'DBIx::Class::ResultSource::Table'); +my $rset_b = DBICNSTest->resultset('B'); +isa_ok($rset_b, 'DBIx::Class::ResultSet'); diff --git a/t/39load_namespaces_3.t b/t/39load_namespaces_3.t new file mode 100644 index 0000000..f48c838 --- /dev/null +++ b/t/39load_namespaces_3.t @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Test::More; + +unshift(@INC, './t/lib'); + +plan tests => 7; + +my $warnings; +eval { + local $SIG{__WARN__} = sub { $warnings .= shift }; + package DBICNSTestOther; + use base qw/DBIx::Class::Schema/; + __PACKAGE__->load_namespaces( + result_namespace => [ '+DBICNSTest::Rslt', '+DBICNSTest::OtherRslt' ], + resultset_namespace => '+DBICNSTest::RSet', + ); +}; +ok(!$@) or diag $@; +like($warnings, qr/load_namespaces found ResultSet class C with no corresponding Result class/); + +my $source_a = DBICNSTestOther->source('A'); +isa_ok($source_a, 'DBIx::Class::ResultSource::Table'); +my $rset_a = DBICNSTestOther->resultset('A'); +isa_ok($rset_a, 'DBICNSTest::RSet::A'); + +my $source_b = DBICNSTestOther->source('B'); +isa_ok($source_b, 'DBIx::Class::ResultSource::Table'); +my $rset_b = DBICNSTestOther->resultset('B'); +isa_ok($rset_b, 'DBIx::Class::ResultSet'); + +my $source_d = DBICNSTestOther->source('D'); +isa_ok($source_d, 'DBIx::Class::ResultSource::Table'); diff --git a/t/39load_namespaces_4.t b/t/39load_namespaces_4.t new file mode 100644 index 0000000..b674f30 --- /dev/null +++ b/t/39load_namespaces_4.t @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Test::More; + +unshift(@INC, './t/lib'); + +plan tests => 6; + +my $warnings; +eval { + local $SIG{__WARN__} = sub { $warnings .= shift }; + package DBICNSTest; + use base qw/DBIx::Class::Schema/; + __PACKAGE__->load_namespaces( default_resultset_class => 'RSBase' ); +}; +ok(!$@) or diag $@; +like($warnings, qr/load_namespaces found ResultSet class C with no corresponding Result class/); + +my $source_a = DBICNSTest->source('A'); +isa_ok($source_a, 'DBIx::Class::ResultSource::Table'); +my $rset_a = DBICNSTest->resultset('A'); +isa_ok($rset_a, 'DBICNSTest::ResultSet::A'); + +my $source_b = DBICNSTest->source('B'); +isa_ok($source_b, 'DBIx::Class::ResultSource::Table'); +my $rset_b = DBICNSTest->resultset('B'); +isa_ok($rset_b, 'DBICNSTest::RSBase'); diff --git a/t/40resultsetmanager.t b/t/40resultsetmanager.t index bdec159..8389291 100644 --- a/t/40resultsetmanager.t +++ b/t/40resultsetmanager.t @@ -17,7 +17,7 @@ BEGIN { use DBICTest::ResultSetManager; # uses Class::Inspector -my $schema = DBICTest::ResultSetManager->compose_connection('DB', 'foo'); +my $schema = DBICTest::ResultSetManager->compose_namespace('DB'); my $rs = $schema->resultset('Foo'); ok( !DB::Foo->can('bar'), 'Foo class does not have bar method' ); diff --git a/t/55storage_stress.t b/t/55storage_stress.t new file mode 100644 index 0000000..f338302 --- /dev/null +++ b/t/55storage_stress.t @@ -0,0 +1,55 @@ +use strict; +use warnings; +use Test::More; + +# XXX obviously, the guts of this test haven't been written yet --blblack + +use lib qw(t/lib); + +plan skip_all => 'Set $ENV{DBICTEST_STORAGE_STRESS} to run this test' + . ' (it is very resource intensive!)' + unless $ENV{DBICTEST_STORAGE_STRESS}; + +my $NKIDS = 20; +my $CYCLES = 5; +my @KILL_RATES = qw/0 0.001 0.01 0.1 0.2 0.5 0.75 1.0/; + +# Stress the storage with these parameters... +sub stress_storage { + my ($connect_info, $num_kids, $cycles, $kill_rate) = @_; + + foreach my $cycle (1..$cycles) { + my $schema = DBICTest::Schema->connection(@$connect_info, { AutoCommit => 1 }); + foreach my $kidno (1..$num_kids) { + ok(1); + } + } +} + +# Get a set of connection information - +# whatever the user has supplied for the vendor-specific tests +sub get_connect_infos { + my @connect_infos; + foreach my $db_prefix (qw/PG MYSQL DB2 MSSQL ORA/) { + my @conn_info = @ENV{ + map { "DBICTEST_${db_prefix}_${_}" } qw/DSN USER PASS/ + }; + push(@connect_infos, \@conn_info) if $conn_info[0]; + } + \@connect_infos; +} + +my $connect_infos = get_connect_infos(); + +plan skip_all => 'This test needs some non-sqlite connect info!' + unless @$connect_infos; + +plan tests => (1 * @$connect_infos * $NKIDS * $CYCLES * @KILL_RATES) + 1; + +use_ok('DBICTest::Schema'); + +foreach my $connect_info (@$connect_infos) { + foreach my $kill_rate (@KILL_RATES) { + stress_storage($connect_info, $NKIDS, $CYCLES, $kill_rate); + } +} diff --git a/t/60core.t b/t/60core.t index c5959d0..3eb80df 100644 --- a/t/60core.t +++ b/t/60core.t @@ -7,7 +7,7 @@ use DBICTest; my $schema = DBICTest->init_schema(); -plan tests => 63; +plan tests => 64; # figure out if we've got a version of sqlite that is older than 3.2.6, in # which case COUNT(DISTINCT()) doesn't work @@ -255,7 +255,7 @@ ok($schema->storage(), 'Storage available'); cmp_ok(@artsn, '==', 4, "Four artists returned"); # make sure subclasses that don't set source_name are ok - ok($schema->source('ArtistSubclass', 'ArtistSubclass exists')); + ok($schema->source('ArtistSubclass'), 'ArtistSubclass exists'); } my $newbook = $schema->resultset( 'Bookmark' )->find(1); @@ -277,6 +277,7 @@ ok(!$@, "stringify to false value doesn't cause error"); # test column_info { $schema->source("Artist")->{_columns}{'artistid'} = {}; + $schema->source("Artist")->column_info_from_storage(1); my $typeinfo = $schema->source("Artist")->column_info('artistid'); is($typeinfo->{data_type}, 'INTEGER', 'column_info ok'); @@ -284,6 +285,19 @@ ok(!$@, "stringify to false value doesn't cause error"); ok($schema->source("Artist")->{_columns_info_loaded} == 1, 'Columns info flag set'); } +# test source_info +{ + my $expected = { + "source_info_key_A" => "source_info_value_A", + "source_info_key_B" => "source_info_value_B", + "source_info_key_C" => "source_info_value_C", + }; + + my $sinfo = $schema->source("Artist")->source_info; + + is_deeply($sinfo, $expected, 'source_info data works'); +} + # test remove_columns { is_deeply([$schema->source('CD')->columns], [qw/cdid artist title year/]); diff --git a/t/63register_class.t b/t/63register_class.t new file mode 100644 index 0000000..2fdc07c --- /dev/null +++ b/t/63register_class.t @@ -0,0 +1,17 @@ +use strict; +use warnings; + +use Test::More tests => 2; +use lib qw(t/lib); +use DBICTest; +use DBICTest::Schema; +use DBICTest::Schema::Artist; + +DBICTest::Schema::Artist->source_name('MyArtist'); +DBICTest::Schema->register_class('FooA', 'DBICTest::Schema::Artist'); + +my $schema = DBICTest->init_schema(); + +my $a = $schema->resultset('FooA')->search; +is($a->count, 3, 'have 3 artists'); +is($schema->class('FooA'), 'DBICTest::FooA', 'Correct artist class'); diff --git a/t/65multipk.t b/t/65multipk.t index 084bb8e..45c9b2f 100644 --- a/t/65multipk.t +++ b/t/65multipk.t @@ -9,7 +9,7 @@ my $schema = DBICTest->init_schema(); plan tests => 5; -my $artist = DBICTest::Artist->find(1); +my $artist = $schema->resultset("Artist")->find(1); ok($artist->find_related('twokeys', {cd => 1}), "find multiple pks using relationships + args"); ok($schema->resultset("FourKeys")->search({ foo => 1, bar => 2 })->find({ hello => 3, goodbye => 4 }), "search on partial key followed by a find"); diff --git a/t/68inflate.t b/t/68inflate.t index 2e7c374..ea917f8 100644 --- a/t/68inflate.t +++ b/t/68inflate.t @@ -5,7 +5,6 @@ use Test::More; use lib qw(t/lib); use DBICTest; -DBICTest::Schema::CD->add_column('year'); my $schema = DBICTest->init_schema(); eval { require DateTime }; @@ -13,7 +12,9 @@ plan skip_all => "Need DateTime for inflation tests" if $@; plan tests => 20; -DBICTest::Schema::CD->inflate_column( 'year', +$schema->class('CD') +#DBICTest::Schema::CD +->inflate_column( 'year', { inflate => sub { DateTime->new( year => shift ) }, deflate => sub { shift->year } } ); diff --git a/t/68inflate_resultclass_hashrefinflator.t b/t/68inflate_resultclass_hashrefinflator.t new file mode 100644 index 0000000..221626a --- /dev/null +++ b/t/68inflate_resultclass_hashrefinflator.t @@ -0,0 +1,87 @@ +use strict; +use warnings; + +use Test::More qw(no_plan); +use lib qw(t/lib); +use DBICTest; +use DBIx::Class::ResultClass::HashRefInflator; +my $schema = DBICTest->init_schema(); + + +# Under some versions of SQLite if the $rs is left hanging around it will lock +# So we create a scope here cos I'm lazy +{ + my $rs = $schema->resultset('CD'); + + # get the defined columns + my @dbic_cols = sort $rs->result_source->columns; + + # use the hashref inflator class as result class + $rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); + + # fetch first record + my $datahashref1 = $rs->first; + + my @hashref_cols = sort keys %$datahashref1; + + is_deeply( \@dbic_cols, \@hashref_cols, 'returned columns' ); +} + + +sub check_cols_of { + my ($dbic_obj, $datahashref) = @_; + + foreach my $col (keys %$datahashref) { + # plain column + if (not ref ($datahashref->{$col}) ) { + is ($datahashref->{$col}, $dbic_obj->get_column($col), 'same value'); + } + # related table entry (belongs_to) + elsif (ref ($datahashref->{$col}) eq 'HASH') { + check_cols_of($dbic_obj->$col, $datahashref->{$col}); + } + # multiple related entries (has_many) + elsif (ref ($datahashref->{$col}) eq 'ARRAY') { + my @dbic_reltable = $dbic_obj->$col; + my @hashref_reltable = @{$datahashref->{$col}}; + + is (scalar @hashref_reltable, scalar @dbic_reltable, 'number of related entries'); + + # for my $index (0..scalar @hashref_reltable) { + for my $index (0..scalar @dbic_reltable) { + my $dbic_reltable_obj = $dbic_reltable[$index]; + my $hashref_reltable_entry = $hashref_reltable[$index]; + + check_cols_of($dbic_reltable_obj, $hashref_reltable_entry); + } + } + } +} + +# create a cd without tracks for testing empty has_many relationship +$schema->resultset('CD')->create({ title => 'Silence is golden', artist => 3, year => 2006 }); + +# order_by to ensure both resultsets have the rows in the same order +my $rs_dbic = $schema->resultset('CD')->search(undef, + { + prefetch => [ qw/ artist tracks / ], + order_by => [ 'me.cdid', 'tracks.position' ], + } +); +my $rs_hashrefinf = $schema->resultset('CD')->search(undef, + { + prefetch => [ qw/ artist tracks / ], + order_by => [ 'me.cdid', 'tracks.position' ], + } +); +$rs_hashrefinf->result_class('DBIx::Class::ResultClass::HashRefInflator'); + +my @dbic = $rs_dbic->all; +my @hashrefinf = $rs_hashrefinf->all; + +for my $index (0..scalar @hashrefinf) { + my $dbic_obj = $dbic[$index]; + my $datahashref = $hashrefinf[$index]; + + check_cols_of($dbic_obj, $datahashref); +} diff --git a/t/71mysql.t b/t/71mysql.t index aeb73ea..3bbdaa1 100644 --- a/t/71mysql.t +++ b/t/71mysql.t @@ -15,7 +15,7 @@ plan skip_all => 'Set $ENV{DBICTEST_MYSQL_DSN}, _USER and _PASS to run this test plan tests => 5; -DBICTest::Schema->compose_connection('MySQLTest' => $dsn, $user, $pass); +DBICTest::Schema->compose_namespace('MySQLTest' => $dsn, $user, $pass); my $dbh = MySQLTest->schema->storage->dbh; diff --git a/t/72pg.t b/t/72pg.t index f0bb3f8..a3239ca 100644 --- a/t/72pg.t +++ b/t/72pg.t @@ -15,6 +15,7 @@ use DBICTest; __PACKAGE__->load_components(qw/PK::Auto Core/); __PACKAGE__->table('casecheck'); __PACKAGE__->add_columns(qw/id name NAME uc_name/); + __PACKAGE__->column_info_from_storage(1); __PACKAGE__->set_primary_key('id'); } @@ -29,7 +30,7 @@ plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test' plan tests => 8; DBICTest::Schema->load_classes( 'Casecheck' ); -DBICTest::Schema->compose_connection('PgTest' => $dsn, $user, $pass); +DBICTest::Schema->compose_namespace('PgTest' => $dsn, $user, $pass); my $dbh = PgTest->schema->storage->dbh; PgTest->schema->source("Artist")->name("testschema.artist"); diff --git a/t/73oracle.t b/t/73oracle.t index c0489ff..7ca5c41 100644 --- a/t/73oracle.t +++ b/t/73oracle.t @@ -13,7 +13,7 @@ plan skip_all => 'Set $ENV{DBICTEST_ORA_DSN}, _USER and _PASS to run this test. plan tests => 6; -DBICTest::Schema->compose_connection('OraTest' => $dsn, $user, $pass); +DBICTest::Schema->compose_namespace('OraTest' => $dsn, $user, $pass); my $dbh = OraTest->schema->storage->dbh; diff --git a/t/745db2.t b/t/745db2.t index ffb7a0b..82d3c2c 100644 --- a/t/745db2.t +++ b/t/745db2.t @@ -14,7 +14,7 @@ plan skip_all => 'Set $ENV{DBICTEST_DB2_DSN}, _USER and _PASS to run this test' plan tests => 6; -DBICTest::Schema->compose_connection('DB2Test' => $dsn, $user, $pass); +DBICTest::Schema->compose_namespace('DB2Test' => $dsn, $user, $pass); my $dbh = DB2Test->schema->storage->dbh; diff --git a/t/746db2_400.t b/t/746db2_400.t index 558ca62..745673b 100644 --- a/t/746db2_400.t +++ b/t/746db2_400.t @@ -17,7 +17,7 @@ plan skip_all => 'Set $ENV{DBICTEST_DB2_400_DSN}, _USER and _PASS to run this te plan tests => 6; -DBICTest::Schema->compose_connection('DB2Test' => $dsn, $user, $pass); +DBICTest::Schema->compose_namespace('DB2Test' => $dsn, $user, $pass); my $dbh = DB2Test->schema->storage->dbh; diff --git a/t/74mssql.t b/t/74mssql.t index 204a640..0bb43b6 100644 --- a/t/74mssql.t +++ b/t/74mssql.t @@ -19,7 +19,7 @@ $storage_type = '::DBI::Sybase::MSSQL' if $dsn =~ /^dbi:Sybase:/; # Add more for others in the future when they exist (ODBC? ADO? JDBC?) DBICTest::Schema->storage_type($storage_type); -DBICTest::Schema->compose_connection( 'MSSQLTest' => $dsn, $user, $pass ); +DBICTest::Schema->compose_namespace( 'MSSQLTest' => $dsn, $user, $pass ); my $dbh = MSSQLTest->schema->storage->dbh; diff --git a/t/81transactions.t b/t/81transactions.t index 5434387..4a7830f 100644 --- a/t/81transactions.t +++ b/t/81transactions.t @@ -118,7 +118,7 @@ my $fail_code = sub { # Force txn_rollback() to throw an exception no warnings 'redefine'; no strict 'refs'; - local *{"DBIx::Class::Schema::txn_rollback"} = sub{die 'FAILED'}; + local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{die 'FAILED'}; eval { $schema->txn_do($fail_code, $artist); diff --git a/t/86sqlt.t b/t/86sqlt.t index 92d90f2..095a878 100644 --- a/t/86sqlt.t +++ b/t/86sqlt.t @@ -10,7 +10,7 @@ plan skip_all => 'SQL::Translator required' if $@; my $schema = DBICTest->init_schema; -plan tests => 53; +plan tests => 54; my $translator = SQL::Translator->new( parser_args => { @@ -24,6 +24,10 @@ $translator->producer('SQLite'); my $output = $translator->translate(); + +ok($output, "SQLT produced someoutput") + or diag($translator->error); + # Note that the constraints listed here are the only ones that are tested -- if # more exist in the Schema than are listed here and all listed constraints are # correct, the test will still pass. If you add a class with UNIQUE or FOREIGN diff --git a/t/87ordered.t b/t/87ordered.t index b1d484c..7bc1bed 100644 --- a/t/87ordered.t +++ b/t/87ordered.t @@ -6,9 +6,11 @@ use Test::More; use lib qw(t/lib); use DBICTest; +use POSIX qw(ceil); + my $schema = DBICTest->init_schema(); -plan tests => 321; +plan tests => 879; my $employees = $schema->resultset('Employee'); $employees->delete(); @@ -23,20 +25,168 @@ hammer_rs( $employees ); DBICTest::Employee->grouping_column('group_id'); $employees->delete(); -foreach my $group_id (1..3) { +foreach my $group_id (1..4) { foreach (1..6) { $employees->create({ name=>'temp', group_id=>$group_id }); } } $employees = $employees->search(undef,{order_by=>'group_id,position'}); -foreach my $group_id (1..3) { +foreach my $group_id (1..4) { my $group_employees = $employees->search({group_id=>$group_id}); $group_employees->all(); ok( check_rs($group_employees), "group intial positions" ); hammer_rs( $group_employees ); } +my $group_3 = $employees->search({group_id=>3}); +my $to_group = 1; +my $to_pos = undef; +while (my $employee = $group_3->next) { + $employee->move_to_group($to_group, $to_pos); + $to_pos++; + $to_group = $to_group==1 ? 2 : 1; +} +foreach my $group_id (1..4) { + my $group_employees = $employees->search({group_id=>$group_id}); + $group_employees->all(); + ok( check_rs($group_employees), "group positions after move_to_group" ); +} + +my $employee = $employees->search({group_id=>4})->first; +$employee->position(2); +$employee->update; +ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 1" ); +$employee = $employees->search({group_id=>4})->first; +$employee->update({position=>3}); +ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 2" ); +$employee = $employees->search({group_id=>4})->first; +$employee->group_id(1); +$employee->update; +ok( + check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})), + "overloaded update 3" +); +$employee = $employees->search({group_id=>4})->first; +$employee->update({group_id=>2}); +ok( + check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})), + "overloaded update 4" +); +$employee = $employees->search({group_id=>4})->first; +$employee->group_id(1); +$employee->position(3); +$employee->update; +ok( + check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})), + "overloaded update 5" +); +$employee = $employees->search({group_id=>4})->first; +$employee->group_id(2); +$employee->position(undef); +$employee->update; +ok( + check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})), + "overloaded update 6" +); +$employee = $employees->search({group_id=>4})->first; +$employee->update({group_id=>1,position=>undef}); +ok( + check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})), + "overloaded update 7" +); + +# multicol tests begin here +DBICTest::Employee->grouping_column(['group_id', 'group_id_2']); +$employees->delete(); +foreach my $group_id (1..4) { + foreach my $group_id_2 (1..4) { + foreach (1..4) { + $employees->create({ name=>'temp', group_id=>$group_id, group_id_2=>$group_id_2 }); + } + } +} +$employees = $employees->search(undef,{order_by=>'group_id,group_id_2,position'}); + +foreach my $group_id (1..3) { + foreach my $group_id_2 (1..3) { + my $group_employees = $employees->search({group_id=>$group_id, group_id_2=>$group_id_2}); + $group_employees->all(); + ok( check_rs($group_employees), "group intial positions" ); + hammer_rs( $group_employees ); + } +} + +# move_to_group, specifying group by hash +my $group_4 = $employees->search({group_id=>4}); +$to_group = 1; +my $to_group_2_base = 7; +my $to_group_2 = 1; +$to_pos = undef; +while (my $employee = $group_4->next) { + $employee->move_to_group({group_id=>$to_group, group_id_2=>$to_group_2}, $to_pos); + $to_pos++; + $to_group = ($to_group % 3) + 1; + $to_group_2_base++; + $to_group_2 = (ceil($to_group_2_base/3.0) %3) +1 +} +foreach my $group_id (1..4) { + foreach my $group_id_2 (1..4) { + my $group_employees = $employees->search({group_id=>$group_id,group_id_2=>$group_id_2}); + $group_employees->all(); + ok( check_rs($group_employees), "group positions after move_to_group" ); + } +} + +$employees->delete(); +foreach my $group_id (1..4) { + foreach my $group_id_2 (1..4) { + foreach (1..4) { + $employees->create({ name=>'temp', group_id=>$group_id, group_id_2=>$group_id_2 }); + } + } +} +$employees = $employees->search(undef,{order_by=>'group_id,group_id_2,position'}); + +$employee = $employees->search({group_id=>4, group_id_2=>1})->first; +$employee->group_id(1); +$employee->update; +ok( + check_rs($employees->search_rs({group_id=>4, group_id_2=>1})) + && check_rs($employees->search_rs({group_id=>1, group_id_2=>1})), + "overloaded multicol update 1" +); + +$employee = $employees->search({group_id=>4, group_id_2=>1})->first; +$employee->update({group_id=>2}); +ok( check_rs($employees->search_rs({group_id=>4, group_id_2=>1})) + && check_rs($employees->search_rs({group_id=>2, group_id_2=>1})), + "overloaded multicol update 2" +); + +$employee = $employees->search({group_id=>3, group_id_2=>1})->first; +$employee->group_id(1); +$employee->group_id_2(3); +$employee->update(); +ok( check_rs($employees->search_rs({group_id=>3, group_id_2=>1})) + && check_rs($employees->search_rs({group_id=>1, group_id_2=>3})), + "overloaded multicol update 3" +); + +$employee = $employees->search({group_id=>3, group_id_2=>1})->first; +$employee->update({group_id=>2, group_id_2=>3}); +ok( check_rs($employees->search_rs({group_id=>3, group_id_2=>1})) + && check_rs($employees->search_rs({group_id=>2, group_id_2=>3})), + "overloaded multicol update 4" +); + +$employee = $employees->search({group_id=>3, group_id_2=>2})->first; +$employee->update({group_id=>2, group_id_2=>4, position=>2}); +ok( check_rs($employees->search_rs({group_id=>3, group_id_2=>2})) + && check_rs($employees->search_rs({group_id=>2, group_id_2=>4})), + "overloaded multicol update 5" +); + sub hammer_rs { my $rs = shift; my $employee; diff --git a/t/93nobindvars.t b/t/93nobindvars.t new file mode 100644 index 0000000..b4e1adc --- /dev/null +++ b/t/93nobindvars.t @@ -0,0 +1,65 @@ +use strict; +use warnings; + +# Copied from 71mysql.t, manually using NoBindVars. This is to give that code +# wider testing, since virtually nobody who regularly runs the test suite +# is using DBD::Sybase+FreeTDS+MSSQL -- blblack + +use Test::More; +use lib qw(t/lib); +use DBICTest; +use DBI::Const::GetInfoType; + +my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MYSQL_${_}" } qw/DSN USER PASS/}; + +#warn "$dsn $user $pass"; + +plan skip_all => 'Set $ENV{DBICTEST_MYSQL_DSN}, _USER and _PASS to run this test' + unless ($dsn && $user); + +plan tests => 4; + +{ # Fake storage driver for mysql + no bind variables + package DBIx::Class::Storage::DBI::MySQLNoBindVars; + use base qw/ + DBIx::Class::Storage::DBI::mysql + DBIx::Class::Storage::DBI::NoBindVars + /; + $INC{'DBIx/Class/Storage/DBI/MySQLNoBindVars.pm'} = 1; +} + +DBICTest::Schema->storage(undef); # just in case? +DBICTest::Schema->storage_type('::DBI::MySQLNoBindVars'); +DBICTest::Schema->compose_namespace('MySQLTest' => $dsn, $user, $pass); + +my $dbh = MySQLTest->schema->storage->dbh; + +$dbh->do("DROP TABLE IF EXISTS artist;"); + +$dbh->do("CREATE TABLE artist (artistid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), charfield CHAR(10));"); + +#'dbi:mysql:host=localhost;database=dbic_test', 'dbic_test', ''); + +MySQLTest::Artist->load_components('PK::Auto'); + +# test primary key handling +my $new = MySQLTest::Artist->create({ name => 'foo' }); +ok($new->artistid, "Auto-PK worked"); + +# test LIMIT support +for (1..6) { + MySQLTest::Artist->create({ name => 'Artist ' . $_ }); +} +my $it = MySQLTest::Artist->search( {}, + { rows => 3, + offset => 2, + order_by => 'artistid' } +); +is( $it->count, 3, "LIMIT count ok" ); +is( $it->next->name, "Artist 2", "iterator->next ok" ); +$it->next; +$it->next; +is( $it->next, undef, "next past end of resultset ok" ); + +# clean up our mess +$dbh->do("DROP TABLE artist"); diff --git a/t/94versioning.t b/t/94versioning.t new file mode 100644 index 0000000..66ea346 --- /dev/null +++ b/t/94versioning.t @@ -0,0 +1,58 @@ +#!/usr/bin/perl +use strict; +use warnings; +use Test::More; +use File::Spec; + +BEGIN { + eval "use DBD::SQLite; use SQL::Translator;"; + plan $@ + ? ( skip_all => 'needs DBD::SQLite and SQL::Translator for testing' ) + : ( tests => 6 ); +} + +use lib qw(t/lib); + +use_ok('DBICVersionOrig'); + +my $db_file = "t/var/versioning.db"; +unlink($db_file) if -e $db_file; +unlink($db_file . "-journal") if -e $db_file . "-journal"; +mkdir("t/var") unless -d "t/var"; +unlink('t/var/DBICVersion-Schema-1.0-SQLite.sql'); + +my $schema_orig = DBICVersion::Schema->connect("dbi:SQLite:$db_file"); +# $schema->storage->ensure_connected(); + +is($schema_orig->ddl_filename('SQLite', 't/var', '1.0'), File::Spec->catfile('t', 'var', 'DBICVersion-Schema-1.0-SQLite.sql'), 'Filename creation working'); +$schema_orig->create_ddl_dir('SQLite', undef, 't/var'); + +ok(-f 't/var/DBICVersion-Schema-1.0-SQLite.sql', 'Created DDL file'); +## do this here or let Versioned.pm do it? +# $schema->deploy(); + +my $tvrs = $schema_orig->resultset('Table'); +is($schema_orig->exists($tvrs), 1, 'Created schema from DDL file'); + +eval "use DBICVersionNew"; +my $schema_new = DBICVersion::Schema->connect("dbi:SQLite:$db_file"); + +unlink('t/var/DBICVersion-Schema-2.0-SQLite.sql'); +unlink('t/var/DBICVersion-Schema-1.0-2.0-SQLite.sql'); +$schema_new->create_ddl_dir('SQLite', undef, 't/var', '1.0'); +ok(-f 't/var/DBICVersion-Schema-1.0-2.0-SQLite.sql', 'Created DDL upgrade file'); + +## create new to pick up filedata for upgrade files we just made (on_connect) +my $schema_upgrade = DBICVersion::Schema->connect("dbi:SQLite:$db_file"); + +## do this here or let Versioned.pm do it? +$schema_upgrade->upgrade(); +$tvrs = $schema_upgrade->resultset('Table'); +is($schema_upgrade->exists($tvrs), 1, 'Upgraded schema from DDL file'); + +unlink($db_file) if -e $db_file; +unlink($db_file . "-journal") if -e $db_file . "-journal"; +unlink('t/var/DBICVersion-Schema-1.0-SQLite.sql'); +unlink('t/var/DBICVersion-Schema-2.0-SQLite.sql'); +unlink('t/var/DBICVersion-Schema-1.0-2.0-SQLite.sql'); +unlink(); diff --git a/t/95sql_maker_quote.t b/t/95sql_maker_quote.t index dc33199..1f4cd90 100644 --- a/t/95sql_maker_quote.t +++ b/t/95sql_maker_quote.t @@ -15,9 +15,9 @@ use lib qw(t/lib); use_ok('DBICTest'); -DBICTest->init_schema(); +my $schema = DBICTest->init_schema(); -my $sql_maker = DBICTest->schema->storage->sql_maker; +my $sql_maker = $schema->storage->sql_maker; $sql_maker->quote_char('`'); $sql_maker->name_sep('.'); diff --git a/t/96file_column.t b/t/96file_column.t new file mode 100644 index 0000000..4773861 --- /dev/null +++ b/t/96file_column.t @@ -0,0 +1,15 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; +use IO::File; + +my $schema = DBICTest->init_schema(); + +plan tests => 1; + +my $fh = new IO::File('t/96file_column.t','r'); +eval { $schema->resultset('FileColumn')->create({file => {handle => $fh, filename =>'96file_column.t'}})}; +ok(!$@,'FileColumn checking if file handled properly.'); diff --git a/t/bindtype_columns.t b/t/bindtype_columns.t new file mode 100644 index 0000000..9d0ad92 --- /dev/null +++ b/t/bindtype_columns.t @@ -0,0 +1,60 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; + +my ($dsn, $dbuser, $dbpass) = @ENV{map { "DBICTEST_PG_${_}" } qw/DSN USER PASS/}; + +plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test' + unless ($dsn && $dbuser); + +plan tests => 3; + +my $schema = DBICTest::Schema->connection($dsn, $dbuser, $dbpass); + +my $dbh = $schema->storage->dbh; + +$dbh->do(qq[ + + CREATE TABLE artist + ( + artistid serial NOT NULL PRIMARY KEY, + media bytea NOT NULL, + name varchar NULL + ); +],{ RaiseError => 1, PrintError => 1 }); + + +$schema->class('Artist')->load_components(qw/ + + PK::Auto + Core +/); + +$schema->class('Artist')->add_columns( + + "media", { + + data_type => "bytea", + is_nullable => 0, + }, +); + +# test primary key handling +my $big_long_string = 'abcd' x 250000; + +my $new = $schema->resultset('Artist')->create({ media => $big_long_string }); + +ok($new->artistid, "Created a blob row"); +is($new->media, $big_long_string, "Set the blob correctly."); + +my $rs = $schema->resultset('Artist')->find({artistid=>$new->artistid}); + +is($rs->get_column('media'), $big_long_string, "Created the blob correctly."); + +$dbh->do("DROP TABLE artist"); + + + diff --git a/t/cdbi-sweet-t/08pager.t b/t/cdbi-sweet-t/08pager.t index 71ccaed..07166e6 100644 --- a/t/cdbi-sweet-t/08pager.t +++ b/t/cdbi-sweet-t/08pager.t @@ -16,9 +16,12 @@ BEGIN { use lib 't/lib'; use_ok('DBICTest'); -DBICTest->init_schema(); -DBICTest::CD->load_components(qw/CDBICompat::Pager/); +DBICTest::Schema::CD->load_components(qw/CDBICompat CDBICompat::Pager/); + +my $schema = DBICTest->init_schema(compose_connection => 1); + +DBICTest::CD->result_source_instance->schema->storage($schema->storage); my ( $pager, $it ) = DBICTest::CD->page( {}, diff --git a/t/lib/DBICNSTest/OtherRslt/D.pm b/t/lib/DBICNSTest/OtherRslt/D.pm new file mode 100644 index 0000000..9a9aaf5 --- /dev/null +++ b/t/lib/DBICNSTest/OtherRslt/D.pm @@ -0,0 +1,6 @@ +package DBICNSTest::OtherRslt::D; +use base qw/DBIx::Class/; +__PACKAGE__->load_components(qw/PK::Auto Core/); +__PACKAGE__->table('d'); +__PACKAGE__->add_columns('d'); +1; diff --git a/t/lib/DBICNSTest/RSBase.pm b/t/lib/DBICNSTest/RSBase.pm new file mode 100644 index 0000000..9786d5f --- /dev/null +++ b/t/lib/DBICNSTest/RSBase.pm @@ -0,0 +1,3 @@ +package DBICNSTest::RSBase; +use base qw/DBIx::Class::ResultSet/; +1; diff --git a/t/lib/DBICNSTest/RSet/A.pm b/t/lib/DBICNSTest/RSet/A.pm new file mode 100644 index 0000000..4cb415f --- /dev/null +++ b/t/lib/DBICNSTest/RSet/A.pm @@ -0,0 +1,3 @@ +package DBICNSTest::RSet::A; +use base qw/DBIx::Class::ResultSet/; +1; diff --git a/t/lib/DBICNSTest/RSet/C.pm b/t/lib/DBICNSTest/RSet/C.pm new file mode 100644 index 0000000..c43a3fe --- /dev/null +++ b/t/lib/DBICNSTest/RSet/C.pm @@ -0,0 +1,3 @@ +package DBICNSTest::RSet::C; +use base qw/DBIx::Class::ResultSet/; +1; diff --git a/t/lib/DBICNSTest/Result/A.pm b/t/lib/DBICNSTest/Result/A.pm new file mode 100644 index 0000000..d2faecb --- /dev/null +++ b/t/lib/DBICNSTest/Result/A.pm @@ -0,0 +1,6 @@ +package DBICNSTest::Result::A; +use base qw/DBIx::Class/; +__PACKAGE__->load_components(qw/PK::Auto Core/); +__PACKAGE__->table('a'); +__PACKAGE__->add_columns('a'); +1; diff --git a/t/lib/DBICNSTest/Result/B.pm b/t/lib/DBICNSTest/Result/B.pm new file mode 100644 index 0000000..e9cdc37 --- /dev/null +++ b/t/lib/DBICNSTest/Result/B.pm @@ -0,0 +1,6 @@ +package DBICNSTest::Result::B; +use base qw/DBIx::Class/; +__PACKAGE__->load_components(qw/PK::Auto Core/); +__PACKAGE__->table('b'); +__PACKAGE__->add_columns('b'); +1; diff --git a/t/lib/DBICNSTest/ResultSet/A.pm b/t/lib/DBICNSTest/ResultSet/A.pm new file mode 100644 index 0000000..c7a86aa --- /dev/null +++ b/t/lib/DBICNSTest/ResultSet/A.pm @@ -0,0 +1,3 @@ +package DBICNSTest::ResultSet::A; +use base qw/DBIx::Class::ResultSet/; +1; diff --git a/t/lib/DBICNSTest/ResultSet/C.pm b/t/lib/DBICNSTest/ResultSet/C.pm new file mode 100644 index 0000000..55ecf1d --- /dev/null +++ b/t/lib/DBICNSTest/ResultSet/C.pm @@ -0,0 +1,3 @@ +package DBICNSTest::ResultSet::C; +use base qw/DBIx::Class::ResultSet/; +1; diff --git a/t/lib/DBICNSTest/Rslt/A.pm b/t/lib/DBICNSTest/Rslt/A.pm new file mode 100644 index 0000000..686e329 --- /dev/null +++ b/t/lib/DBICNSTest/Rslt/A.pm @@ -0,0 +1,6 @@ +package DBICNSTest::Rslt::A; +use base qw/DBIx::Class/; +__PACKAGE__->load_components(qw/PK::Auto Core/); +__PACKAGE__->table('a'); +__PACKAGE__->add_columns('a'); +1; diff --git a/t/lib/DBICNSTest/Rslt/B.pm b/t/lib/DBICNSTest/Rslt/B.pm new file mode 100644 index 0000000..fb02f3f --- /dev/null +++ b/t/lib/DBICNSTest/Rslt/B.pm @@ -0,0 +1,6 @@ +package DBICNSTest::Rslt::B; +use base qw/DBIx::Class/; +__PACKAGE__->load_components(qw/PK::Auto Core/); +__PACKAGE__->table('b'); +__PACKAGE__->add_columns('b'); +1; diff --git a/t/lib/DBICTest.pm b/t/lib/DBICTest.pm index 97855cb..27c8549 100755 --- a/t/lib/DBICTest.pm +++ b/t/lib/DBICTest.pm @@ -55,7 +55,12 @@ sub init_schema { my $dbuser = $ENV{"DBICTEST_DBUSER"} || ''; my $dbpass = $ENV{"DBICTEST_DBPASS"} || ''; - my $schema = DBICTest::Schema->compose_connection('DBICTest' => $dsn, $dbuser, $dbpass); + my $compose_method = ($args{compose_connection} + ? 'compose_connection' + : 'compose_namespace'); + + my $schema = DBICTest::Schema->$compose_method('DBICTest') + ->connect($dsn, $dbuser, $dbpass); $schema->storage->on_connect_do(['PRAGMA synchronous = OFF']); if ( !$args{no_deploy} ) { __PACKAGE__->deploy_schema( $schema ); @@ -225,8 +230,8 @@ sub populate_schema { ]); $schema->populate('Link', [ - [ qw/id title/ ], - [ 1, 'aaa' ] + [ qw/id url title/ ], + [ 1, '', 'aaa' ] ]); $schema->populate('Bookmark', [ diff --git a/t/lib/DBICTest/Schema.pm b/t/lib/DBICTest/Schema.pm index f8b2cd9..7ebd040 100644 --- a/t/lib/DBICTest/Schema.pm +++ b/t/lib/DBICTest/Schema.pm @@ -9,6 +9,7 @@ __PACKAGE__->load_classes(qw/ Artist Employee CD + FileColumn Link Bookmark #dummy diff --git a/t/lib/DBICTest/Schema/Artist.pm b/t/lib/DBICTest/Schema/Artist.pm index 0bb49c4..90eb7bf 100644 --- a/t/lib/DBICTest/Schema/Artist.pm +++ b/t/lib/DBICTest/Schema/Artist.pm @@ -4,10 +4,15 @@ package # hide from PAUSE use base 'DBIx::Class::Core'; __PACKAGE__->table('artist'); +__PACKAGE__->source_info({ + "source_info_key_A" => "source_info_value_A", + "source_info_key_B" => "source_info_value_B", + "source_info_key_C" => "source_info_value_C", +}); __PACKAGE__->add_columns( 'artistid' => { data_type => 'integer', - is_auto_increment => 1 + is_auto_increment => 1, }, 'name' => { data_type => 'varchar', diff --git a/t/lib/DBICTest/Schema/ArtistSourceName.pm b/t/lib/DBICTest/Schema/ArtistSourceName.pm index c4c8a8b..c59bbe5 100644 --- a/t/lib/DBICTest/Schema/ArtistSourceName.pm +++ b/t/lib/DBICTest/Schema/ArtistSourceName.pm @@ -2,7 +2,7 @@ package # hide from PAUSE DBICTest::Schema::ArtistSourceName; use base 'DBICTest::Schema::Artist'; - +__PACKAGE__->table(__PACKAGE__->table); __PACKAGE__->source_name('SourceNameArtists'); 1; diff --git a/t/lib/DBICTest/Schema/Employee.pm b/t/lib/DBICTest/Schema/Employee.pm index 78b3d16..7beb833 100644 --- a/t/lib/DBICTest/Schema/Employee.pm +++ b/t/lib/DBICTest/Schema/Employee.pm @@ -19,6 +19,10 @@ __PACKAGE__->add_columns( data_type => 'integer', is_nullable => 1, }, + group_id_2 => { + data_type => 'integer', + is_nullable => 1, + }, name => { data_type => 'varchar', size => 100, diff --git a/t/lib/DBICTest/Schema/FileColumn.pm b/t/lib/DBICTest/Schema/FileColumn.pm new file mode 100644 index 0000000..a6d768f --- /dev/null +++ b/t/lib/DBICTest/Schema/FileColumn.pm @@ -0,0 +1,19 @@ +package +DBICTest::Schema::FileColumn; + +use strict; +use warnings; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->load_components(qw/InflateColumn::File/); + +__PACKAGE__->table('file_columns'); + +__PACKAGE__->add_columns( + id => { data_type => 'integer', is_auto_increment => 1 }, + file => { data_type => 'varchar', is_file_column => 1, file_column_path => '/tmp', size=>255 } +); + +__PACKAGE__->set_primary_key('id'); + +1; diff --git a/t/lib/DBICTest/Schema/FourKeys.pm b/t/lib/DBICTest/Schema/FourKeys.pm index cdffa2f..a1e23db 100644 --- a/t/lib/DBICTest/Schema/FourKeys.pm +++ b/t/lib/DBICTest/Schema/FourKeys.pm @@ -3,17 +3,17 @@ package # hide from PAUSE use base 'DBIx::Class::Core'; -DBICTest::Schema::FourKeys->table('fourkeys'); -DBICTest::Schema::FourKeys->add_columns( +__PACKAGE__->table('fourkeys'); +__PACKAGE__->add_columns( 'foo' => { data_type => 'integer' }, 'bar' => { data_type => 'integer' }, 'hello' => { data_type => 'integer' }, 'goodbye' => { data_type => 'integer' }, 'sensors' => { data_type => 'character' }, ); -DBICTest::Schema::FourKeys->set_primary_key(qw/foo bar hello goodbye/); +__PACKAGE__->set_primary_key(qw/foo bar hello goodbye/); -DBICTest::Schema::FourKeys->has_many( +__PACKAGE__->has_many( 'fourkeys_to_twokeys', 'DBICTest::Schema::FourKeys_to_TwoKeys', { 'foreign.f_foo' => 'self.foo', 'foreign.f_bar' => 'self.bar', @@ -21,7 +21,7 @@ DBICTest::Schema::FourKeys->has_many( 'foreign.f_goodbye' => 'self.goodbye', }); -DBICTest::Schema::FourKeys->many_to_many( +__PACKAGE__->many_to_many( 'twokeys', 'fourkeys_to_twokeys', 'twokeys', ); diff --git a/t/lib/DBICTest/Schema/LinerNotes.pm b/t/lib/DBICTest/Schema/LinerNotes.pm index 168f311..0c82588 100644 --- a/t/lib/DBICTest/Schema/LinerNotes.pm +++ b/t/lib/DBICTest/Schema/LinerNotes.pm @@ -3,8 +3,8 @@ package # hide from PAUSE use base qw/DBIx::Class::Core/; -DBICTest::Schema::LinerNotes->table('liner_notes'); -DBICTest::Schema::LinerNotes->add_columns( +__PACKAGE__->table('liner_notes'); +__PACKAGE__->add_columns( 'liner_id' => { data_type => 'integer', }, @@ -13,8 +13,8 @@ DBICTest::Schema::LinerNotes->add_columns( size => 100, }, ); -DBICTest::Schema::LinerNotes->set_primary_key('liner_id'); -DBICTest::Schema::LinerNotes->belongs_to( +__PACKAGE__->set_primary_key('liner_id'); +__PACKAGE__->belongs_to( 'cd', 'DBICTest::Schema::CD', 'liner_id' ); diff --git a/t/lib/DBICTest/Schema/NoPrimaryKey.pm b/t/lib/DBICTest/Schema/NoPrimaryKey.pm index 1723390..1edda61 100644 --- a/t/lib/DBICTest/Schema/NoPrimaryKey.pm +++ b/t/lib/DBICTest/Schema/NoPrimaryKey.pm @@ -3,13 +3,13 @@ package # hide from PAUSE use base 'DBIx::Class::Core'; -DBICTest::Schema::NoPrimaryKey->table('noprimarykey'); -DBICTest::Schema::NoPrimaryKey->add_columns( +__PACKAGE__->table('noprimarykey'); +__PACKAGE__->add_columns( 'foo' => { data_type => 'integer' }, 'bar' => { data_type => 'integer' }, 'baz' => { data_type => 'integer' }, ); -DBICTest::Schema::NoPrimaryKey->add_unique_constraint(foo_bar => [ qw/foo bar/ ]); +__PACKAGE__->add_unique_constraint(foo_bar => [ qw/foo bar/ ]); 1; diff --git a/t/lib/DBICTest/Schema/OneKey.pm b/t/lib/DBICTest/Schema/OneKey.pm index 4cc2918..63356ac 100644 --- a/t/lib/DBICTest/Schema/OneKey.pm +++ b/t/lib/DBICTest/Schema/OneKey.pm @@ -3,8 +3,8 @@ package # hide from PAUSE use base 'DBIx::Class::Core'; -DBICTest::Schema::OneKey->table('onekey'); -DBICTest::Schema::OneKey->add_columns( +__PACKAGE__->table('onekey'); +__PACKAGE__->add_columns( 'id' => { data_type => 'integer', is_auto_increment => 1, @@ -16,7 +16,7 @@ DBICTest::Schema::OneKey->add_columns( data_type => 'integer', }, ); -DBICTest::Schema::OneKey->set_primary_key('id'); +__PACKAGE__->set_primary_key('id'); 1; diff --git a/t/lib/DBICTest/Schema/Serialized.pm b/t/lib/DBICTest/Schema/Serialized.pm index 41610da..687dcd1 100644 --- a/t/lib/DBICTest/Schema/Serialized.pm +++ b/t/lib/DBICTest/Schema/Serialized.pm @@ -3,11 +3,11 @@ package # hide from PAUSE use base 'DBIx::Class::Core'; -DBICTest::Schema::Serialized->table('serialized'); -DBICTest::Schema::Serialized->add_columns( +__PACKAGE__->table('serialized'); +__PACKAGE__->add_columns( 'id' => { data_type => 'integer' }, 'serialized' => { data_type => 'text' }, ); -DBICTest::Schema::Serialized->set_primary_key('id'); +__PACKAGE__->set_primary_key('id'); 1; diff --git a/t/lib/DBICVersionNew.pm b/t/lib/DBICVersionNew.pm new file mode 100644 index 0000000..f92c3a5 --- /dev/null +++ b/t/lib/DBICVersionNew.pm @@ -0,0 +1,48 @@ +package DBICVersion::Table; + +use base 'DBIx::Class'; +use strict; +use warnings; + +__PACKAGE__->load_components(qw/ Core/); +__PACKAGE__->table('TestVersion'); + +__PACKAGE__->add_columns + ( 'Version' => { + 'data_type' => 'INTEGER', + 'is_auto_increment' => 1, + 'default_value' => undef, + 'is_foreign_key' => 0, + 'is_nullable' => 0, + 'size' => '' + }, + 'VersionName' => { + 'data_type' => 'VARCHAR', + 'is_auto_increment' => 0, + 'default_value' => undef, + 'is_foreign_key' => 0, + 'is_nullable' => 1, + 'size' => '20' + }, + ); + +__PACKAGE__->set_primary_key('Version'); + +package DBICVersion::Schema; +use base 'DBIx::Class::Schema'; +use strict; +use warnings; + +our $VERSION = '2.0'; + +__PACKAGE__->register_class('Table', 'DBICVersion::Table'); +__PACKAGE__->load_components('+DBIx::Class::Schema::Versioned'); +__PACKAGE__->upgrade_directory('t/var/'); +__PACKAGE__->backup_directory('t/var/backup/'); + +#sub upgrade_directory +#{ +# return 't/var/'; +#} + +1; diff --git a/t/lib/DBICVersionOrig.pm b/t/lib/DBICVersionOrig.pm new file mode 100644 index 0000000..5a12ce4 --- /dev/null +++ b/t/lib/DBICVersionOrig.pm @@ -0,0 +1,46 @@ +package DBICVersion::Table; + +use base 'DBIx::Class'; +use strict; +use warnings; + +__PACKAGE__->load_components(qw/ Core/); +__PACKAGE__->table('TestVersion'); + +__PACKAGE__->add_columns + ( 'Version' => { + 'data_type' => 'INTEGER', + 'is_auto_increment' => 1, + 'default_value' => undef, + 'is_foreign_key' => 0, + 'is_nullable' => 0, + 'size' => '' + }, + 'VersionName' => { + 'data_type' => 'VARCHAR', + 'is_auto_increment' => 0, + 'default_value' => undef, + 'is_foreign_key' => 0, + 'is_nullable' => 0, + 'size' => '10' + }, + ); + +__PACKAGE__->set_primary_key('Version'); + +package DBICVersion::Schema; +use base 'DBIx::Class::Schema'; +use strict; +use warnings; + +our $VERSION = '1.0'; + +__PACKAGE__->register_class('Table', 'DBICVersion::Table'); +__PACKAGE__->load_components('+DBIx::Class::Schema::Versioned'); + +sub upgrade_directory +{ + return 't/var/'; +} + +1; diff --git a/t/lib/sqlite.sql b/t/lib/sqlite.sql index 2ce5dad..c9de968 100644 --- a/t/lib/sqlite.sql +++ b/t/lib/sqlite.sql @@ -11,6 +11,7 @@ CREATE TABLE employee ( employee_id INTEGER PRIMARY KEY NOT NULL, position integer NOT NULL, group_id integer, + group_id_2 integer, name varchar(100) ); @@ -128,6 +129,14 @@ CREATE TABLE link ( ); -- +-- Table: file_columns +-- +CREATE TABLE file_columns ( + id INTEGER PRIMARY KEY NOT NULL, + file varchar(255) +); + +-- -- Table: tags -- CREATE TABLE tags (