Skip to content

Commit 6a9aedf

Browse files
authored
Merge pull request #8748 from kenjis/feat-db-tablename-object-4.6
feat: fix spark db:table causes errors with table name including special chars
2 parents b3d957d + 58197a4 commit 6a9aedf

File tree

14 files changed

+399
-45
lines changed

14 files changed

+399
-45
lines changed

phpstan-baseline.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,12 +2137,6 @@
21372137
'count' => 1,
21382138
'path' => __DIR__ . '/system/Database/BaseConnection.php',
21392139
];
2140-
$ignoreErrors[] = [
2141-
// identifier: missingType.iterableValue
2142-
'message' => '#^Property CodeIgniter\\\\Database\\\\BaseConnection\\:\\:\\$aliasedTables type has no value type specified in iterable type array\\.$#',
2143-
'count' => 1,
2144-
'path' => __DIR__ . '/system/Database/BaseConnection.php',
2145-
];
21462140
$ignoreErrors[] = [
21472141
// identifier: missingType.iterableValue
21482142
'message' => '#^Property CodeIgniter\\\\Database\\\\BaseConnection\\:\\:\\$dataCache type has no value type specified in iterable type array\\.$#',

system/Commands/Database/ShowTableInfo.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use CodeIgniter\CLI\BaseCommand;
1717
use CodeIgniter\CLI\CLI;
1818
use CodeIgniter\Database\BaseConnection;
19+
use CodeIgniter\Database\TableName;
1920
use CodeIgniter\Exceptions\InvalidArgumentException;
2021
use Config\Database;
2122

@@ -199,7 +200,7 @@ private function showDataOfTable(string $tableName, int $limitRows, int $limitFi
199200
CLI::newLine();
200201

201202
$this->removeDBPrefix();
202-
$thead = $this->db->getFieldNames($tableName);
203+
$thead = $this->db->getFieldNames(TableName::fromActualName($this->db, $tableName));
203204
$this->restoreDBPrefix();
204205

205206
// If there is a field named `id`, sort by it.
@@ -277,7 +278,7 @@ private function makeTableRows(
277278
$this->tbody = [];
278279

279280
$this->removeDBPrefix();
280-
$builder = $this->db->table($tableName);
281+
$builder = $this->db->table(TableName::fromActualName($this->db, $tableName));
281282
$builder->limit($limitRows);
282283
if ($sortField !== null) {
283284
$builder->orderBy($sortField, $this->sortDesc ? 'DESC' : 'ASC');

system/Database/BaseBuilder.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ class BaseBuilder
298298
/**
299299
* Constructor
300300
*
301-
* @param array|string $tableName tablename or tablenames with or without aliases
301+
* @param array|string|TableName $tableName tablename or tablenames with or without aliases
302302
*
303303
* Examples of $tableName: `mytable`, `jobs j`, `jobs j, users u`, `['jobs j','users u']`
304304
*
@@ -315,15 +315,20 @@ public function __construct($tableName, ConnectionInterface $db, ?array $options
315315
*/
316316
$this->db = $db;
317317

318+
if ($tableName instanceof TableName) {
319+
$this->tableName = $tableName->getTableName();
320+
$this->QBFrom[] = $this->db->escapeIdentifier($tableName);
321+
$this->db->addTableAlias($tableName->getAlias());
322+
}
318323
// If it contains `,`, it has multiple tables
319-
if (is_string($tableName) && ! str_contains($tableName, ',')) {
324+
elseif (is_string($tableName) && ! str_contains($tableName, ',')) {
320325
$this->tableName = $tableName; // @TODO remove alias if exists
326+
$this->from($tableName);
321327
} else {
322328
$this->tableName = '';
329+
$this->from($tableName);
323330
}
324331

325-
$this->from($tableName);
326-
327332
if ($options !== null && $options !== []) {
328333
foreach ($options as $key => $value) {
329334
if (property_exists($this, $key)) {
@@ -3038,10 +3043,10 @@ protected function trackAliases($table)
30383043
$table = preg_replace('/\s+AS\s+/i', ' ', $table);
30393044

30403045
// Grab the alias
3041-
$table = trim(strrchr($table, ' '));
3046+
$alias = trim(strrchr($table, ' '));
30423047

30433048
// Store the alias, if it doesn't already exist
3044-
$this->db->addTableAlias($table);
3049+
$this->db->addTableAlias($alias);
30453050
}
30463051
}
30473052

system/Database/BaseConnection.php

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ abstract class BaseConnection implements ConnectionInterface
340340
/**
341341
* Array of table aliases.
342342
*
343-
* @var array
343+
* @var list<string>
344344
*/
345345
protected $aliasedTables = [];
346346

@@ -576,10 +576,14 @@ public function setAliasedTables(array $aliases)
576576
*
577577
* @return $this
578578
*/
579-
public function addTableAlias(string $table)
579+
public function addTableAlias(string $alias)
580580
{
581-
if (! in_array($table, $this->aliasedTables, true)) {
582-
$this->aliasedTables[] = $table;
581+
if ($alias === '') {
582+
return $this;
583+
}
584+
585+
if (! in_array($alias, $this->aliasedTables, true)) {
586+
$this->aliasedTables[] = $alias;
583587
}
584588

585589
return $this;
@@ -925,7 +929,7 @@ abstract protected function _transRollback(): bool;
925929
/**
926930
* Returns a non-shared new instance of the query builder for this connection.
927931
*
928-
* @param array|string $tableName
932+
* @param array|string|TableName $tableName
929933
*
930934
* @return BaseBuilder
931935
*
@@ -1054,10 +1058,10 @@ public function getConnectDuration(int $decimals = 6): string
10541058
* insert the table prefix (if it exists) in the proper position, and escape only
10551059
* the correct identifiers.
10561060
*
1057-
* @param array|int|string $item
1058-
* @param bool $prefixSingle Prefix a table name with no segments?
1059-
* @param bool $protectIdentifiers Protect table or column names?
1060-
* @param bool $fieldExists Supplied $item contains a column name?
1061+
* @param array|int|string|TableName $item
1062+
* @param bool $prefixSingle Prefix a table name with no segments?
1063+
* @param bool $protectIdentifiers Protect table or column names?
1064+
* @param bool $fieldExists Supplied $item contains a column name?
10611065
*
10621066
* @return array|string
10631067
* @phpstan-return ($item is array ? array : string)
@@ -1078,6 +1082,11 @@ public function protectIdentifiers($item, bool $prefixSingle = false, ?bool $pro
10781082
return $escapedArray;
10791083
}
10801084

1085+
if ($item instanceof TableName) {
1086+
/** @psalm-suppress NoValue I don't know why ERROR. */
1087+
return $this->escapeTableName($item);
1088+
}
1089+
10811090
// If you pass `['column1', 'column2']`, `$item` will be int because the array keys are int.
10821091
$item = (string) $item;
10831092

@@ -1220,10 +1229,18 @@ private function protectDotItem(string $item, string $alias, bool $protectIdenti
12201229
*
12211230
* This function escapes single identifier.
12221231
*
1223-
* @param non-empty-string $item
1232+
* @param non-empty-string|TableName $item
12241233
*/
1225-
public function escapeIdentifier(string $item): string
1234+
public function escapeIdentifier($item): string
12261235
{
1236+
if ($item === '') {
1237+
return '';
1238+
}
1239+
1240+
if ($item instanceof TableName) {
1241+
return $this->escapeTableName($item);
1242+
}
1243+
12271244
return $this->escapeChar
12281245
. str_replace(
12291246
$this->escapeChar,
@@ -1233,6 +1250,17 @@ public function escapeIdentifier(string $item): string
12331250
. $this->escapeChar;
12341251
}
12351252

1253+
/**
1254+
* Returns escaped table name with alias.
1255+
*/
1256+
private function escapeTableName(TableName $tableName): string
1257+
{
1258+
$alias = $tableName->getAlias();
1259+
1260+
return $this->escapeIdentifier($tableName->getActualTableName())
1261+
. (($alias !== '') ? ' ' . $this->escapeIdentifier($alias) : '');
1262+
}
1263+
12361264
/**
12371265
* Escape the SQL Identifiers
12381266
*
@@ -1546,12 +1574,16 @@ public function tableExists(string $tableName, bool $cached = true): bool
15461574
/**
15471575
* Fetch Field Names
15481576
*
1577+
* @param string|TableName $tableName
1578+
*
15491579
* @return false|list<string>
15501580
*
15511581
* @throws DatabaseException
15521582
*/
1553-
public function getFieldNames(string $table)
1583+
public function getFieldNames($tableName)
15541584
{
1585+
$table = ($tableName instanceof TableName) ? $tableName->getTableName() : $tableName;
1586+
15551587
// Is there a cached result?
15561588
if (isset($this->dataCache['field_names'][$table])) {
15571589
return $this->dataCache['field_names'][$table];
@@ -1561,7 +1593,7 @@ public function getFieldNames(string $table)
15611593
$this->initialize();
15621594
}
15631595

1564-
if (false === ($sql = $this->_listColumns($table))) {
1596+
if (false === ($sql = $this->_listColumns($tableName))) {
15651597
if ($this->DBDebug) {
15661598
throw new DatabaseException('This feature is not available for the database you are using.');
15671599
}
@@ -1777,9 +1809,11 @@ abstract protected function _listTables(bool $constrainByPrefix = false, ?string
17771809
/**
17781810
* Generates a platform-specific query string so that the column names can be fetched.
17791811
*
1812+
* @param string|TableName $table
1813+
*
17801814
* @return false|string
17811815
*/
1782-
abstract protected function _listColumns(string $table = '');
1816+
abstract protected function _listColumns($table = '');
17831817

17841818
/**
17851819
* Platform-specific field data information.

system/Database/MySQLi/Connection.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use CodeIgniter\Database\BaseConnection;
1717
use CodeIgniter\Database\Exceptions\DatabaseException;
18+
use CodeIgniter\Database\TableName;
1819
use CodeIgniter\Exceptions\LogicException;
1920
use mysqli;
2021
use mysqli_result;
@@ -422,10 +423,19 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
422423

423424
/**
424425
* Generates a platform-specific query string so that the column names can be fetched.
426+
*
427+
* @param string|TableName $table
425428
*/
426-
protected function _listColumns(string $table = ''): string
429+
protected function _listColumns($table = ''): string
427430
{
428-
return 'SHOW COLUMNS FROM ' . $this->protectIdentifiers($table, true, null, false);
431+
$tableName = $this->protectIdentifiers(
432+
$table,
433+
true,
434+
null,
435+
false
436+
);
437+
438+
return 'SHOW COLUMNS FROM ' . $tableName;
429439
}
430440

431441
/**

system/Database/OCI8/Connection.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use CodeIgniter\Database\BaseConnection;
1717
use CodeIgniter\Database\Exceptions\DatabaseException;
1818
use CodeIgniter\Database\Query;
19+
use CodeIgniter\Database\TableName;
1920
use ErrorException;
2021
use stdClass;
2122

@@ -284,18 +285,25 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
284285

285286
/**
286287
* Generates a platform-specific query string so that the column names can be fetched.
288+
*
289+
* @param string|TableName $table
287290
*/
288-
protected function _listColumns(string $table = ''): string
291+
protected function _listColumns($table = ''): string
289292
{
290-
if (str_contains($table, '.')) {
291-
sscanf($table, '%[^.].%s', $owner, $table);
293+
if ($table instanceof TableName) {
294+
$tableName = $this->escape(strtoupper($table->getActualTableName()));
295+
$owner = $this->username;
296+
} elseif (str_contains($table, '.')) {
297+
sscanf($table, '%[^.].%s', $owner, $tableName);
298+
$tableName = $this->escape(strtoupper($this->DBPrefix . $tableName));
292299
} else {
293-
$owner = $this->username;
300+
$owner = $this->username;
301+
$tableName = $this->escape(strtoupper($this->DBPrefix . $table));
294302
}
295303

296304
return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
297305
WHERE UPPER(OWNER) = ' . $this->escape(strtoupper($owner)) . '
298-
AND UPPER(TABLE_NAME) = ' . $this->escape(strtoupper($this->DBPrefix . $table));
306+
AND UPPER(TABLE_NAME) = ' . $tableName;
299307
}
300308

301309
/**

system/Database/Postgre/Connection.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use CodeIgniter\Database\BaseConnection;
1717
use CodeIgniter\Database\Exceptions\DatabaseException;
1818
use CodeIgniter\Database\RawSql;
19+
use CodeIgniter\Database\TableName;
1920
use ErrorException;
2021
use PgSql\Connection as PgSqlConnection;
2122
use PgSql\Result as PgSqlResult;
@@ -300,13 +301,20 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
300301

301302
/**
302303
* Generates a platform-specific query string so that the column names can be fetched.
304+
*
305+
* @param string|TableName $table
303306
*/
304-
protected function _listColumns(string $table = ''): string
307+
protected function _listColumns($table = ''): string
305308
{
309+
if ($table instanceof TableName) {
310+
$tableName = $this->escape($table->getActualTableName());
311+
} else {
312+
$tableName = $this->escape($this->DBPrefix . strtolower($table));
313+
}
314+
306315
return 'SELECT "column_name"
307316
FROM "information_schema"."columns"
308-
WHERE LOWER("table_name") = '
309-
. $this->escape($this->DBPrefix . strtolower($table))
317+
WHERE LOWER("table_name") = ' . $tableName
310318
. ' ORDER BY "ordinal_position"';
311319
}
312320

system/Database/SQLSRV/Connection.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use CodeIgniter\Database\BaseConnection;
1717
use CodeIgniter\Database\Exceptions\DatabaseException;
18+
use CodeIgniter\Database\TableName;
1819
use stdClass;
1920

2021
/**
@@ -225,12 +226,20 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
225226

226227
/**
227228
* Generates a platform-specific query string so that the column names can be fetched.
229+
*
230+
* @param string|TableName $table
228231
*/
229-
protected function _listColumns(string $table = ''): string
232+
protected function _listColumns($table = ''): string
230233
{
234+
if ($table instanceof TableName) {
235+
$tableName = $this->escape(strtolower($table->getActualTableName()));
236+
} else {
237+
$tableName = $this->escape($this->DBPrefix . strtolower($table));
238+
}
239+
231240
return 'SELECT [COLUMN_NAME] '
232241
. ' FROM [INFORMATION_SCHEMA].[COLUMNS]'
233-
. ' WHERE [TABLE_NAME] = ' . $this->escape($this->DBPrefix . $table)
242+
. ' WHERE [TABLE_NAME] = ' . $tableName
234243
. ' AND [TABLE_SCHEMA] = ' . $this->escape($this->schema);
235244
}
236245

0 commit comments

Comments
 (0)