@@ -4703,4 +4703,233 @@ public function testAlterTableDuplicateKeyNameWithUnique(): void {
4703
4703
$ this ->assertSame ( "SQLSTATE[42000]: Syntax error or access violation: 1061 Duplicate key name 'idx' " , $ exception ->getMessage () );
4704
4704
$ this ->assertSame ( '42S21 ' , $ exception ->getCode () );
4705
4705
}
4706
+
4707
+ public function testNoBackslashEscapesSqlMode (): void {
4708
+ $ backslash = chr ( 92 );
4709
+
4710
+ $ query = "SELECT
4711
+ '''' AS value_1,
4712
+ ' {$ backslash }\"' AS value_2,
4713
+ ' {$ backslash }0' AS value_3,
4714
+ ' {$ backslash }n' AS value_4,
4715
+ ' {$ backslash }r' AS value_5,
4716
+ ' {$ backslash }t' AS value_6,
4717
+ ' {$ backslash }b' AS value_7,
4718
+ ' {$ backslash }{$ backslash }' AS value_8,
4719
+ '🙂' AS value_9,
4720
+ ' {$ backslash }🙂' AS value_10,
4721
+ ' {$ backslash }%' AS value_11,
4722
+ ' {$ backslash }_' AS value_12
4723
+ " ;
4724
+
4725
+ // With NO_BACKSLASH_ESCAPES disabled:
4726
+ $ this ->assertQuery ( "SET SESSION sql_mode = '' " );
4727
+ $ result = $ this ->assertQuery ( $ query );
4728
+ $ this ->assertSame ( chr ( 39 ), $ result [0 ]->value_1 ); // single quote
4729
+ $ this ->assertSame ( chr ( 34 ), $ result [0 ]->value_2 ); // double quote
4730
+ $ this ->assertSame ( chr ( 0 ), $ result [0 ]->value_3 ); // ASCII NULL
4731
+ $ this ->assertSame ( chr ( 10 ), $ result [0 ]->value_4 ); // newline
4732
+ $ this ->assertSame ( chr ( 13 ), $ result [0 ]->value_5 ); // carriage return
4733
+ $ this ->assertSame ( chr ( 9 ), $ result [0 ]->value_6 ); // tab
4734
+ $ this ->assertSame ( chr ( 8 ), $ result [0 ]->value_7 ); // backspace
4735
+ $ this ->assertSame ( chr ( 92 ), $ result [0 ]->value_8 ); // backslash
4736
+ $ this ->assertSame ( '🙂 ' , $ result [0 ]->value_9 );
4737
+ $ this ->assertSame ( '🙂 ' , $ result [0 ]->value_10 );
4738
+
4739
+ // Characters "%" and "_" follow special escaping rules. Escape sequences
4740
+ // "\%" and "\_" preserve the backslash so it can be used in some contexts.
4741
+ $ this ->assertSame ( $ backslash . '% ' , $ result [0 ]->value_11 );
4742
+ $ this ->assertSame ( $ backslash . '_ ' , $ result [0 ]->value_12 );
4743
+
4744
+ // With NO_BACKSLASH_ESCAPES enabled:
4745
+ $ this ->assertQuery ( "SET SESSION sql_mode = 'NO_BACKSLASH_ESCAPES' " );
4746
+ $ result = $ this ->assertQuery ( $ query );
4747
+ $ this ->assertSame ( "' " , $ result [0 ]->value_1 );
4748
+ $ this ->assertSame ( $ backslash . '" ' , $ result [0 ]->value_2 );
4749
+ $ this ->assertSame ( $ backslash . '0 ' , $ result [0 ]->value_3 );
4750
+ $ this ->assertSame ( $ backslash . 'n ' , $ result [0 ]->value_4 );
4751
+ $ this ->assertSame ( $ backslash . 'r ' , $ result [0 ]->value_5 );
4752
+ $ this ->assertSame ( $ backslash . 't ' , $ result [0 ]->value_6 );
4753
+ $ this ->assertSame ( $ backslash . 'b ' , $ result [0 ]->value_7 );
4754
+ $ this ->assertSame ( $ backslash . $ backslash , $ result [0 ]->value_8 );
4755
+ $ this ->assertSame ( '🙂 ' , $ result [0 ]->value_9 );
4756
+ $ this ->assertSame ( $ backslash . '🙂 ' , $ result [0 ]->value_10 );
4757
+ $ this ->assertSame ( $ backslash . '% ' , $ result [0 ]->value_11 );
4758
+ $ this ->assertSame ( $ backslash . '_ ' , $ result [0 ]->value_12 );
4759
+ }
4760
+
4761
+ public function testNoBackslashEscapesSqlModeWithPatternMatching (): void {
4762
+ $ backslash = chr ( 92 );
4763
+
4764
+ $ this ->assertQuery ( 'CREATE TABLE t (id INT PRIMARY KEY AUTO_INCREMENT, value TEXT) ' );
4765
+ $ this ->assertQuery ( "INSERT INTO t (value) VALUES ('abc') " );
4766
+ $ this ->assertQuery ( "INSERT INTO t (value) VALUES ('abc_') " );
4767
+ $ this ->assertQuery ( "INSERT INTO t (value) VALUES ('abc%') " );
4768
+ $ this ->assertQuery ( "INSERT INTO t (value) VALUES ('abc {$ backslash }{$ backslash }x') " ); // abc\x
4769
+
4770
+ /*
4771
+ * 1. With NO_BACKSLASH_ESCAPES disabled:
4772
+ *
4773
+ * Backslashes serve as special escape characters on two levels:
4774
+ *
4775
+ * 1. In MySQL string literals.
4776
+ * 2. In LIKE patterns.
4777
+ *
4778
+ * Additionally, "\_" and "\%" sequences preserve the backslash in MySQL
4779
+ * string literals, making them equivalent to "\\_" and "\\%" sequences.
4780
+ *
4781
+ * Here's what that does to some escape sequences:
4782
+ *
4783
+ * "\_"
4784
+ * 1) String literal resolves to: "\_" sequence
4785
+ * 2) Pattern matching resolves to: "_" character
4786
+ *
4787
+ * "\\_"
4788
+ * 1) String literal resolves to: "\_" sequence
4789
+ * 2) Pattern matching resolves to: "_" character
4790
+ *
4791
+ * "\\\_"
4792
+ * 1) String literal resolves to: "\\_" sequence
4793
+ * 2) Pattern matching resolves to: "\" character + "_" wildcard
4794
+ *
4795
+ * "\\\\_"
4796
+ * 1) String literal resolves to: "\\_" sequence
4797
+ * 2) Pattern matching resolves to: "\" character + "_" wildcard
4798
+ *
4799
+ * The same rules applies to the "%" character.
4800
+ */
4801
+ $ this ->assertQuery ( "SET SESSION sql_mode = '' " );
4802
+
4803
+ // A "_" = a wildcard:
4804
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc_' ORDER BY id " );
4805
+ $ this ->assertCount ( 2 , $ result );
4806
+ $ this ->assertSame ( 'abc_ ' , $ result [0 ]->value );
4807
+ $ this ->assertSame ( 'abc% ' , $ result [1 ]->value );
4808
+
4809
+ // A "\_" sequence = the "_" character:
4810
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }_' " );
4811
+ $ this ->assertCount ( 1 , $ result );
4812
+ $ this ->assertSame ( 'abc_ ' , $ result [0 ]->value );
4813
+
4814
+ // A "\\_" sequence = the "_" character:
4815
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }_' " );
4816
+ $ this ->assertCount ( 1 , $ result );
4817
+ $ this ->assertSame ( 'abc_ ' , $ result [0 ]->value );
4818
+
4819
+ // A "\\\_" sequence = the "\" character and a wildcard:
4820
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }_' " );
4821
+ $ this ->assertCount ( 1 , $ result );
4822
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4823
+
4824
+ // A "\\\\_" sequence = the "\" character and a wildcard:
4825
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }{$ backslash }_' " );
4826
+ $ this ->assertCount ( 1 , $ result );
4827
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4828
+
4829
+ // A "\\\\\_" sequence = the "\" character and the "_" character:
4830
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }{$ backslash }{$ backslash }_' " );
4831
+ $ this ->assertCount ( 0 , $ result );
4832
+
4833
+ // A "%" = a wildcard:
4834
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc%' ORDER BY id " );
4835
+ $ this ->assertCount ( 4 , $ result );
4836
+ $ this ->assertSame ( 'abc ' , $ result [0 ]->value );
4837
+ $ this ->assertSame ( 'abc_ ' , $ result [1 ]->value );
4838
+ $ this ->assertSame ( 'abc% ' , $ result [2 ]->value );
4839
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [3 ]->value );
4840
+
4841
+ // A "\%" sequence = the "%" character:
4842
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }%' " );
4843
+ $ this ->assertCount ( 1 , $ result );
4844
+ $ this ->assertSame ( 'abc% ' , $ result [0 ]->value );
4845
+
4846
+ // A "\\%" sequence = the "%" character:
4847
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }%' " );
4848
+ $ this ->assertCount ( 1 , $ result );
4849
+ $ this ->assertSame ( 'abc% ' , $ result [0 ]->value );
4850
+
4851
+ // A "\\\%" sequence = the "\" character and a wildcard:
4852
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }%' " );
4853
+ $ this ->assertCount ( 1 , $ result );
4854
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4855
+
4856
+ // A "\\\\%" sequence = the "\" character and a wildcard:
4857
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }{$ backslash }%' " );
4858
+ $ this ->assertCount ( 1 , $ result );
4859
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4860
+
4861
+ // A "\\\\\%" sequence = the "\" character and the "%" character:
4862
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }{$ backslash }{$ backslash }%' " );
4863
+ $ this ->assertCount ( 0 , $ result );
4864
+
4865
+ // A "\x" sequence = the "x" character:
4866
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }x' " );
4867
+ $ this ->assertCount ( 0 , $ result );
4868
+
4869
+ // A "\\x" sequence = the "x" character:
4870
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }x' " );
4871
+ $ this ->assertCount ( 0 , $ result );
4872
+
4873
+ // A "\\\x" sequence = the "x" character:
4874
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }x' " );
4875
+ $ this ->assertCount ( 0 , $ result );
4876
+
4877
+ // A "\\\\x" sequence = the "\" character and the "x" character:
4878
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }{$ backslash }{$ backslash }x' " );
4879
+ $ this ->assertCount ( 1 , $ result );
4880
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4881
+
4882
+ /*
4883
+ * 2. With NO_BACKSLASH_ESCAPES enabled:
4884
+ *
4885
+ * Backslashes don't serve as special escape characters at all:
4886
+ *
4887
+ * 1. No special meaning in MySQL string literals.
4888
+ * 2. No special meaning in LIKE patterns.
4889
+ * This can be overriden using the "ESCAPE ..." clause of the LIKE
4890
+ * expression. This is not implemented in the SQLite driver yet.
4891
+ */
4892
+ $ this ->assertQuery ( "SET SESSION sql_mode = 'NO_BACKSLASH_ESCAPES' " );
4893
+
4894
+ // A "_" = a wildcard:
4895
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc_' ORDER BY id " );
4896
+ $ this ->assertCount ( 2 , $ result );
4897
+ $ this ->assertSame ( 'abc_ ' , $ result [0 ]->value );
4898
+ $ this ->assertSame ( 'abc% ' , $ result [1 ]->value );
4899
+
4900
+ // A "\_" sequence = the "\" character and a wildcard:
4901
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }_' " );
4902
+ $ this ->assertCount ( 1 , $ result );
4903
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4904
+
4905
+ // A "\\_" sequence = two "\" characters and a wildcard:
4906
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }_' " );
4907
+ $ this ->assertCount ( 0 , $ result );
4908
+
4909
+ // A "%" = a wildcard:
4910
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc%' ORDER BY id " );
4911
+ $ this ->assertCount ( 4 , $ result );
4912
+ $ this ->assertSame ( 'abc ' , $ result [0 ]->value );
4913
+ $ this ->assertSame ( 'abc_ ' , $ result [1 ]->value );
4914
+ $ this ->assertSame ( 'abc% ' , $ result [2 ]->value );
4915
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [3 ]->value );
4916
+
4917
+ // A "\%" sequence = the "\" character and a wildcard.
4918
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }%' " );
4919
+ $ this ->assertCount ( 1 , $ result );
4920
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4921
+
4922
+ // A "\\%" sequence = two "\" characters and a wildcard.
4923
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }%' " );
4924
+ $ this ->assertCount ( 0 , $ result );
4925
+
4926
+ // A "\x" sequence = the "\" and the "x" character.
4927
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }x' " );
4928
+ $ this ->assertCount ( 1 , $ result );
4929
+ $ this ->assertSame ( "abc {$ backslash }x " , $ result [0 ]->value );
4930
+
4931
+ // A "\\x" sequence = two "\" characters and the "x" character.
4932
+ $ result = $ this ->assertQuery ( "SELECT value FROM t WHERE value LIKE 'abc {$ backslash }{$ backslash }x' " );
4933
+ $ this ->assertCount ( 0 , $ result );
4934
+ }
4706
4935
}
0 commit comments