diff --git a/modules/EcommerceFinance/Services/UpstreamOdbcConnection.php b/modules/EcommerceFinance/Services/UpstreamOdbcConnection.php index 923f1ecd..88b16bc0 100644 --- a/modules/EcommerceFinance/Services/UpstreamOdbcConnection.php +++ b/modules/EcommerceFinance/Services/UpstreamOdbcConnection.php @@ -176,12 +176,14 @@ PHP; throw new ServiceUnavailableHttpException(60, '上游数据库配置不完整,请联系管理员。'); } - return new PDO( + $pdo = new PDO( self::pdoDblibDsn($host, $port, $db, $tdsVersion), $user, $pass, self::pdoOptions($queryTimeout), ); + + return self::initializeSqlServerSession($pdo); } $dsn = self::odbcDsn( @@ -197,7 +199,9 @@ PHP; $applicationIntent, ); - return new PDO($dsn, null, null, self::pdoOptions($queryTimeout)); + return self::initializeSqlServerSession( + new PDO($dsn, null, null, self::pdoOptions($queryTimeout)), + ); } catch (PDOException $exception) { Log::warning('上游数据库连接失败。', [ 'source' => $source, @@ -303,6 +307,20 @@ PHP; ]; } + /** + * SQL Server 的跨库/异构只读视图要求这两个会话选项保持开启。 + * + * 这里在连接创建后统一初始化,避免各 Repository 拼接 SET 多语句, + * 也继续让 UpstreamReadOnlyQuery 只接受单条 SELECT/WITH。 + */ + private static function initializeSqlServerSession(PDO $pdo): PDO + { + $pdo->exec('SET ANSI_NULLS ON'); + $pdo->exec('SET ANSI_WARNINGS ON'); + + return $pdo; + } + private static function encryptionAttributes(string $driver, string $mode): string { if (strcasecmp($driver, 'FreeTDS') === 0) { diff --git a/tests/Unit/EcommerceFinance/UpstreamOdbcConnectionTest.php b/tests/Unit/EcommerceFinance/UpstreamOdbcConnectionTest.php index f5ede1b4..d27a1aaa 100644 --- a/tests/Unit/EcommerceFinance/UpstreamOdbcConnectionTest.php +++ b/tests/Unit/EcommerceFinance/UpstreamOdbcConnectionTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\EcommerceFinance; use Modules\EcommerceFinance\Services\UpstreamOdbcConnection; +use PDO; use ReflectionMethod; use Tests\TestCase; @@ -41,4 +42,33 @@ class UpstreamOdbcConnectionTest extends TestCase $this->assertFalse($jiansun); $this->assertFalse($method->invoke(null, 'operations_replica', 'FreeTDS', 'strict')); } + + public function test_sql_server_connection_enables_required_ansi_session_options(): void + { + $pdo = new class extends PDO + { + /** @var array */ + public array $statements = []; + + public function __construct() + { + } + + public function exec(string $statement): int|false + { + $this->statements[] = $statement; + + return 0; + } + }; + $method = new ReflectionMethod(UpstreamOdbcConnection::class, 'initializeSqlServerSession'); + + $result = $method->invoke(null, $pdo); + + $this->assertSame($pdo, $result); + $this->assertSame([ + 'SET ANSI_NULLS ON', + 'SET ANSI_WARNINGS ON', + ], $pdo->statements); + } }