@@ -2370,6 +2370,197 @@ public function testTruncatesInvalidDates() {
23702370 $ this ->assertEquals ( '0000-00-00 00:00:00 ' , $ results [1 ]->option_value );
23712371 }
23722372
2373+ /**
2374+ * Test NO_ZERO_DATE SQL mode behavior.
2375+ *
2376+ * MySQL reference (https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html#sqlmode_no_zero_date):
2377+ *
2378+ * "The NO_ZERO_DATE mode affects whether the server permits '0000-00-00' as a valid date.
2379+ * Its effect also depends on whether strict SQL mode is enabled.
2380+ * - If this mode is not enabled, '0000-00-00' is permitted and inserts produce no warning.
2381+ * - If this mode is enabled, '0000-00-00' is permitted but produces a warning.
2382+ * - If this mode and strict mode are both enabled, '0000-00-00' is not permitted
2383+ * and inserts produce an error, unless IGNORE is also given."
2384+ */
2385+ public function testZeroDateAcceptedWhenNoZeroDateModeIsOff () {
2386+ // With NO_ZERO_DATE disabled, '0000-00-00 00:00:00' should be accepted.
2387+ $ this ->assertQuery ( "SET sql_mode = 'STRICT_TRANS_TABLES' " );
2388+
2389+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('0000-00-00 00:00:00'); " );
2390+
2391+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2392+ $ results = $ this ->engine ->get_query_results ();
2393+ $ this ->assertCount ( 1 , $ results );
2394+ $ this ->assertEquals ( '0000-00-00 00:00:00 ' , $ results [0 ]->option_value );
2395+ }
2396+
2397+ /**
2398+ * Test that zero dates are rejected in strict mode when NO_ZERO_DATE is active.
2399+ *
2400+ * MySQL reference (https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html#sqlmode_no_zero_date):
2401+ *
2402+ * "If this mode and strict mode are both enabled, '0000-00-00' is not
2403+ * permitted and inserts produce an error."
2404+ */
2405+ public function testZeroDateRejectedWhenNoZeroDateAndStrictModeAreOn () {
2406+ // Default modes include both NO_ZERO_DATE and STRICT_TRANS_TABLES.
2407+ $ this ->assertQueryError (
2408+ "INSERT INTO _dates (option_value) VALUES ('0000-00-00 00:00:00'); " ,
2409+ "Incorrect datetime value: '0000-00-00 00:00:00' "
2410+ );
2411+ }
2412+
2413+ /**
2414+ * Test that zero dates are accepted (with warning) when NO_ZERO_DATE is on
2415+ * but strict mode is off.
2416+ *
2417+ * MySQL reference (https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html#sqlmode_no_zero_date):
2418+ *
2419+ * "If this mode is enabled, '0000-00-00' is permitted but produces a warning."
2420+ */
2421+ public function testZeroDateAcceptedWhenNoZeroDateOnButStrictModeOff () {
2422+ $ this ->assertQuery ( "SET sql_mode = 'NO_ZERO_DATE' " );
2423+
2424+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('0000-00-00 00:00:00'); " );
2425+
2426+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2427+ $ results = $ this ->engine ->get_query_results ();
2428+ $ this ->assertCount ( 1 , $ results );
2429+ $ this ->assertEquals ( '0000-00-00 00:00:00 ' , $ results [0 ]->option_value );
2430+ }
2431+
2432+ /**
2433+ * Test that zero dates work with the DATE column type too.
2434+ */
2435+ public function testZeroDateAcceptedForDateColumn () {
2436+ $ this ->assertQuery ( "SET sql_mode = 'STRICT_TRANS_TABLES' " );
2437+
2438+ $ this ->assertQuery (
2439+ "CREATE TABLE _date_test (
2440+ ID INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
2441+ col_date DATE NOT NULL
2442+ ); "
2443+ );
2444+
2445+ $ this ->assertQuery ( "INSERT INTO _date_test (col_date) VALUES ('0000-00-00'); " );
2446+
2447+ $ this ->assertQuery ( 'SELECT * FROM _date_test; ' );
2448+ $ results = $ this ->engine ->get_query_results ();
2449+ $ this ->assertCount ( 1 , $ results );
2450+ $ this ->assertEquals ( '0000-00-00 ' , $ results [0 ]->col_date );
2451+ }
2452+
2453+ /**
2454+ * Test NO_ZERO_IN_DATE SQL mode behavior.
2455+ *
2456+ * MySQL reference (https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html#sqlmode_no_zero_in_date):
2457+ *
2458+ * "The NO_ZERO_IN_DATE mode affects whether the server permits dates in
2459+ * which the year part is nonzero but the month or day part is 0. (This
2460+ * mode affects dates such as '2010-00-01' or '2010-01-00', but not
2461+ * '0000-00-00'. To control whether the server permits '0000-00-00',
2462+ * use the NO_ZERO_DATE mode.)
2463+ * - If this mode is not enabled, dates with zero parts are permitted
2464+ * and inserts produce no warning.
2465+ * - If this mode is enabled, dates with zero parts are inserted as
2466+ * '0000-00-00' and produce a warning.
2467+ * - If this mode and strict mode are both enabled, dates with zero parts
2468+ * are not permitted and inserts produce an error."
2469+ */
2470+ public function testZeroInDateAcceptedWhenNoZeroInDateModeIsOff () {
2471+ // Disable NO_ZERO_IN_DATE but keep strict mode on.
2472+ $ this ->assertQuery ( "SET sql_mode = 'STRICT_TRANS_TABLES' " );
2473+
2474+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2020-00-15 00:00:00'); " );
2475+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2020-01-00 00:00:00'); " );
2476+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2020-00-00 00:00:00'); " );
2477+
2478+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2479+ $ results = $ this ->engine ->get_query_results ();
2480+ $ this ->assertCount ( 3 , $ results );
2481+ $ this ->assertEquals ( '2020-00-15 00:00:00 ' , $ results [0 ]->option_value );
2482+ $ this ->assertEquals ( '2020-01-00 00:00:00 ' , $ results [1 ]->option_value );
2483+ $ this ->assertEquals ( '2020-00-00 00:00:00 ' , $ results [2 ]->option_value );
2484+ }
2485+
2486+ /**
2487+ * Test that dates with zero parts are rejected in strict mode when
2488+ * NO_ZERO_IN_DATE is active.
2489+ */
2490+ public function testZeroInDateRejectedWhenNoZeroInDateAndStrictModeAreOn () {
2491+ // Default modes include both NO_ZERO_IN_DATE and STRICT_TRANS_TABLES.
2492+ $ this ->assertQueryError (
2493+ "INSERT INTO _dates (option_value) VALUES ('2020-00-15 00:00:00'); " ,
2494+ "Incorrect datetime value: '2020-00-15 00:00:00' "
2495+ );
2496+ }
2497+
2498+ /**
2499+ * Test that dates with zero parts get stored as '0000-00-00 00:00:00'
2500+ * when NO_ZERO_IN_DATE is on but strict mode is off.
2501+ *
2502+ * MySQL reference (https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html#sqlmode_no_zero_in_date):
2503+ *
2504+ * "If this mode is enabled, dates with zero parts are inserted as
2505+ * '0000-00-00' and produce a warning."
2506+ */
2507+ public function testZeroInDateBecomesZeroDateWhenNoZeroInDateOnButStrictOff () {
2508+ $ this ->assertQuery ( "SET sql_mode = 'NO_ZERO_IN_DATE' " );
2509+
2510+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2020-00-15 00:00:00'); " );
2511+
2512+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2513+ $ results = $ this ->engine ->get_query_results ();
2514+ $ this ->assertCount ( 1 , $ results );
2515+ $ this ->assertEquals ( '0000-00-00 00:00:00 ' , $ results [0 ]->option_value );
2516+ }
2517+
2518+ /**
2519+ * Test that all modes disabled allows both zero dates and zero-in-dates.
2520+ */
2521+ public function testBothZeroDateModesDisabledAcceptsAll () {
2522+ $ this ->assertQuery ( "SET sql_mode = '' " );
2523+
2524+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('0000-00-00 00:00:00'); " );
2525+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2020-00-15 00:00:00'); " );
2526+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2020-01-00 00:00:00'); " );
2527+
2528+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2529+ $ results = $ this ->engine ->get_query_results ();
2530+ $ this ->assertCount ( 3 , $ results );
2531+ $ this ->assertEquals ( '0000-00-00 00:00:00 ' , $ results [0 ]->option_value );
2532+ $ this ->assertEquals ( '2020-00-15 00:00:00 ' , $ results [1 ]->option_value );
2533+ $ this ->assertEquals ( '2020-01-00 00:00:00 ' , $ results [2 ]->option_value );
2534+ }
2535+
2536+ /**
2537+ * Test that valid dates still work correctly regardless of zero date modes.
2538+ */
2539+ public function testValidDatesWorkWithZeroDateModes () {
2540+ // Default modes (NO_ZERO_DATE + STRICT_TRANS_TABLES).
2541+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2022-01-15 14:30:00'); " );
2542+
2543+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2544+ $ results = $ this ->engine ->get_query_results ();
2545+ $ this ->assertCount ( 1 , $ results );
2546+ $ this ->assertEquals ( '2022-01-15 14:30:00 ' , $ results [0 ]->option_value );
2547+ }
2548+
2549+ /**
2550+ * Test zero date handling in UPDATE statements.
2551+ */
2552+ public function testZeroDateInUpdate () {
2553+ $ this ->assertQuery ( "SET sql_mode = 'STRICT_TRANS_TABLES' " );
2554+
2555+ $ this ->assertQuery ( "INSERT INTO _dates (option_value) VALUES ('2022-01-15 14:30:00'); " );
2556+ $ this ->assertQuery ( "UPDATE _dates SET option_value = '0000-00-00 00:00:00'; " );
2557+
2558+ $ this ->assertQuery ( 'SELECT * FROM _dates; ' );
2559+ $ results = $ this ->engine ->get_query_results ();
2560+ $ this ->assertCount ( 1 , $ results );
2561+ $ this ->assertEquals ( '0000-00-00 00:00:00 ' , $ results [0 ]->option_value );
2562+ }
2563+
23732564 public function testCaseInsensitiveSelect () {
23742565 $ this ->assertQuery (
23752566 "CREATE TABLE _tmp_table (
0 commit comments