Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug: Query builder escapes negative integers
**Describe the bug**
In #606, a bit of code was added that escapes negative values. However, I think negative values should not be escaped at all. For example, I would like to look back at negative dates using something like

```sql
WHERE date_pickup < DateAdd(month, ?, Convert(date, GetDate())
```

Due the conversion, this is translated to


```sql
WHERE date_pickup < DateAdd(month, '-6', Convert(date, GetDate())
```
Which results in an error, since we now pass a varchar to the function instead of an integer.
My question is: why are negative values escaped at all? The offending bit of code checks that the integer is negative before escaping it, and I'm not sure what the original commit intends to solve.

**CodeIgniter 4 version**
Tested in 4.1.3, should be present in all versions.

**Affected module(s)**
`Database/BaseConnection.php` lines 1312 - 1314 in the escape function. Specifically when using SQLSRV driver. Though it should be independent of driver used.


**Context**
 - OS: Windows 10
 - Web server IIS
 - PHP version 8.0


Discussion:
I sent a PR to fix this.  #5277



Patch:
diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php
index ebb63b7f5827..44654e4423a0 100644
--- a/system/Database/BaseConnection.php
+++ b/system/Database/BaseConnection.php
@@ -565,7 +565,7 @@ abstract protected function execute(string $sql);
      *
      * @param mixed ...$binds
      *
-     * @return BaseResult|bool|Query
+     * @return BaseResult|bool|Query BaseResult when “read” type query, bool when “write” type query, Query when prepared query
      *
      * @todo BC set $queryClass default as null in 4.1
      */
@@ -955,6 +955,8 @@ public function getConnectDuration(int $decimals = 6): string
      * the correct identifiers.
      *
      * @param array|string $item
+     * @param bool         $prefixSingle Prefix an item with no segments?
+     * @param bool         $fieldExists  Supplied $item contains a field name?
      *
      * @return array|string
      */
@@ -1200,10 +1202,6 @@ public function escape($str)
             return ($str === false) ? 0 : 1;
         }
 
-        if (is_numeric($str) && $str < 0) {
-            return "'{$str}'";
-        }
-
         return $str ?? 'NULL';
     }
 


Test patch:
diff --git a/tests/system/Database/BaseQueryTest.php b/tests/system/Database/BaseQueryTest.php
index fe942ffeb187..d7fe4bca2819 100644
--- a/tests/system/Database/BaseQueryTest.php
+++ b/tests/system/Database/BaseQueryTest.php
@@ -345,6 +345,38 @@ public function testSetQueryBindsWithSetEscapeFalse()
         $this->assertSame($expected, $query->getQuery());
     }
 
+    /**
+     * @see https://github.com/codeigniter4/CodeIgniter4/issues/4973
+     */
+    public function testSetQueryBindsWithSetEscapeNegativeIntegers()
+    {
+        $query = new Query($this->db);
+
+        $query->setQuery(
+            'SELECT * FROM product WHERE date_pickup < DateAdd(month, ?, Convert(date, GetDate())',
+            [-6],
+            true
+        );
+
+        $expected = 'SELECT * FROM product WHERE date_pickup < DateAdd(month, -6, Convert(date, GetDate())';
+
+        $this->assertSame($expected, $query->getQuery());
+    }
+
+    public function testSetQueryNamedBindsWithNegativeIntegers()
+    {
+        $query = new Query($this->db);
+
+        $query->setQuery(
+            'SELECT * FROM product WHERE date_pickup < DateAdd(month, :num:, Convert(date, GetDate())',
+            ['num' => -6]
+        );
+
+        $expected = 'SELECT * FROM product WHERE date_pickup < DateAdd(month, -6, Convert(date, GetDate())';
+
+        $this->assertSame($expected, $query->getQuery());
+    }
+
     /**
      * @see https://github.com/codeigniter4/CodeIgniter4/issues/2762
      */
diff --git a/tests/system/Database/Live/EscapeTest.php b/tests/system/Database/Live/EscapeTest.php
index 08265788d00e..1260badcee77 100644
--- a/tests/system/Database/Live/EscapeTest.php
+++ b/tests/system/Database/Live/EscapeTest.php
@@ -38,9 +38,9 @@ protected function setUp(): void
      *
      * @see https://github.com/codeigniter4/CodeIgniter4/issues/606
      */
-    public function testEscapeProtectsNegativeNumbers()
+    public function testDoesNotEscapeNegativeNumbers()
     {
-        $this->assertSame("'-100'", $this->db->escape(-100));
+        $this->assertSame(-100, $this->db->escape(-100));
     }
 
     public function testEscape()