소스 검색

apply changes for aliases

root 2 년 전
부모
커밋
769036c414
6개의 변경된 파일303개의 추가작업 그리고 16개의 파일을 삭제
  1. 1 1
      html/admin/users/edit_alias.php
  2. 29 6
      html/inc/common.php
  3. 257 7
      scripts/eyelib/mysql.pm
  4. 11 1
      scripts/stat-sync.pl
  5. 2 1
      updates/2-4-14/user_auth.sql
  6. 3 0
      updates/2-4-14/user_auth_alias.sql

+ 1 - 1
html/admin/users/edit_alias.php

@@ -66,7 +66,7 @@ require_once ($_SERVER['DOCUMENT_ROOT']."/inc/header.php");
 	<td><input type="submit" onclick="return confirm('<?php echo WEB_msg_delete; ?>?')" name="s_remove" value="<?php echo WEB_btn_delete; ?>"></td>
 </tr>
 <?php
-$t_User_auth_alias = get_records($db_link,'User_auth_alias',"auth_id=$id ORDER BY alias");
+$t_User_auth_alias = get_records($db_link,'User_auth_alias',"deleted=0 AND auth_id=$id ORDER BY alias");
 if (!empty($t_User_auth_alias)) {
 foreach ( $t_User_auth_alias as $row ) {
     print "<tr align=center>\n";

+ 29 - 6
html/inc/common.php

@@ -4110,10 +4110,10 @@ function update_record($db, $table, $filter, $newvalue)
         'deleted' => '1',
         'mac' => '1',
     ];
-    
+
     $dns_fields = [
-//        'dhcp_hostname' => '1',
         'dns_name' => '1',
+        'alias' => '1',
     ];
 
     foreach ($newvalue as $key => $value) {
@@ -4133,6 +4133,13 @@ function update_record($db, $table, $filter, $newvalue)
             }
             if (!empty($dns_fields["$key"])) {
                 $dns_changed = 1;
+                $run_sql = $run_sql . " `old_dns_name`='" . mysqli_real_escape_string($db, trim($newvalue['dns_name'])) . "',";
+            }
+        }
+        if ($table === "User_auth_alias") {
+            if (!empty($dns_fields["$key"])) {
+                $dns_changed = 1;
+                $run_sql = $run_sql . " `old_alias`='" . mysqli_real_escape_string($db, trim($newvalue['alias'])) . "',";
             }
         }
         if (!preg_match('/password/i',$key)) {
@@ -4207,8 +4214,10 @@ function delete_record($db, $table, $filter)
             $changed_log = $changed_log . " $key => $value,";
         }
     }
-    //never delete user ip record
+    $delete_it = 1;
+    //never delete user ip record or dns alias record
     if ($table === 'User_auth') {
+        $delete_it = 0;
         $changed_time = GetNowTimeString();
         $new_sql = "UPDATE $table SET deleted=1, changed=1, `changed_time`='" . $changed_time . "' WHERE $filter";
         LOG_DEBUG($db, "Run sql: $new_sql");
@@ -4216,19 +4225,33 @@ function delete_record($db, $table, $filter)
         if (!$sql_result) {
             LOG_ERROR($db, "UPDATE Request (from delete): " . mysqli_error($db));
             return;
+            }
         }
-    } else {
+
+    if ($table === 'User_auth_alias') {
+        $delete_it = 0;
+        $new_sql = "UPDATE $table SET `deleted`=1, `old_alias` = `alias`, `dns_changed`=1 WHERE $filter";
+        LOG_DEBUG($db, "Run sql: $new_sql");
+        $sql_result = mysqli_query($db, $new_sql) or LOG_ERROR($db, "SQL: $new_sql :" . mysqli_error($db));
+        if (!$sql_result) {
+            LOG_ERROR($db, "UPDATE Request (from delete): " . mysqli_error($db));
+            return;
+            }
+        }
+
+    if ($delete_it) {
         $new_sql = "DELETE FROM $table WHERE $filter";
         LOG_DEBUG($db, "Run sql: $new_sql");
         $sql_result = mysqli_query($db, $new_sql) or LOG_ERROR($db, "SQL: $new_sql :" . mysqli_error($db));
         if (!$sql_result) {
             LOG_ERROR($db, "DELETE Request: $new_sql : " . mysqli_error($db));
             return;
+            }
         }
-    }
+
     if ($table !== "sessions") {
         LOG_VERBOSE($db, "Delete FROM table $table WHERE $filter $changed_log");
-    }
+        }
     return $changed_log;
 }
 

+ 257 - 7
scripts/eyelib/mysql.pm

@@ -62,8 +62,12 @@ StrToIp
 get_first_line
 update_dns_record
 update_dns_record_by_dhcp
+update_dns_cname
+delete_dns_cname
 update_dns_hostname
+delete_dns_hostname
 update_dns_ptr
+delete_dns_ptr
 update_record
 write_db_log
 set_changed
@@ -629,18 +633,19 @@ sub update_dns_record {
 my $hdb = shift;
 my $auth_record = shift;
 
-if (!$auth_record->{dns_name}) { return 0; }
-
+#get domain
 my $ad_zone = get_option($hdb,33);
 
-#update dns block
+#get current and old dns name
 my $fqdn_static=lc($auth_record->{dns_name});
 $fqdn_static=~s/\.$ad_zone$//i;
 $fqdn_static=~s/\.$//;
 
-#skip update unknown domain
-if ($fqdn_static =~/\./) { return 0; }
+my $old_fqdn_static=lc($auth_record->{old_dns_name});
+$old_fqdn_static=~s/\.$ad_zone$//i;
+$old_fqdn_static=~s/\.$//;
 
+#get dns server
 my $ad_dns = get_option($hdb,3);
 
 my $enable_ad_dns_update = ($ad_zone and $ad_dns and $config_ref{enable_dns_updates});
@@ -649,15 +654,46 @@ log_debug("Auth record: ".Dumper($auth_record));
 log_debug("enable_ad_dns_update: ".$enable_ad_dns_update);
 log_debug("DNS update flags - zone: ".$ad_zone.", dns: ".$ad_dns.", enable_ad_dns_update: ".$enable_ad_dns_update);
 
+#dns update disabled?
 my $maybe_update_dns=( $enable_ad_dns_update and $office_networks->match_string($auth_record->{ip}) );
 if (!$maybe_update_dns) {
-    db_log_debug($hdb,"FOUND Auth_id: $auth_record->{id}. DNS update don't needed.");
+        db_log_info($hdb,"FOUND Auth_id: $auth_record->{id}. DNS update disabled.");
+        do_sql($hdb,"UPDATE User_auth_alias SET old_alias='', dns_changed=0 WHERE auth_id=".$auth_record->{id});
+        do_sql($hdb,"DELETE FROM User_auth_alias WHERE deleted=1 AND auth_id=".$auth_record->{id});
+        do_sql($hdb,"UPDATE User_auth SET dns_changed=0 WHERE auth_id=".$auth_record->{id});
+        return 0;
+    }
+
+#skip update unknown domain
+if ($fqdn_static =~/\./) {
+        do_sql($hdb,"UPDATE User_auth_alias SET old_alias='', dns_changed=0 WHERE auth_id=".$auth_record->{id});
+        do_sql($hdb,"DELETE FROM User_auth_alias WHERE deleted=1 AND auth_id=".$auth_record->{id});
+        do_sql($hdb,"UPDATE User_auth SET dns_changed=0 WHERE auth_id=".$auth_record->{id});
+        return 0;
+    }
+
+if (!$auth_record->{dns_name} or $auth_record->{deleted}) { 
+    #remove dns records
+    #get and remove aliases
+    my @aliases = get_records_sql($hdb,"SELECT * FROM User_auth_alias WHERE auth_id=".$auth_record->{id});
+    if (@aliases and scalar @aliases) {
+        foreach my $alias (@aliases) {
+            delete_dns_cname($fqdn_static,$alias->{alias},$ad_zone,$ad_dns,$hdb) if ($alias->{alias});
+            delete_dns_cname($fqdn_static,$alias->{old_alias},$ad_zone,$ad_dns,$hdb) if ($alias->{old_alias});
+            do_sql($hdb,"DELETE FROM User_auth_alias WHERE id=".$alias->{id});
+            }
+        }
+    delete_dns_hostname($fqdn_static,$auth_record->{ip},$ad_zone,$ad_dns,$hdb) if (!$fqdn_static);
+    delete_dns_hostname($old_fqdn_static,$auth_record->{ip},$ad_zone,$ad_dns,$hdb) if (!$old_fqdn_static);
+    delete_dns_ptr($fqdn_static,$auth_record->{ip},$ad_zone,$ad_dns,$hdb);
+    do_sql($hdb,"UPDATE User_auth SET old_dns_name='', dns_changed=0 WHERE auth_id=".$auth_record->{id});
     return 0;
     }
 
 log_debug("DNS update enabled.");
 
 $fqdn_static=lc($fqdn_static.'.'.$ad_zone);
+$old_fqdn_static=lc($old_fqdn_static.'.'.$ad_zone);
 
 db_log_info($hdb,"Update dns request for auth_id: $auth_record->{id} $fqdn_static => $auth_record->{ip}");
 
@@ -683,10 +719,46 @@ if (!$static_ok) {
                 } else {
                 db_log_warning($hdb,"Static record mismatch! Expected $fqdn_static => $auth_record->{ip}, recivied: $static_ref");
                 }
+        delete_dns_hostname($old_fqdn_static,$auth_record->{ip},$ad_zone,$ad_dns,$hdb) if ($old_fqdn_static);
+        #get and remove aliases
+        my @aliases = get_records_sql($hdb,"SELECT * FROM User_auth_alias WHERE auth_id=".$auth_record->{id});
+        if (@aliases and scalar @aliases) {
+            foreach my $alias (@aliases) {
+                delete_dns_cname($fqdn_static,$alias->{alias},$ad_zone,$ad_dns,$hdb) if ($alias->{alias});
+                delete_dns_cname($fqdn_static,$alias->{old_alias},$ad_zone,$ad_dns,$hdb) if ($alias->{old_alias});
+                if ($alias->{deleted}) {
+                    do_sql($hdb,"DELETE FROM User_auth_alias WHERE id=".$alias->{id});
+                    }
+                }
+            }
         update_dns_hostname($fqdn_static,$auth_record->{ip},$ad_zone,$ad_dns,$hdb);
         update_dns_ptr($fqdn_static,$auth_record->{ip},$ad_zone,$ad_dns,$hdb);
+        do_sql($hdb,"UPDATE User_auth SET old_dns_name='', dns_changed=0 WHERE auth_id=".$auth_record->{id});
+        #get and remove aliases
+        my @aliases = get_records_sql($hdb,"SELECT * FROM User_auth_alias WHERE auth_id=".$auth_record->{id});
+        if (@aliases and scalar @aliases) {
+            foreach my $alias (@aliases) {
+                update_dns_cname($fqdn_static,$alias->{alias},$ad_zone,$ad_dns,$hdb) if ($alias->{alias});
+                do_sql($hdb,"UPDATE User_auth_alias SET old_alias='', dns_changed=0 WHERE id=".$alias->{id});
+                }
+            }
         } else {
-	db_log_debug($hdb,"Static record for $fqdn_static [$static_ok] correct.");
+	db_log_debug($hdb,"Static record for $fqdn_static [$static_ok] correct. Checking aliases");
+        #get aliases
+        my @aliases = get_records_sql($hdb,"SELECT * FROM User_auth_alias WHERE dns_changed=1 AND auth_id=".$auth_record->{id});
+        if (@aliases and scalar @aliases) {
+            foreach my $alias (@aliases) {
+                if ($alias->{deleted}) {
+                    delete_dns_cname($fqdn_static,$alias->{alias},$ad_zone,$ad_dns,$hdb) if ($alias->{alias});
+                    delete_dns_cname($fqdn_static,$alias->{old_alias},$ad_zone,$ad_dns,$hdb) if ($alias->{old_alias});
+                    do_sql($hdb,"DELETE FROM User_auth_alias WHERE id=".$alias->{id});
+                    } else {
+                    delete_dns_cname($fqdn_static,$alias->{old_alias},$ad_zone,$ad_dns,$hdb) if ($alias->{old_alias});
+                    update_dns_cname($fqdn_static,$alias->{alias},$ad_zone,$ad_dns,$hdb) if ($alias->{alias});
+                    do_sql($hdb,"UPDATE User_auth_alias SET old_alias='', dns_changed=0 WHERE id=".$alias->{id});
+                    }
+                }
+            }
         }
 }
 
@@ -807,6 +879,16 @@ if ($fqdn ne '' and !$dynamic_ok) {
                     } else {
         	    db_log_info($hdb,"Static dns hostname not defined. Create dns record by dhcp request. $fqdn => $dhcp_record->{ip}");
         	    update_dns_hostname($fqdn,$dhcp_record->{ip},$ad_zone,$ad_dns,$hdb);
+        	    db_log_info($hdb,"Clear aliases if exists for $fqdn => $dhcp_record->{ip}");
+                    #get and remove aliases
+                    my @aliases = get_records_sql($hdb,"SELECT * FROM User_auth_alias WHERE auth_id=".$auth_record->{id});
+                    if (@aliases and scalar @aliases) {
+                            foreach my $alias (@aliases) {
+                                delete_dns_cname($fqdn_static,$alias->{alias},$ad_zone,$ad_dns,$hdb) if ($alias->{alias});
+                                delete_dns_cname($fqdn_static,$alias->{old_alias},$ad_zone,$ad_dns,$hdb) if ($alias->{old_alias});
+                                do_sql($hdb,"DELETE FROM User_auth_alias WHERE id=".$alias->{id});
+                            }
+                        }
         	    }
 	    } else {
             db_log_error($hdb,"Found another record with some hostname id: $name_record->{id} ip: $name_record->{ip} hostname: $name_record->{dns_name}. Skip update.");
@@ -861,6 +943,85 @@ sub unset_lock_discovery {
 
 #------------------------------------------------------------------------------------------------------------
 
+sub update_dns_cname {
+my $fqdn = shift;
+my $alias = shift;
+my $zone = shift;
+my $server = shift;
+my $db = shift;
+#skip update domain controllers
+if (!$db) {
+    log_info("DNS-UPDATE: Zone $zone Server: $server CNAME: $alias for $fqdn"); 
+    } else {
+    db_log_info($db,"DNS-UPDATE: Zone $zone Server: $server CNAME: $alias for $fqdn ");
+    }
+my $ad_zone = get_option($db,33);
+my $nsupdate_file = "/tmp/".$fqdn."-nsupdate";
+my @add_dns;
+if ($config_ref{dns_server_type}=~/windows/i) {
+    push(@add_dns,"gsstsig");
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $alias cname");
+    push(@add_dns,"update add $alias 3600 cname $fqdn.");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    do_exec('/usr/bin/kinit -k -t /opt/Eye/scripts/cfg/dns_updater.keytab dns_updater@'.uc($ad_zone).' && /usr/bin/nsupdate "'.$nsupdate_file.'"');
+    }
+
+if ($config_ref{dns_server_type}=~/bind/i) {
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $alias cname");
+    push(@add_dns,"update add $alias 3600 cname $fqdn.");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    do_exec('/usr/bin/nsupdate -k /etc/bind/rndc.key "'.$nsupdate_file.'"');
+    }
+
+if (-e "$nsupdate_file") { unlink "$nsupdate_file"; }
+}
+
+#---------------------------------------------------------------------------------------------------------------
+
+sub delete_dns_cname {
+my $fqdn = shift;
+my $alias = shift;
+my $zone = shift;
+my $server = shift;
+my $db = shift;
+if (!$db) {
+    log_info("DNS-UPDATE: Delete => Zone $zone Server: $server CNAME: $alias for $fqdn ");
+    } else {
+    db_log_info($db,"DNS-UPDATE: Delete => Zone $zone Server: $server CNAME: $alias for $fqdn");
+    }
+my $ad_zone = get_option($db,33);
+my $nsupdate_file = "/tmp/".$fqdn."-nsupdate";
+my @add_dns;
+if ($config_ref{dns_server_type}=~/windows/i) {
+    push(@add_dns,"gsstsig");
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $alias cname ");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    do_exec('/usr/bin/kinit -k -t /opt/Eye/scripts/cfg/dns_updater.keytab dns_updater@'.uc($ad_zone).' && /usr/bin/nsupdate "'.$nsupdate_file.'"');
+    }
+
+if ($config_ref{dns_server_type}=~/bind/i) {
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $alias cname");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    do_exec('/usr/bin/nsupdate -k /etc/bind/rndc.key "'.$nsupdate_file.'"');
+    }
+
+if (-e "$nsupdate_file") { unlink "$nsupdate_file"; }
+}
+
+#------------------------------------------------------------------------------------------------------------
+
 sub update_dns_hostname {
 my $fqdn = shift;
 my $ip = shift;
@@ -903,6 +1064,46 @@ if (-e "$nsupdate_file") { unlink "$nsupdate_file"; }
 
 #---------------------------------------------------------------------------------------------------------------
 
+sub delete_dns_hostname {
+my $fqdn = shift;
+my $ip = shift;
+my $zone = shift;
+my $server = shift;
+my $db = shift;
+#skip update domain controllers
+if ($fqdn=~/^dc[0-9]{1,2}\./i) { return; }
+if (!$db) {
+    log_info("DNS-UPDATE: Delete => Zone $zone Server: $server A: $fqdn IP: $ip"); 
+    } else {
+    db_log_info($db,"DNS-UPDATE: Delete => Zone $zone Server: $server A: $fqdn IP: $ip");
+    }
+my $ad_zone = get_option($db,33);
+my $nsupdate_file = "/tmp/".$fqdn."-nsupdate";
+my @add_dns;
+if ($config_ref{dns_server_type}=~/windows/i) {
+    push(@add_dns,"gsstsig");
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $fqdn A");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    do_exec('/usr/bin/kinit -k -t /opt/Eye/scripts/cfg/dns_updater.keytab dns_updater@'.uc($ad_zone).' && /usr/bin/nsupdate "'.$nsupdate_file.'"');
+    }
+
+if ($config_ref{dns_server_type}=~/bind/i) {
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $fqdn A");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    do_exec('/usr/bin/nsupdate -k /etc/bind/rndc.key "'.$nsupdate_file.'"');
+    }
+
+if (-e "$nsupdate_file") { unlink "$nsupdate_file"; }
+}
+
+#---------------------------------------------------------------------------------------------------------------
+
 sub update_dns_ptr {
 my $fqdn = shift;
 my $ip = shift;
@@ -954,6 +1155,55 @@ if (-e "$nsupdate_file") { unlink "$nsupdate_file"; }
 
 #---------------------------------------------------------------------------------------------------------------
 
+sub delete_dns_ptr {
+my $fqdn = shift;
+my $ip = shift;
+my $server = shift;
+my $db = shift;
+my $radr;
+my $zone;
+#skip update domain controllers
+if ($fqdn=~/^dc[0-9]{1,2}\./i) { return; }
+if ($ip =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
+    return 0 if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
+    $radr = "$4.$3.$2.$1.in-addr.arpa";
+    $zone = "$3.$2.$1.in-addr.arpa";
+    }
+if (!$radr or !$zone) { return 0; }
+if (!$db) { 
+    log_info("DNS-UPDATE: Delete => Zone $zone Server: $server A: $fqdn PTR: $ip"); 
+    } else {
+    db_log_info($db,"DNS-UPDATE: Delete => Zone $zone Server: $server A: $fqdn PTR: $ip");
+    }
+my $ad_zone = get_option($db,33);
+my $nsupdate_file = "/tmp/".$radr."-nsupdate";
+my @add_dns;
+if ($config_ref{dns_server_type}=~/windows/i) {
+    push(@add_dns,"gsstsig");
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $radr PTR");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    my $run_cmd = '/usr/bin/kinit -k -t /opt/Eye/scripts/cfg/dns_updater.keytab dns_updater@'.uc($ad_zone).' && /usr/bin/nsupdate "'.$nsupdate_file.'"';
+    do_exec($run_cmd);
+    }
+
+if ($config_ref{dns_server_type}=~/bind/i) {
+    push(@add_dns,"server $server");
+    push(@add_dns,"zone $zone");
+    push(@add_dns,"update delete $radr PTR");
+    push(@add_dns,"send");
+    write_to_file($nsupdate_file,\@add_dns);
+    my $run_cmd = '/usr/bin/nsupdate -k /etc/bind/rndc.key "'.$nsupdate_file.'"';
+    do_exec($run_cmd);
+    }
+
+if (-e "$nsupdate_file") { unlink "$nsupdate_file"; }
+}
+
+#---------------------------------------------------------------------------------------------------------------
+
 sub new_user {
 my $db = shift;
 my $user_info = shift;

+ 11 - 1
scripts/stat-sync.pl

@@ -109,7 +109,17 @@ if (!$pid) {
 	        }
 
             #dns changed records
-            my @dns_changed = get_records_sql($hdb,"SELECT id,dns_name,ip from User_auth WHERE deleted=0 AND dns_changed=1");
+            my @dns_changed = get_records_sql($hdb,"SELECT id,dns_name,ip,old_dns_name,deleted from User_auth WHERE dns_changed=1");
+            if (@dns_changed and scalar @dns_changed) {
+                    foreach my $auth (@dns_changed) {
+                        update_dns_record($hdb,$auth);
+        	        do_sql($hdb,"UPDATE User_auth SET dns_changed=0 WHERE id=".$auth->{id});
+                        log_info("Clear changed dns for auth id: ".$auth->{id});
+                    }
+	        }
+
+            #dns changed alias records
+            @dns_changed = get_records_sql($hdb,"SELECT id,dns_name,ip,old_dns_name,deleted FROM User_auth WHERE User_auth.id IN (SELECT auth_id FROM User_auth_alias WHERE dns_changed=1);");
             if (@dns_changed and scalar @dns_changed) {
                     foreach my $auth (@dns_changed) {
                         update_dns_record($hdb,$auth);

+ 2 - 1
updates/2-4-14/user_auth.sql

@@ -1,3 +1,4 @@
 ALTER TABLE `User_auth` ADD `dns_changed` INT NOT NULL DEFAULT '0' AFTER `dhcp_changed`;
 ALTER TABLE `User_auth` CHANGE `dhcp_changed` `dhcp_changed` INT(11) NOT NULL DEFAULT '0';
-ALTER TABLE `User_auth_alias` ADD `changed` INT NOT NULL DEFAULT '0' AFTER `timestamp`;
+ALTER TABLE `User_auth` ADD `old_dns_name` VARCHAR(100) NULL DEFAULT NULL AFTER `dns_name`;
+ALTER TABLE `User_auth` CHANGE `dns_name` `dns_name` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;

+ 3 - 0
updates/2-4-14/user_auth_alias.sql

@@ -0,0 +1,3 @@
+ALTER TABLE `User_auth_alias` ADD `dns_changed` INT NOT NULL DEFAULT '0' AFTER `timestamp`;
+ALTER TABLE `User_auth_alias` ADD `old_alias` VARCHAR(100) NULL DEFAULT NULL AFTER `alias`;
+ALTER TABLE `User_auth_alias` ADD `deleted` INT NOT NULL DEFAULT '0' AFTER `changed`;