diff --git a/packages/php-db-import-export/src/Backend/Snowflake/ToFinalTable/SqlBuilder.php b/packages/php-db-import-export/src/Backend/Snowflake/ToFinalTable/SqlBuilder.php index b29fb7c5f..eea4bd086 100644 --- a/packages/php-db-import-export/src/Backend/Snowflake/ToFinalTable/SqlBuilder.php +++ b/packages/php-db-import-export/src/Backend/Snowflake/ToFinalTable/SqlBuilder.php @@ -377,6 +377,7 @@ static function ($columnName) { }, $stagingTableDefinition->getColumnsNames(), ); + break; } $dest = sprintf( diff --git a/packages/php-db-import-export/tests/functional/Snowflake/ToFinal/SqlBuilderTest.php b/packages/php-db-import-export/tests/functional/Snowflake/ToFinal/SqlBuilderTest.php index eb5a53165..08f117487 100644 --- a/packages/php-db-import-export/tests/functional/Snowflake/ToFinal/SqlBuilderTest.php +++ b/packages/php-db-import-export/tests/functional/Snowflake/ToFinal/SqlBuilderTest.php @@ -1621,6 +1621,159 @@ public function testGetUpdateWithPkCommandNullManipulationWithTimestampFeatures( ], $result); } + + public function nullManipulationWithTimestampFeaturesTableWithoutTypes(): Generator + { + yield 'default' => [ + 'default', + // phpcs:ignore + 'UPDATE "import_export_test_schema"."import_export_test_test" AS "dest" SET "col1" = COALESCE("src"."col1", \'\'), "col2" = COALESCE("src"."col2", \'\'), "_timestamp" = \'2020-01-01 01:01:01\' FROM "import_export_test_schema"."__temp_stagingTable" AS "src" WHERE "dest"."col1" = COALESCE("src"."col1", \'\') AND (COALESCE(TO_VARCHAR("dest"."col1"), \'\') != COALESCE("src"."col1", \'\') OR COALESCE(TO_VARCHAR("dest"."col2"), \'\') != COALESCE("src"."col2", \'\'))', + false, + ]; + yield 'new-native-types' => [ + 'new-native-types', + // phpcs:ignore + 'UPDATE "import_export_test_schema"."import_export_test_test" AS "dest" SET "col1" = COALESCE("src"."col1", \'\'), "col2" = COALESCE("src"."col2", \'\'), "_timestamp" = \'2020-01-01 01:01:01\' FROM "import_export_test_schema"."__temp_stagingTable" AS "src" WHERE "dest"."col1" = COALESCE("src"."col1", \'\') AND (COALESCE(TO_VARCHAR("dest"."col1"), \'\') != COALESCE("src"."col1", \'\') OR COALESCE(TO_VARCHAR("dest"."col2"), \'\') != COALESCE("src"."col2", \'\'))', + false, + ]; + yield 'native-types_timestamp-bc' => [ + 'native-types_timestamp-bc', + // phpcs:ignore + 'UPDATE "import_export_test_schema"."import_export_test_test" AS "dest" SET "col1" = COALESCE("src"."col1", \'\'), "col2" = COALESCE("src"."col2", \'\'), "_timestamp" = \'2020-01-01 01:01:01\' FROM "import_export_test_schema"."__temp_stagingTable" AS "src" WHERE "dest"."col1" = COALESCE("src"."col1", \'\') AND (COALESCE(TO_VARCHAR("dest"."col1"), \'\') != COALESCE("src"."col1", \'\') OR COALESCE(TO_VARCHAR("dest"."col2"), \'\') != COALESCE("src"."col2", \'\'))', + false, + ]; + } + + /** + * @dataProvider nullManipulationWithTimestampFeaturesTableWithoutTypes + */ + public function testGetUpdateWithPkCommandNullManipulationWithTimestampFeaturesTableWithoutTypes( + string $feature, + string $expectedSQL, + bool $expectedTimestampChange, + ): void { + $timestampInit = new DateTime('2020-01-01 00:00:01'); + $timestampSet = new DateTime('2020-01-01 01:01:01'); + $this->createTestSchema(); + $this->createTestTableWithColumns(true); + $this->createStagingTableWithData(true); + $this->connection->executeStatement( + sprintf( + 'INSERT INTO %s.%s("pk1","pk2","col1","col2") VALUES (3,3,\'\',NULL)', + self::TEST_SCHEMA_QUOTED, + self::TEST_STAGING_TABLE_QUOTED, + ), + ); + + // create fake destination and say that there is pk on col1 + $fakeDestination = new SnowflakeTableDefinition( + self::TEST_SCHEMA, + self::TEST_TABLE, + true, + new ColumnCollection([ + $this->createNullableGenericColumn('col1'), + $this->createNullableGenericColumn('col2'), + ]), + ['col1'], + ); + // create fake stage and say that there is less columns + $fakeStage = new SnowflakeTableDefinition( + self::TEST_SCHEMA, + self::TEST_STAGING_TABLE, + true, + new ColumnCollection([ + $this->createNullableGenericColumn('col1'), + $this->createNullableGenericColumn('col2'), + ]), + [], + ); + + $this->connection->executeStatement( + sprintf( + 'INSERT INTO %s("id","col1","col2","_timestamp") VALUES (1,\'1\',\'1\',\'%s\')', + self::TEST_TABLE_IN_SCHEMA, + $timestampInit->format(DateTimeHelper::FORMAT), + ), + ); + $this->connection->executeStatement( + sprintf( + 'INSERT INTO %s("id","col1","col2","_timestamp") VALUES (3,\'3\',NULL,\'%s\')', + self::TEST_TABLE_IN_SCHEMA, + $timestampInit->format(DateTimeHelper::FORMAT), + ), + ); + + $result = $this->connection->fetchAllAssociative(sprintf( + 'SELECT * FROM %s', + self::TEST_TABLE_IN_SCHEMA, + )); + + self::assertEqualsCanonicalizing([ + [ + 'id' => '1', + 'col1' => '1', + 'col2' => '1', + '_timestamp' => $timestampInit->format(DateTimeHelper::FORMAT), + ], + [ + 'id' => '3', + 'col1' => '3', + 'col2' => null, + '_timestamp' => $timestampInit->format(DateTimeHelper::FORMAT), + ], + ], $result); + + // use timestamp + $options = new SnowflakeImportOptions( + convertEmptyValuesToNull: [], + isIncremental: false, + useTimestamp: true, + numberOfIgnoredLines: 0, + requireSameTables: SnowflakeImportOptions::SAME_TABLES_REQUIRED, + nullManipulation: SnowflakeImportOptions::NULL_MANIPULATION_ENABLED, + ignoreColumns: [ToStageImporterInterface::TIMESTAMP_COLUMN_NAME], + features: [$feature], + ); + $sql = $this->getBuilder()->getUpdateWithPkCommand( + $fakeStage, + $fakeDestination, + $options, + $timestampSet->format(DateTimeHelper::FORMAT), + ); + + self::assertEquals( + $expectedSQL, + $sql, + ); + $this->connection->executeStatement($sql); + + $result = $this->connection->fetchAllAssociative(sprintf( + 'SELECT * FROM %s', + self::TEST_TABLE_IN_SCHEMA, + )); + + $assertTimestamp = $timestampInit; + if ($expectedTimestampChange) { + $assertTimestamp = $timestampSet; + } + + // timestamp was updated to $timestampSet but there is same row as in stage table so no other value is updated + self::assertEqualsCanonicalizing([ + [ + 'id' => '1', + 'col1' => '1', + 'col2' => '1', + '_timestamp' => $assertTimestamp->format(DateTimeHelper::FORMAT), + ], // timestamp is not update when row has same value + [ + 'id' => '3', + 'col1' => '3', + 'col2' => '', + '_timestamp' => $timestampInit->format(DateTimeHelper::FORMAT), + ], // timestamp is not update when row has same value and there is null + ], $result); + } + private function getStagingTableDefinition(): SnowflakeTableDefinition { return new SnowflakeTableDefinition(