<?php
function fd2_need($v,$m){if(!$v)throw new RuntimeException($m);}
function fd2_json($v){$j=json_encode($v,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRESERVE_ZERO_FRACTION);fd2_need($j!==false,'JSON encoding');return $j;}
function fd2_canonical($v){if(!is_array($v))return $v;foreach($v as &$x)$x=fd2_canonical($x);unset($x);if($v!==[]&&array_keys($v)!==range(0,count($v)-1))ksort($v,SORT_STRING);return $v;}
function fd2_equal($a,$b,$m){fd2_need(fd2_canonical($a)===fd2_canonical($b),$m);}
function fd2_tokens($s){preg_match_all('~\x27(?:\x27\x27|[^\x27])*\x27|"(?:""|[^"])*"|--[^\r\n]*|[A-Za-z_][A-Za-z0-9_$]*|(?:<>|:=|<=|>=)|[^\s]~u',$s,$m);$out=[];foreach($m[0]as $t)if(substr($t,0,2)!=='--')$out[]=($t[0]===chr(39)||$t[0]===chr(34))?$t:strtoupper($t);return $out;}
function fd2_trigger($actual,$approved){$headers=[];$bodies=[];foreach([$actual,$approved]as $s){fd2_need(preg_match('/FOR\s+EACH\s+ROW\b/i',$s,$m,PREG_OFFSET_CAPTURE)===1,'Full owned trigger header');$pos=$m[0][1]+strlen($m[0][0]);$bodies[]=fd2_tokens(substr($s,$pos));$header=[];foreach(fd2_tokens(substr($s,0,$pos))as $t)if(!in_array($t,['OR','REPLACE'],true))$header[]=$t[0]===chr(34)?strtoupper(substr($t,1,-1)):$t;$headers[]=$header;}fd2_equal($headers[0],$headers[1],'Exact owned trigger header');fd2_equal($bodies[0],$bodies[1],'Complete literal-preserving61-field body');}
function fd2_expected_columns($cols,$calibrated){if(!$calibrated)return $cols;foreach($cols as &$c)if(in_array($c['column_name'],['CREATE_TIME','UPDATE_TIME'],true)){$c['data_scale']=is_float($c['data_scale'])?0.0:0;$c['data_default']='CURRENT_TIMESTAMP(0)';$n=strlen($c['data_default']);$c['default_length']=is_float($c['default_length'])?(float)$n:$n;}unset($c);return $cols;}
function fd2_expected_ddl($sql,$calibrated){if(!$calibrated)return $sql;foreach(['CREATE_TIME','UPDATE_TIME']as $n){$old='"'.$n.'" DATETIME(6) DEFAULT CURRENT_TIMESTAMP NOT NULL';$new='"'.$n.'" DATETIME(0) DEFAULT CURRENT_TIMESTAMP(0) NOT NULL';fd2_need(substr_count($sql,$old)===1,'Exactly two original time declarations');$sql=str_replace($old,$new,$sql);}return $sql;}
