Skip to content

Commit 851bf0d

Browse files
committed
fix!: spark db:table causes errors w/z table name including speciali chars
1 parent 5b902a0 commit 851bf0d

File tree

12 files changed

+132
-41
lines changed

12 files changed

+132
-41
lines changed

phpstan-baseline.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2161,12 +2161,6 @@
21612161
'count' => 1,
21622162
'path' => __DIR__ . '/system/Database/BaseConnection.php',
21632163
];
2164-
$ignoreErrors[] = [
2165-
// identifier: missingType.iterableValue
2166-
'message' => '#^Property CodeIgniter\\\\Database\\\\BaseConnection\\:\\:\\$aliasedTables type has no value type specified in iterable type array\\.$#',
2167-
'count' => 1,
2168-
'path' => __DIR__ . '/system/Database/BaseConnection.php',
2169-
];
21702164
$ignoreErrors[] = [
21712165
// identifier: missingType.iterableValue
21722166
'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: 9 additions & 4 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)) {

system/Database/BaseConnection.php

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,10 @@ public function setAliasedTables(array $aliases)
578578
*/
579579
public function addTableAlias(string $alias)
580580
{
581+
if ($alias === '') {
582+
return $this;
583+
}
584+
581585
if (! in_array($alias, $this->aliasedTables, true)) {
582586
$this->aliasedTables[] = $alias;
583587
}
@@ -915,7 +919,7 @@ abstract protected function _transRollback(): bool;
915919
/**
916920
* Returns a non-shared new instance of the query builder for this connection.
917921
*
918-
* @param array|string $tableName
922+
* @param array|string|TableName $tableName
919923
*
920924
* @return BaseBuilder
921925
*
@@ -1044,10 +1048,10 @@ public function getConnectDuration(int $decimals = 6): string
10441048
* insert the table prefix (if it exists) in the proper position, and escape only
10451049
* the correct identifiers.
10461050
*
1047-
* @param array|int|string $item
1048-
* @param bool $prefixSingle Prefix a table name with no segments?
1049-
* @param bool $protectIdentifiers Protect table or column names?
1050-
* @param bool $fieldExists Supplied $item contains a column name?
1051+
* @param array|int|string|TableName $item
1052+
* @param bool $prefixSingle Prefix a table name with no segments?
1053+
* @param bool $protectIdentifiers Protect table or column names?
1054+
* @param bool $fieldExists Supplied $item contains a column name?
10511055
*
10521056
* @return array|string
10531057
* @phpstan-return ($item is array ? array : string)
@@ -1068,6 +1072,11 @@ public function protectIdentifiers($item, bool $prefixSingle = false, ?bool $pro
10681072
return $escapedArray;
10691073
}
10701074

1075+
if ($item instanceof TableName) {
1076+
/** @psalm-suppress NoValue I don't know why ERROR. */
1077+
return $this->escapeTableName($item);
1078+
}
1079+
10711080
// If you pass `['column1', 'column2']`, `$item` will be int because the array keys are int.
10721081
$item = (string) $item;
10731082

@@ -1210,14 +1219,18 @@ private function protectDotItem(string $item, string $alias, bool $protectIdenti
12101219
*
12111220
* This function escapes single identifier.
12121221
*
1213-
* @param non-empty-string $item
1222+
* @param non-empty-string|TableName $item
12141223
*/
1215-
public function escapeIdentifier(string $item): string
1224+
public function escapeIdentifier($item): string
12161225
{
12171226
if ($item === '') {
12181227
return '';
12191228
}
12201229

1230+
if ($item instanceof TableName) {
1231+
return $this->escapeTableName($item);
1232+
}
1233+
12211234
return $this->escapeChar
12221235
. str_replace(
12231236
$this->escapeChar,
@@ -1227,6 +1240,17 @@ public function escapeIdentifier(string $item): string
12271240
. $this->escapeChar;
12281241
}
12291242

1243+
/**
1244+
* Returns escaped table name with alias.
1245+
*/
1246+
private function escapeTableName(TableName $tableName): string
1247+
{
1248+
$alias = $tableName->getAlias();
1249+
1250+
return $this->escapeIdentifier($tableName->getActualTableName())
1251+
. (($alias !== '') ? ' ' . $this->escapeIdentifier($alias) : '');
1252+
}
1253+
12301254
/**
12311255
* Escape the SQL Identifiers
12321256
*
@@ -1540,12 +1564,16 @@ public function tableExists(string $tableName, bool $cached = true): bool
15401564
/**
15411565
* Fetch Field Names
15421566
*
1567+
* @param string|TableName $tableName
1568+
*
15431569
* @return false|list<string>
15441570
*
15451571
* @throws DatabaseException
15461572
*/
1547-
public function getFieldNames(string $table)
1573+
public function getFieldNames($tableName)
15481574
{
1575+
$table = ($tableName instanceof TableName) ? $tableName->getTableName() : $tableName;
1576+
15491577
// Is there a cached result?
15501578
if (isset($this->dataCache['field_names'][$table])) {
15511579
return $this->dataCache['field_names'][$table];
@@ -1555,7 +1583,7 @@ public function getFieldNames(string $table)
15551583
$this->initialize();
15561584
}
15571585

1558-
if (false === ($sql = $this->_listColumns($table))) {
1586+
if (false === ($sql = $this->_listColumns($tableName))) {
15591587
if ($this->DBDebug) {
15601588
throw new DatabaseException('This feature is not available for the database you are using.');
15611589
}
@@ -1771,9 +1799,11 @@ abstract protected function _listTables(bool $constrainByPrefix = false, ?string
17711799
/**
17721800
* Generates a platform-specific query string so that the column names can be fetched.
17731801
*
1802+
* @param string|TableName $table
1803+
*
17741804
* @return false|string
17751805
*/
1776-
abstract protected function _listColumns(string $table = '');
1806+
abstract protected function _listColumns($table = '');
17771807

17781808
/**
17791809
* 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;
@@ -408,10 +409,19 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
408409

409410
/**
410411
* Generates a platform-specific query string so that the column names can be fetched.
412+
*
413+
* @param string|TableName $table
411414
*/
412-
protected function _listColumns(string $table = ''): string
415+
protected function _listColumns($table = ''): string
413416
{
414-
return 'SHOW COLUMNS FROM ' . $this->protectIdentifiers($table, true, null, false);
417+
$tableName = $this->protectIdentifiers(
418+
$table,
419+
true,
420+
null,
421+
false
422+
);
423+
424+
return 'SHOW COLUMNS FROM ' . $tableName;
415425
}
416426

417427
/**

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

@@ -266,18 +267,25 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
266267

267268
/**
268269
* Generates a platform-specific query string so that the column names can be fetched.
270+
*
271+
* @param string|TableName $table
269272
*/
270-
protected function _listColumns(string $table = ''): string
273+
protected function _listColumns($table = ''): string
271274
{
272-
if (str_contains($table, '.')) {
273-
sscanf($table, '%[^.].%s', $owner, $table);
275+
if ($table instanceof TableName) {
276+
$tableName = $this->escape(strtoupper($table->getActualTableName()));
277+
$owner = $this->username;
278+
} elseif (str_contains($table, '.')) {
279+
sscanf($table, '%[^.].%s', $owner, $tableName);
280+
$tableName = $this->escape(strtoupper($this->DBPrefix . $tableName));
274281
} else {
275-
$owner = $this->username;
282+
$owner = $this->username;
283+
$tableName = $this->escape(strtoupper($this->DBPrefix . $table));
276284
}
277285

278286
return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
279287
WHERE UPPER(OWNER) = ' . $this->escape(strtoupper($owner)) . '
280-
AND UPPER(TABLE_NAME) = ' . $this->escape(strtoupper($this->DBPrefix . $table));
288+
AND UPPER(TABLE_NAME) = ' . $tableName;
281289
}
282290

283291
/**

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;
@@ -302,13 +303,20 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
302303

303304
/**
304305
* Generates a platform-specific query string so that the column names can be fetched.
306+
*
307+
* @param string|TableName $table
305308
*/
306-
protected function _listColumns(string $table = ''): string
309+
protected function _listColumns($table = ''): string
307310
{
311+
if ($table instanceof TableName) {
312+
$tableName = $this->escape($table->getActualTableName());
313+
} else {
314+
$tableName = $this->escape($this->DBPrefix . strtolower($table));
315+
}
316+
308317
return 'SELECT "column_name"
309318
FROM "information_schema"."columns"
310-
WHERE LOWER("table_name") = '
311-
. $this->escape($this->DBPrefix . strtolower($table))
319+
WHERE LOWER("table_name") = ' . $tableName
312320
. ' ORDER BY "ordinal_position"';
313321
}
314322

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

system/Database/SQLite3/Connection.php

Lines changed: 17 additions & 4 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 Exception;
1920
use SQLite3;
2021
use SQLite3Result;
@@ -209,19 +210,31 @@ protected function _listTables(bool $prefixLimit = false, ?string $tableName = n
209210

210211
/**
211212
* Generates a platform-specific query string so that the column names can be fetched.
213+
*
214+
* @param string|TableName $table
212215
*/
213-
protected function _listColumns(string $table = ''): string
216+
protected function _listColumns($table = ''): string
214217
{
215-
return 'PRAGMA TABLE_INFO(' . $this->protectIdentifiers($table, true, null, false) . ')';
218+
if ($table instanceof TableName) {
219+
$tableName = $this->escapeIdentifier($table);
220+
} else {
221+
$tableName = $this->protectIdentifiers($table, true, null, false);
222+
}
223+
224+
return 'PRAGMA TABLE_INFO(' . $tableName . ')';
216225
}
217226

218227
/**
228+
* @param string|TableName $tableName
229+
*
219230
* @return false|list<string>
220231
*
221232
* @throws DatabaseException
222233
*/
223-
public function getFieldNames(string $table)
234+
public function getFieldNames($tableName)
224235
{
236+
$table = ($tableName instanceof TableName) ? $tableName->getTableName() : $tableName;
237+
225238
// Is there a cached result?
226239
if (isset($this->dataCache['field_names'][$table])) {
227240
return $this->dataCache['field_names'][$table];
@@ -231,7 +244,7 @@ public function getFieldNames(string $table)
231244
$this->initialize();
232245
}
233246

234-
$sql = $this->_listColumns($table);
247+
$sql = $this->_listColumns($tableName);
235248

236249
$query = $this->query($sql);
237250
$this->dataCache['field_names'][$table] = [];

0 commit comments

Comments
 (0)