99/**
1010 * To support some features these commits from laravel needed to be backported for older versions:
1111 * - [10.x] Escaping functionality within the Grammar (https://github.com/laravel/framework/commit/e953137280cdf6e0fe3c3e4c49d7209ad86c92c0).
12+ * - [10.x] Add toRawSql, dumpRawSql() and ddRawSql() to Query Builders (https://github.com/laravel/framework/commit/830efbeeb815a5f1558433b673a58b0ddcbdc750).
13+ * - [11.x] Allow for custom Postgres operators to be added (https://github.com/laravel/framework/commit/029e993cb976e76a8d34d6b175eed8c8af1c3ed8)
1214 */
1315trait GrammarBackport
1416{
@@ -18,6 +20,22 @@ trait GrammarBackport
1820 * @var \Illuminate\Database\Connection
1921 */
2022 protected $ connection ;
23+ /**
24+ * The Postgres grammar specific custom operators.
25+ *
26+ * @var array
27+ */
28+ protected static $ customOperators = [];
29+
30+ /**
31+ * Set any Postgres grammar specific custom operators.
32+ */
33+ public static function customOperators (array $ operators ): void
34+ {
35+ static ::$ customOperators = array_values (
36+ array_merge (static ::$ customOperators , array_filter (array_filter ($ operators , 'is_string ' )))
37+ );
38+ }
2139
2240 /**
2341 * Escapes a value for safe SQL embedding.
@@ -34,6 +52,14 @@ public function escape($value, $binary = false): string
3452 return $ this ->connection ->escape ($ value , $ binary );
3553 }
3654
55+ /**
56+ * Get the Postgres grammar specific operators.
57+ */
58+ public function getOperators (): array
59+ {
60+ return array_values (array_unique (array_merge ($ this ->operators , static ::$ customOperators )));
61+ }
62+
3763 /**
3864 * Set the grammar's database connection.
3965 *
@@ -45,4 +71,49 @@ public function setConnection($connection): static
4571
4672 return $ this ;
4773 }
74+
75+ /**
76+ * Substitute the given bindings into the given raw SQL query.
77+ *
78+ * @param string $sql
79+ * @param array $bindings
80+ */
81+ public function substituteBindingsIntoRawSql ($ sql , $ bindings ): string
82+ {
83+ $ bindings = array_map (fn ($ value ) => $ this ->escape ($ value ), $ bindings );
84+
85+ $ query = '' ;
86+
87+ $ isStringLiteral = false ;
88+
89+ for ($ i = 0 ; $ i < \strlen ($ sql ); ++$ i ) {
90+ $ char = $ sql [$ i ];
91+ $ nextChar = $ sql [$ i + 1 ] ?? null ;
92+
93+ // Single quotes can be escaped as '' according to the SQL standard while
94+ // MySQL uses \'. Postgres has operators like ?| that must get encoded
95+ // in PHP like ??|. We should skip over the escaped characters here.
96+ if (\in_array ($ char .$ nextChar , ["\' " , "'' " , '?? ' ])) {
97+ $ query .= $ char .$ nextChar ;
98+ ++$ i ;
99+ } elseif ("' " === $ char ) { // Starting / leaving string literal...
100+ $ query .= $ char ;
101+ $ isStringLiteral = !$ isStringLiteral ;
102+ } elseif ('? ' === $ char && !$ isStringLiteral ) { // Substitutable binding...
103+ $ query .= array_shift ($ bindings ) ?? '? ' ;
104+ } else { // Normal character...
105+ $ query .= $ char ;
106+ }
107+ }
108+
109+ foreach ($ this ->getOperators () as $ operator ) {
110+ if (!str_contains ($ operator , '? ' )) {
111+ continue ;
112+ }
113+
114+ $ query = str_replace (str_replace ('? ' , '?? ' , $ operator ), $ operator , $ query );
115+ }
116+
117+ return $ query ;
118+ }
48119}
0 commit comments