Show
Ignore:
Timestamp:
04/16/08 15:36:30 (20 months ago)
Author:
mpaschal
Message:

Land the new implementation of metadata based on narrow tables
BugzID: 68749

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • branches/release-35/lib/MT/Object.pm

    r1873 r1927  
    5353    } 
    5454 
     55    my %meta; 
     56 
    5557    my $super_props = $class->SUPER::properties(); 
     58    $props->{meta} = 1 if $super_props && $super_props->{meta}; 
     59 
     60    if ($props->{meta}) { 
     61        # yank out any meta columns before we start working on column_defs 
     62        $meta{$_} = delete $props->{column_defs}{$_} 
     63            for grep { $props->{column_defs}{$_} =~ m/\bmeta\b/ } 
     64            keys %{ $props->{column_defs} }; 
     65    } 
     66 
    5667    if ($super_props) { 
    5768        # subclass; merge hash 
    58         for (qw(primary_key meta_column class_column datasource driver audit meta)) { 
     69        for (qw(primary_key class_column datasource driver audit)) { 
    5970            $props->{$_} = $super_props->{$_} 
    6071                if exists $super_props->{$_} && !(exists $props->{$_}); 
    6172        } 
    62         for my $p (qw(column_defs defaults indexes meta_columns)) { 
     73        for my $p (qw(column_defs defaults indexes)) { 
    6374            if (exists $super_props->{$p}) { 
    6475                foreach my $k (keys %{ $super_props->{$p} }) { 
     
    8192    # Legacy MT::Object types only define 'columns'; we still support that 
    8293    # but they aren't handled properly with the upgrade system as a result. 
    83     if (exists $props->{column_defs}) { 
    84         $props->{columns} = [ keys %{ $props->{column_defs} } ]; 
    85     } else { 
     94    if (! exists $props->{column_defs}) { 
    8695        map { $props->{column_defs}{$_} = () } @{ $props->{columns} }; 
    8796    } 
     97    $props->{columns} = [ keys %{ $props->{column_defs} } ]; 
    8898 
    8999    # Support audit flags 
     
    98108    } 
    99109 
    100     # Metadata column 
    101     $props->{meta_column} ||= 'meta' if exists $props->{meta}; 
    102     if (my $col = $props->{meta_column}) { 
    103         if (!$props->{column_defs}{$col}) { 
    104             $props->{column_defs}{$col} = 'blob'; 
    105             push @{ $props->{columns} }, $col; 
    106         } 
    107         no strict 'refs'; ## no critic 
    108         *{$class . '::' . $col} = \&__meta_column; 
    109         $class->add_trigger( pre_save => \&pre_save_serialize_metadata ); 
    110     } 
    111  
    112110    # Classed object types 
    113111    $props->{class_column} ||= 'class' if exists $props->{class_type}; 
     
    151149        foreach my $isa_class (@classes) { 
    152150            next if UNIVERSAL::isa($class, $isa_class); 
    153             eval "require $isa_class;" or die; 
     151            eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $isa_class;" or die; 
    154152            no strict 'refs'; ## no critic 
    155153            push @{$class . '::ISA'}, $isa_class; 
     
    160158            for my $name (keys %$cols) { 
    161159                next if exists $props->{column_defs}{$name}; 
     160                if ($cols->{$name} =~ m/\bmeta\b/) { 
     161                    $meta{$name} = $cols->{$name}; 
     162                    next; 
     163                } 
     164 
    162165                $class->install_column($name, $cols->{$name}); 
    163166                $props->{indexes}{$name} = 1 
     
    226229        no strict 'refs'; ## no critic 
    227230        *{$class . '::driver'} = sub { $_[0]->dbi_driver(@_) }; 
     231    } 
     232 
     233    # inherit parent's metadata setup 
     234    if ($props->{meta}) { # if ($super_props && $super_props->{meta_installed}) { 
     235        $class->install_meta({ ( %meta ? ( column_defs => \%meta ) : ( columns => [] ) ) }); 
     236        $class->add_trigger( post_remove => \&remove_meta ); 
    228237    } 
    229238 
     
    347356            return $package; 
    348357        } else { 
    349             eval "use $package;"; 
     358            eval "# line " . __LINE__ . " " . __FILE__ . "\nuse $package;"; 
    350359            return $package unless $@; 
    351             eval "use $pkg; $package->new;"; 
     360            eval "# line " . __LINE__ . " " . __FILE__ . "\nuse $pkg; $package->new;"; 
    352361            return $package unless $@; 
    353362        } 
     
    373382# 'meta' metadata column support 
    374383 
     384sub new { 
     385    my $class = shift; 
     386    my $obj = $class->SUPER::new(@_); 
     387    if ($obj->properties->{meta_installed}) { 
     388        $obj->init_meta(); 
     389    } 
     390    return $obj; 
     391} 
     392 
     393sub init_meta { 
     394    my $obj = shift; 
     395    require MT::Meta::Proxy; 
     396    $obj->{__meta} = MT::Meta::Proxy->new($obj); 
     397} 
     398 
    375399sub install_meta { 
    376400    my $class = shift; 
    377     my ($props) = @_; 
     401    my ($params) = @_; 
    378402    if ( ( $class ne 'MT::Config' ) && (!$MT::plugins_installed) ) { 
    379         push @PRE_INIT_META, [$class, $props]; 
     403        push @PRE_INIT_META, [$class, $params]; 
    380404        return; 
    381405    } 
    382     my $cprops = $class->properties; 
    383     my $fields = $cprops->{meta_columns} ||= {}; 
    384     my $meta_col = $cprops->{meta_column}; 
    385     foreach my $name (@{ $props->{columns} }) { 
    386         $fields->{$name} = (); 
    387         # Skip adding this method if the class overloads it. 
    388         # this lets the SUPER::columnname magic do it's thing 
    389         unless ($class->can($name)) { 
    390             no strict 'refs'; ## no critic 
    391             *{"${class}::$name"} = sub { shift->$meta_col($name, @_) }; 
    392         } 
    393     } 
     406 
     407    require MT::Meta; 
     408    my $pkg = ref $class || $class; 
     409    if (!$pkg->SUPER::properties->{meta_installed}) { 
     410        $pkg->add_trigger( post_save => \&post_save_save_metadata ); 
     411        $pkg->add_trigger( post_load => \&post_load_initialize_metadata ); 
     412    } 
     413 
     414    my $props = $class->properties; 
     415 
     416    if (!$params->{columns} && !$params->{fields} && !$params->{column_defs}) { 
     417        return $class->error('No meta fields specified to install_meta'); 
     418    } 
     419 
     420    $params->{fields} ||= []; 
     421    if (my $cols = delete $params->{columns}) { 
     422        foreach my $col (@$cols) { 
     423            push @{ $params->{fields} }, { 
     424                name => $col, 
     425                type => 'vblob', 
     426            }; 
     427            # $props->{fields}{$col} = 'vblob'; 
     428        } 
     429    } 
     430    if (my $cols = delete $params->{column_defs}) { 
     431        foreach my $col ( keys %$cols ) { 
     432            my $type = $cols->{$col}; 
     433            $type =~ s/\s.*//; # take first keyword, ignoring anything after 
     434            $type .= '_indexed' 
     435                if $cols->{$col} =~ m/\bindexed\b/; 
     436            $type = MT::Meta->normalize_type($type); 
     437 
     438            push @{ $params->{fields} }, { 
     439                name => $col, 
     440                type => $type, 
     441            }; 
     442            # $props->{fields}{$col} = $type; 
     443        } 
     444    } 
     445 
     446    $params->{datasource} ||= $class->datasource . '_meta'; 
     447 
     448    if ($props->{meta_installed} && !@{ $params->{fields} }) { 
     449        return 1; 
     450    } 
     451 
     452    if (my $fields = MT::Meta->install($pkg, $params)) { 
     453        # we may have inherited meta fields so lets update with 
     454        # the fields returned by MT::Meta 
     455        $props->{fields} = $fields; 
     456    } 
     457 
     458    return $props->{meta_installed} = 1; 
     459} 
     460 
     461sub meta_args { 
     462    my $class = shift; 
     463    my $id_field = $class->datasource . '_id'; 
     464    return { 
     465        key         => $class->datasource, 
     466        column_defs => { 
     467            $id_field         => 'integer not null', 
     468            type              => 'string(255) not null', 
     469            vchar             => 'string(255)', 
     470            vchar_indexed     => 'string(255)', 
     471            vdatetime         => 'datetime', 
     472            vdatetime_indexed => 'datetime', 
     473            vinteger          => 'integer', 
     474            vinteger_indexed  => 'integer', 
     475            vfloat            => 'float', 
     476            vfloat_indexed    => 'float', 
     477            vblob             => 'blob', 
     478            vclob             => 'text', 
     479        }, 
     480        columns => [ $id_field, qw( 
     481            type 
     482            vchar 
     483            vchar_indexed 
     484            vdatetime 
     485            vdatetime_indexed 
     486            vinteger 
     487            vinteger_indexed 
     488            vfloat 
     489            vfloat_indexed 
     490            vblob 
     491            vclob 
     492        ) ], 
     493        indexes => { 
     494            $id_field => 1, 
     495            id_type   => { columns => [ $id_field, 'type' ] }, 
     496            id_type_vchar => { columns => [ $id_field, 'type', 'vchar_indexed' ] }, 
     497            id_type_vdatetime => { columns => [ $id_field, 'type', 
     498                'vdatetime_indexed' ] }, 
     499            id_type_vinteger => { columns => [ $id_field, 'type', 
     500                'vinteger_indexed' ] }, 
     501            id_type_vfloat => { columns => [ $id_field, 'type', 
     502                'vfloat_indexed' ] }, 
     503        }, 
     504        primary_key => [ $class->datasource . '_id', 'type' ], 
     505    }; 
    394506} 
    395507 
    396508sub has_meta { 
    397     my $props = $_[0]->properties; 
    398     return $props->{meta} && (@_ > 1 ? exists $props->{meta_columns}{$_[1]} : 1); 
    399 } 
    400  
    401 sub pre_save_serialize_metadata { 
    402     my ($obj) = shift; 
    403     my $meta_col = $obj->properties->{meta_column}; 
    404     if ($obj->{changed_cols}{$meta_col}) { 
    405         require MT::Serialize; 
    406         my $meta = $obj->$meta_col; 
    407         $obj->$meta_col(MT::Serialize->serialize(\$meta)); 
    408     } 
    409 } 
    410  
    411 sub __thaw_meta { 
    412     my ($meta) = @_; 
    413     $$meta = '' unless defined $$meta; 
    414     require MT::Serialize; 
    415     my $out = MT::Serialize->unserialize($$meta); 
    416     if (ref $out eq 'REF') { 
    417         return $$out; 
    418     } else { 
    419         return {}; 
    420     } 
    421 } 
    422  
    423 # $obj->meta returns a hashref of metadata information 
    424 # $obj->meta($scalar) allows assignment of a serialized value 
    425 # $obj->meta('name', 'value') assigns an individual metadata element 
    426 # $obj->meta('name') returns an individual metadata value 
    427 # $obj->save will automatically serialize the metadata back to the database 
    428 sub __meta_column { 
    429     my $obj = shift; 
    430     my $meta_col = $obj->properties->{meta_column} or return; 
    431  
    432     if (@_) { 
    433         my $var = shift; 
    434         if ((defined $var) && ($var =~ m/^SERG\0\0\0\0/)) { 
    435             return $obj->column($meta_col, $var); 
    436         } 
    437         my $meta = $obj->column($meta_col); 
    438         if (!defined($meta)) { 
    439             $obj->{column_values}{$meta_col} = $meta = {}; 
    440         } elsif (!ref $meta) { 
    441             $obj->{column_values}{$meta_col} = $meta = __thaw_meta(\$meta); 
    442         } 
     509    my $obj = shift; 
     510    return $obj->is_meta_column(@_) if @_; 
     511    return $obj->properties->{meta_installed} ? 1 : 0; 
     512} 
     513 
     514sub post_load_initialize_metadata { 
     515    my $obj = shift; 
     516    if (defined $obj && $obj->properties->{meta_installed}) { 
     517        $obj->init_meta(); 
     518        $obj->{__meta}->set_primary_keys($obj); 
     519    } 
     520} 
     521 
     522sub is_meta_column { 
     523    my $obj = shift; 
     524    my ($field) = @_; 
     525 
     526    my $props = $obj->properties; 
     527    return unless $props->{meta_installed}; 
     528 
     529    my $meta = $obj->meta_pkg; 
     530    return 1 if $props->{fields}{$field}; 
     531 
     532    return; 
     533} 
     534 
     535sub meta_pkg { 
     536    my $class = shift; 
     537    my $props = $class->properties; 
     538    return unless $props->{meta}; # this only works for meta-enabled classes 
     539 
     540    return $props->{meta_pkg} if $props->{meta_pkg}; 
     541 
     542    my $meta = ref $class || $class; 
     543    $meta .= '::Meta'; 
     544    return $props->{meta_pkg} = $meta; 
     545} 
     546 
     547sub has_column { 
     548    my $obj = shift; 
     549    return 1 if $obj->SUPER::has_column(@_); 
     550    return 1 if $obj->is_meta_column(@_); 
     551    return; 
     552} 
     553 
     554sub post_save_save_metadata { 
     555    my $obj = shift; 
     556    if (defined $obj && exists $obj->{__meta}) { 
     557        $obj->{__meta}->set_primary_keys($obj); 
     558        $obj->{__meta}->save; 
     559    } 
     560} 
     561 
     562sub meta { 
     563    my $obj = shift; 
     564    my ($name, $value) = @_; 
     565 
     566    return !$obj->{__meta} ? undef 
     567         : 2 == scalar @_  ? $obj->{__meta}->set($name, $value) 
     568         : 1 == scalar @_  ? $obj->{__meta}->get($name) 
     569         :                   $obj->{__meta}->get_hash 
     570         ; 
     571} 
     572 
     573sub meta_obj { 
     574    my $obj = shift; 
     575    return $obj->{__meta}; 
     576} 
     577 
     578sub column_func { 
     579    my $obj = shift; 
     580    my ($col) = @_; 
     581    return if !$col; 
     582 
     583    return $obj->SUPER::column_func(@_) 
     584        if !$obj->is_meta_column($col); 
     585 
     586    return sub { 
     587        my $obj = shift; 
    443588        if (@_) { 
    444             $meta->{$var} = shift if @_; 
    445             $obj->{changed_cols}{$meta_col}++; 
    446         } 
    447         return $meta->{$var}; 
    448     } else { 
    449         my $meta = $obj->column($meta_col); 
    450         if (!ref $meta) { 
    451             $meta = __thaw_meta(\$meta); 
    452             $obj->{column_values}{$meta_col} = $meta; 
    453         } 
    454         # we should assume changes are going to be made, since 
    455         # we can't really monitor the hash once it has left us 
    456         $obj->{changed_cols}{$meta_col}++; 
    457         return $meta; 
    458     } 
     589            $obj->{__meta}->set($col, @_); 
     590        } 
     591        else { 
     592            $obj->{__meta}->get($col); 
     593        } 
     594    }; 
    459595} 
    460596 
     
    466602    my $ret = sprintf '%04d-%02d-%02d %02d:%02d:%02d', unpack 'A4A2A2A2A2A2', $_[0];   
    467603    return $ret;   
    468 }   
     604} 
    469605   
    470606sub db2ts {   
     
    754890} 
    755891 
     892sub clone_all { 
     893    my $obj = shift; 
     894    my $clone = $obj->SUPER::clone_all(); 
     895    $clone->{__meta} = $obj->{__meta};  # TODO: clone this too 
     896    return $clone; 
     897} 
     898 
    756899sub clone { 
    757900    my $obj = shift; 
    758901    my($param) = @_; 
    759     my $clone = $obj->SUPER::clone_all; 
     902    my $clone = $obj->clone_all(); 
    760903 
    761904    ## If the caller has listed a set of columns not to copy to the clone, 
     
    821964} 
    822965 
     966sub remove_meta { 
     967    my $obj = shift; 
     968    return 1 unless ref $obj; 
     969    my $mpkg = $obj->meta_pkg or return; 
     970    my $id_field = $obj->datasource . '_id'; 
     971    return $mpkg->remove({ $id_field => $obj->id }); 
     972} 
     973 
    823974sub remove_children { 
    824975    my $obj = shift; 
     
    834985    my $obj_id = $obj->id; 
    835986    for my $class (@classes) { 
    836         eval "use $class;"; 
     987        eval "# line " . __LINE__ . " " . __FILE__ . "\nuse $class;"; 
    837988        $class->remove({ $key => $obj_id }); 
    838989    } 
     
    9431094    $def{key} = 1 if ($props->{primary_key}) && ($props->{primary_key} eq $col); 
    9441095    $def{auto} = 1 if $def =~ m/\bauto[_ ]increment\b/i; 
    945     $def{default} = $props->{defaults}{$col} if exists $props->{defaults}{$col}; 
     1096    $def{default} = $props->{defaults}{$col} 
     1097        if exists $props->{defaults}{$col}; 
    9461098    \%def; 
    9471099} 
     
    10081160    $hash; 
    10091161} 
     1162 
     1163package MT::Object::Meta; 
     1164 
     1165use base qw( Data::ObjectDriver::BaseObject ); 
     1166 
     1167sub driver { $MT::Object::DRIVER ||= MT::ObjectDriverFactory->new } 
     1168 
     1169sub install_properties { 
     1170    my $class = shift; 
     1171    my ($props) = @_; 
     1172    $props->{column_defs}->{$_} ||= 'string' 
     1173        for @{ $props->{columns} }; 
     1174    $class->SUPER::install_properties(@_); 
     1175} 
     1176 
     1177sub meta_pkg { undef } 
     1178 
     1179*table_name = \&MT::Object::table_name; 
     1180*column_defs = \&MT::Object::column_defs; 
     1181*column_def = \&MT::Object::column_def; 
     1182*index_defs = \&MT::Object::index_defs; 
     1183*__parse_defs = \&MT::Object::__parse_defs; 
     1184*__parse_def = \&MT::Object::__parse_def; 
     1185*count = \&MT::Object::count; 
     1186*columns_of_type = \&MT::Object::columns_of_type; 
     1187 
     1188# TODO: copy this too 
     1189sub blob_requires_zip {} 
    10101190 
    101111911;