Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug: Query binding fails when date_format is set with '%H:%i:%s'
**Describe the bug**
Query builder is binding incorrectly the statement with date_format (eg. %d/%m/%Y %H:%i:%s) - 22/09/2021 10:45:00 and crash.

```php
<?php

$id = 1;
//Works
$sql = " SELECT mytable.id, DATE_FORMAT(mytable.created_at,'%d/%m/%Y %H:%i') AS created_at_uk FROM mytable WHERE mytable.id = ?";
$query = $this->db->query($sql, [$id]);

//DOES NOT work
$sql = " SELECT mytable.id, DATE_FORMAT(mytable.created_at,'%d/%m/%Y %H:%i:%s') AS created_at_uk FROM mytable WHERE mytable.id = ?";
$query = $this->db->query($sql, [$id]);

```
As you can see, the only difference between both SQL is the `:%s`. This should not be binding as the (%s) means seconds on date_format.

**CodeIgniter 4 version**
CI 4.1.3

**Affected module(s)**
Not sure but looks like is Query.php

**Expected behavior, and steps to reproduce if appropriate**
Should return the query with day/month/year hour:minute:second

**Context**
 - OS: Mac
 - Web server Apache
 - PHP version 8.0



Discussion:
`:%i:` matches the following regexp:
https://github.com/codeigniter4/CodeIgniter4/blob/fd824042df6be1375d6ea8ae5919a3d61c109b4b/system/Database/Query.php#L278

See https://regex101.com/r/lSp7qq/2


Ref #4769, #4444, #4281

Refs #5138.



Patch:
diff --git a/system/Database/Query.php b/system/Database/Query.php
index 7f0873fdc40f..441ac0a82fc2 100644
--- a/system/Database/Query.php
+++ b/system/Database/Query.php
@@ -269,17 +269,19 @@ public function getOriginalQuery(): string
     /**
      * Escapes and inserts any binds into the finalQueryString object.
      *
-     * @see https://regex101.com/r/EUEhay/4
+     * @see https://regex101.com/r/EUEhay/5
      */
     protected function compileBinds()
     {
         $sql = $this->finalQueryString;
 
-        $hasNamedBinds = preg_match('/:((?!=).+):/', $sql) === 1;
+        $hasBinds      = strpos($sql, $this->bindMarker) !== false;
+        $hasNamedBinds = ! $hasBinds
+            && preg_match('/:(?!=).+:/', $sql) === 1;
 
         if (empty($this->binds)
             || empty($this->bindMarker)
-            || (! $hasNamedBinds && strpos($sql, $this->bindMarker) === false)
+            || (! $hasNamedBinds && ! $hasBinds)
         ) {
             return;
         }


Test patch:
diff --git a/tests/system/Database/BaseQueryTest.php b/tests/system/Database/BaseQueryTest.php
index f53beadcd8e9..c9f905bc136d 100644
--- a/tests/system/Database/BaseQueryTest.php
+++ b/tests/system/Database/BaseQueryTest.php
@@ -238,6 +238,23 @@ public function testBindingAutoEscapesParameters()
         $this->assertSame($expected, $query->getQuery());
     }
 
+    /**
+     * @see https://github.com/codeigniter4/CodeIgniter4/issues/5114
+     */
+    public function testBindingWithTwoColons()
+    {
+        $query = new Query($this->db);
+
+        $query->setQuery(
+            "SELECT mytable.id, DATE_FORMAT(mytable.created_at,'%d/%m/%Y %H:%i:%s') AS created_at_uk FROM mytable WHERE mytable.id = ?",
+            [1]
+        );
+
+        $expected = "SELECT mytable.id, DATE_FORMAT(mytable.created_at,'%d/%m/%Y %H:%i:%s') AS created_at_uk FROM mytable WHERE mytable.id = 1";
+
+        $this->assertSame($expected, $query->getQuery());
+    }
+
     public function testNamedBinds()
     {
         $query = new Query($this->db);