<?php
declare(strict_types=1);
$lane=getenv('MR3_LANE');$root=getenv('MR3_ROOT');if(!in_array($lane,['B','C'],true))exit(78);$cfg=(static function($file){return require $file;})($root.'/application/database.php');
if($lane==='B'){$local=(static function($file){return require $file;})('/workspace/zhct/zhctproject/store/application/config/local/database.php');foreach(['hostname','database','username','password'] as $k)if($cfg[$k]!==$local[$k])throw new RuntimeException('Not code local');$dsn='mysql:host='.$cfg['hostname'].';port='.($cfg['hostport']?:3306).';dbname='.$cfg['database'].';charset=utf8mb4';}else{if($cfg['hostname']!=='127.0.0.1'||(string)$cfg['hostport']!=='5237'||$cfg['database']!=='ZHCT_APP')throw new RuntimeException('Not nonprod Store DM');$dsn='dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';}
$db=new PDO($dsn,$cfg['username'],$cfg['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);
$query=static function(string $sql,array $args=[])use($db):array{if(!preg_match('/^(SELECT|CALL SP_TABLEDEF)\b/i',$sql))throw new RuntimeException('Readonly metadata/query only');$st=$db->prepare($sql);$st->execute($args);$rows=[];while($row=$st->fetch(PDO::FETCH_ASSOC)){$row=array_change_key_case($row,CASE_LOWER);foreach($row as &$v)if(is_resource($v))$v=stream_get_contents($v);unset($v);$rows[]=$row;if(count($rows)>5000)throw new RuntimeException('Metadata cap');}return $rows;};
$fields=['dingtalk_avatar','update_time','dingtalk_userid','primary_department_uuid','dingtalk_last_seen_full_batch_id','dingtalk_sync_time','dingtalk_sync_md5'];$out=['lane'=>$lane,'business_dml'=>0,'ddl'=>0,'external_calls'=>0,'probes'=>[],'metadata_errors'=>[]];
if($lane==='B'){$out['effective_database_verified']=$db->query('SELECT DATABASE()')->fetchColumn()===$local['database'];$out['version']=$db->query('SELECT VERSION()')->fetchColumn();$out['columns']=$query("SELECT COLUMN_NAME,COLUMN_TYPE,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_SET_NAME,COLLATION_NAME,CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,DATETIME_PRECISION,NUMERIC_PRECISION,NUMERIC_SCALE,EXTRA,COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ydy_staff' ORDER BY ORDINAL_POSITION");$out['indexes']=$query("SELECT INDEX_NAME,NON_UNIQUE,SEQ_IN_INDEX,COLUMN_NAME,SUB_PART,COLLATION,INDEX_TYPE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ydy_staff' ORDER BY INDEX_NAME,SEQ_IN_INDEX");$out['constraints']=$query("SELECT CONSTRAINT_NAME,COLUMN_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ydy_staff' ORDER BY CONSTRAINT_NAME,ORDINAL_POSITION");$out['triggers']=$query("SELECT TRIGGER_NAME,EVENT_MANIPULATION FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE='ydy_staff'");$out['row_counts']=$query('SELECT COUNT(*) AS TOTAL,SUM(CASE WHEN IS_SHOW=1 THEN 1 ELSE 0 END) AS VISIBLE FROM ydy_staff');
 foreach($fields as $f){$sql='SELECT COUNT(*) AS TOTAL,SUM(CASE WHEN '.$f.' IS NULL THEN 1 ELSE 0 END) AS NULLS,MAX(CHAR_LENGTH(CAST('.$f.' AS CHAR))) AS MAX_CHARS,MAX(LENGTH(CAST('.$f.' AS CHAR))) AS MAX_BYTES FROM ydy_staff';$out['source_field_stats'][$f]=$query($sql);}
}else{$out['effective_database_verified']=$db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn()==='ZHCT_APP';$out['version']=$query('SELECT * FROM V$VERSION');$out['columns']=$query("SELECT COLUMN_NAME,DATA_TYPE,DATA_LENGTH,DATA_PRECISION,DATA_SCALE,NULLABLE,DATA_DEFAULT,CHAR_LENGTH,CHAR_USED FROM USER_TAB_COLUMNS WHERE TABLE_NAME='YDY_STAFF' ORDER BY COLUMN_ID");$out['indexes']=$query("SELECT i.INDEX_NAME,i.UNIQUENESS,c.COLUMN_NAME,c.COLUMN_POSITION FROM USER_INDEXES i JOIN USER_IND_COLUMNS c ON i.INDEX_NAME=c.INDEX_NAME WHERE i.TABLE_NAME='YDY_STAFF' ORDER BY i.INDEX_NAME,c.COLUMN_POSITION");$out['constraints']=$query("SELECT CONSTRAINT_NAME,CONSTRAINT_TYPE,R_CONSTRAINT_NAME,SEARCH_CONDITION FROM USER_CONSTRAINTS WHERE TABLE_NAME='YDY_STAFF' ORDER BY CONSTRAINT_NAME");$out['triggers']=$query("SELECT TRIGGER_NAME,STATUS,TRIGGERING_EVENT FROM USER_TRIGGERS WHERE TABLE_NAME='YDY_STAFF'");$out['row_counts']=$query('SELECT COUNT(*) AS TOTAL,SUM(CASE WHEN IS_SHOW=1 THEN 1 ELSE 0 END) AS VISIBLE FROM YDY_STAFF');
 foreach(['CASE_SENSITIVE','COMPATIBLE_MODE','LENGTH_IN_CHAR','BLANK_PAD_MODE','TIME_ZONE'] as $name){try{$out['database_parameters'][$name]=$query('SELECT PARA_NAME,PARA_VALUE FROM V$DM_INI WHERE PARA_NAME=?',[$name]);}catch(Throwable $e){$out['metadata_errors'][]=['item'=>$name,'class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}}
 try{$out['table_definition']=$query("CALL SP_TABLEDEF('ZHCT_APP','YDY_STAFF')");}catch(Throwable $e){$out['metadata_errors'][]=['item'=>'tabledef','class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}
 try{$row=$query('SELECT * FROM V$DM_INI WHERE ROWNUM<=1');$out['dm_ini_column_names']=array_keys($row[0]??[]);}catch(Throwable $e){$out['metadata_errors'][]=['item'=>'dm_ini_keys','class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}
}
$columns=array_map(static function($v){return strtolower($v['column_name']);},$out['columns']);$out['missing_seven']=array_values(array_diff($fields,$columns));$out['column_count']=count($columns);
$tests=$lane==='B'?[
 'case_default'=>"SELECT CASE WHEN CAST('AbC' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_general_ci = CAST('abc' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_general_ci THEN 1 ELSE 0 END AS EQ",
 'case_bin'=>"SELECT CASE WHEN CAST('AbC' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin = CAST('abc' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin THEN 1 ELSE 0 END AS EQ",
 'trailing_bin'=>"SELECT CASE WHEN CAST('AbC' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin = CAST('AbC ' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin THEN 1 ELSE 0 END AS EQ",
 'ascii_case'=>"SELECT CASE WHEN CAST('AbC' AS CHAR CHARACTER SET ascii) COLLATE ascii_bin = CAST('abc' AS CHAR CHARACTER SET ascii) COLLATE ascii_bin THEN 1 ELSE 0 END AS EQ",
 'ascii_trailing'=>"SELECT CASE WHEN CAST('AbC' AS CHAR CHARACTER SET ascii) COLLATE ascii_bin = CAST('AbC ' AS CHAR CHARACTER SET ascii) COLLATE ascii_bin THEN 1 ELSE 0 END AS EQ",
 'max_uint64'=>"SELECT CAST('18446744073709551615' AS DECIMAL(20,0)) AS EXACT_TEXT",
 'cast_datetime0'=>"SELECT CAST('2026-10-10 11:22:33.123456' AS DATETIME) AS DT",
]:[
 'case_default'=>"SELECT CASE WHEN CAST('AbC' AS VARCHAR(128 CHAR)) = CAST('abc' AS VARCHAR(128 CHAR)) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'trailing_varchar'=>"SELECT CASE WHEN CAST('AbC' AS VARCHAR(128 CHAR)) = CAST('AbC ' AS VARCHAR(128 CHAR)) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'trailing_char'=>"SELECT CASE WHEN CAST('AbC' AS CHAR(36 CHAR)) = CAST('AbC ' AS CHAR(36 CHAR)) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'rtrim_case'=>"SELECT CASE WHEN RTRIM(CAST('AbC' AS VARCHAR(128 CHAR))) = RTRIM(CAST('abc ' AS VARCHAR(128 CHAR))) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'char_length'=>"SELECT LENGTH(CAST('AbC' AS CHAR(36 CHAR))) AS N, LENGTHB(CAST('AbC' AS CHAR(36 CHAR))) AS BYTES FROM DUAL",
 'chinese_character_unit'=>"SELECT LENGTH(CAST('中文' AS VARCHAR(2 CHAR))) AS N,LENGTHB(CAST('中文' AS VARCHAR(2 CHAR))) AS BYTES FROM DUAL",
 'max_uint64'=>"SELECT CAST('18446744073709551615' AS DECIMAL(20,0)) AS EXACT_TEXT FROM DUAL",
 'max_uint64_cast_text'=>"SELECT CAST(CAST('18446744073709551615' AS DECIMAL(20,0)) AS VARCHAR(20)) AS EXACT_TEXT FROM DUAL",
 'binary_case'=>"SELECT CASE WHEN CAST('AbC' AS VARBINARY(128)) = CAST('abc' AS VARBINARY(128)) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'binary_trimmed_tail'=>"SELECT CASE WHEN CAST(RTRIM('AbC') AS VARBINARY(128)) = CAST(RTRIM('AbC ') AS VARBINARY(128)) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'nlssort_binary_case'=>"SELECT CASE WHEN NLSSORT('AbC','NLS_SORT=BINARY') = NLSSORT('abc','NLS_SORT=BINARY') THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'regexp_ascii'=>"SELECT CASE WHEN REGEXP_LIKE('AbC012','^[ -~]*$') THEN 1 ELSE 0 END AS ASCII_VALID FROM DUAL",
 'case_flag_function'=>"SELECT SF_GET_CASE_SENSITIVE_FLAG() AS CASE_FLAG FROM DUAL",
 'binary_hex_values'=>"SELECT RAWTOHEX(CAST('AbC' AS VARBINARY(128))) AS UPPER_RAW, RAWTOHEX(CAST('abc' AS VARBINARY(128))) AS LOWER_RAW FROM DUAL",
 'binary_hex_case'=>"SELECT CASE WHEN RAWTOHEX(CAST('AbC' AS VARBINARY(128))) = RAWTOHEX(CAST('abc' AS VARBINARY(128))) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'binary_hex_trailing'=>"SELECT CASE WHEN RAWTOHEX(CAST(RTRIM('AbC') AS VARBINARY(128))) = RAWTOHEX(CAST(RTRIM('AbC ') AS VARBINARY(128))) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'binary_hex_chinese'=>"SELECT RAWTOHEX(CAST('中文' AS VARBINARY(512))) AS HEX FROM DUAL",
 'ascii_nonascii_reject'=>"SELECT CASE WHEN REGEXP_LIKE('中文','^[ -~]*$') THEN 1 ELSE 0 END AS ASCII_VALID FROM DUAL",
 'utl_raw_ascii_values'=>"SELECT RAWTOHEX(UTL_RAW.CAST_TO_RAW('AbC')) AS UPPER_RAW,RAWTOHEX(UTL_RAW.CAST_TO_RAW('abc')) AS LOWER_RAW FROM DUAL",
 'utl_raw_ascii_case'=>"SELECT CASE WHEN RAWTOHEX(UTL_RAW.CAST_TO_RAW('AbC')) = RAWTOHEX(UTL_RAW.CAST_TO_RAW('abc')) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'utl_raw_ascii_trailing'=>"SELECT CASE WHEN RAWTOHEX(UTL_RAW.CAST_TO_RAW(RTRIM('AbC'))) = RAWTOHEX(UTL_RAW.CAST_TO_RAW(RTRIM('AbC '))) THEN 1 ELSE 0 END AS EQ FROM DUAL",
 'utl_raw_chinese'=>"SELECT RAWTOHEX(UTL_RAW.CAST_TO_RAW('中文')) AS HEX FROM DUAL",
 'cast_datetime0'=>"SELECT CAST('2026-10-10 11:22:33.123456' AS DATETIME(0)) AS DT FROM DUAL",
 'cast_timestamp0'=>"SELECT CAST('2026-10-10 11:22:33.123456' AS TIMESTAMP(0)) AS DT FROM DUAL",
];
foreach($tests as $label=>$sql){try{$rows=$query($sql);$types=[];foreach($rows[0]??[] as $k=>$v)$types[$k]=gettype($v);$out['probes'][$label]=['rows'=>$rows,'php_types'=>$types];}catch(Throwable $e){$out['probes'][$label]=['error_class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}}
if($lane==='C')foreach(['upper'=>'AbC','lower'=>'abc','tail'=>'AbC ','chinese'=>'中文'] as $label=>$value){try{$out['bound_raw_hex'][$label]=$query('SELECT RAWTOHEX(UTL_RAW.CAST_TO_RAW(?)) AS HEX FROM DUAL',[$value]);}catch(Throwable $e){$out['bound_raw_hex'][$label]=['error_class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}}
if($lane==='C')foreach(['bound_default_case'=>["SELECT CASE WHEN CAST(? AS VARCHAR(128 CHAR))=CAST(? AS VARCHAR(128 CHAR)) THEN 1 ELSE 0 END AS EQ FROM DUAL",['AbC','abc']], 'bound_raw_hex_case'=>["SELECT CASE WHEN RAWTOHEX(UTL_RAW.CAST_TO_RAW(?))=RAWTOHEX(UTL_RAW.CAST_TO_RAW(?)) THEN 1 ELSE 0 END AS EQ FROM DUAL",['AbC','abc']], 'bound_raw_hex_trailing'=>["SELECT CASE WHEN RAWTOHEX(UTL_RAW.CAST_TO_RAW(RTRIM(?)))=RAWTOHEX(UTL_RAW.CAST_TO_RAW(RTRIM(?))) THEN 1 ELSE 0 END AS EQ FROM DUAL",['AbC','AbC ']]] as $label=>$test){try{$out['bound_comparisons'][$label]=$query($test[0],$test[1]);}catch(Throwable $e){$out['bound_comparisons'][$label]=['error_class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}}
file_put_contents(__DIR__.'/result-private-'.$lane.'.json',json_encode($out,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));chmod(__DIR__.'/result-private-'.$lane.'.json',0600);echo json_encode($out,JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE),"\n";
