Skip to content

Commit 07f066c

Browse files
taka-oyamahalnique
andauthored
fix: phpstan errors (#281)
* fix!: SchemaBuilder::getForeignKeys was returning incorrect values (#275) * fix!: SchemaBuilder::getTables was returning incorrect values (#278) * fix: SchemaBuilder::getColumns was missing values (#280) Co-authored-by: halnique <shunsuke4dev@gmail.com> * fix: phpstan errors * fix --------- Co-authored-by: halnique <shunsuke4dev@gmail.com>
1 parent bd0adbe commit 07f066c

File tree

6 files changed

+190
-75
lines changed

6 files changed

+190
-75
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ RUN pecl_mt_install() { \
3434
&& apk add --no-cache bash gmp libxml2 libstdc++ \
3535
&& apk add --no-cache --virtual=.build-deps autoconf curl-dev gcc gmp-dev g++ libxml2-dev linux-headers make pcre-dev tzdata \
3636
&& docker-php-ext-install -j$(nproc) bcmath gmp \
37-
&& pecl_mt_install protobuf \
37+
&& pecl_mt_install protobuf-4.30.2 \
3838
&& pecl_mt_install grpc \
3939
&& pecl_mt_install pcov \
4040
&& docker-php-ext-enable grpc opcache protobuf \

phpstan.neon

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,14 @@ parameters:
66
- message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface\\:\\:recordsHaveBeenModified\\(\\)\\.$#"
77
count: 1
88
path: src/Query/Processor.php
9+
-
10+
message: '#^Closure invoked with 2 parameters, 3 required\.$#'
11+
identifier: arguments.count
12+
count: 1
13+
path: src/Schema/Builder.php
14+
15+
-
16+
message: '#^Parameter \#2 of closure expects Closure, Closure\|null given\.$#'
17+
identifier: argument.type
18+
count: 1
19+
path: src/Schema/Builder.php

src/Query/Processor.php

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -100,41 +100,53 @@ protected function processColumn(mixed $value): mixed
100100
}
101101

102102
/**
103-
* Process the results of a columns query.
104-
*
105-
* {@inheritDoc}
106-
* @param array<array-key, array<array-key, mixed>> $results
107-
* @return array<array-key, array{
108-
* name: string,
109-
* type_name: string,
110-
* type: string,
111-
* collation: null,
112-
* nullable: bool,
113-
* default: scalar,
114-
* auto_increment: false,
115-
* comment: null
116-
* }>
103+
* @inheritDoc
104+
*/
105+
public function processTables($results)
106+
{
107+
return array_map(function ($result) {
108+
$result = (object) $result;
109+
110+
return [
111+
'name' => $result->name,
112+
'schema' => $result->schema !== '' ? $result->schema : null,
113+
'schema_qualified_name' => $result->schema !== ''
114+
? $result->schema . '.' . $result->name
115+
: $result->name,
116+
'parent' => $result->parent,
117+
'size' => null,
118+
'comment' => null,
119+
'collation' => null,
120+
'engine' => null,
121+
];
122+
}, $results);
123+
}
124+
125+
/**
126+
* @inheritDoc
117127
*/
118128
public function processColumns($results)
119129
{
120130
return array_map(static function (array $result) {
131+
$result = (object) $result;
132+
121133
return [
122-
'name' => $result['COLUMN_NAME'],
123-
'type_name' => preg_replace("/\([^)]+\)/", "", $result['SPANNER_TYPE']),
124-
'type' => $result['SPANNER_TYPE'],
134+
'name' => $result->name,
135+
'type_name' => (string) preg_replace("/\([^)]+\)/", "", $result->type),
136+
'type' => $result->type,
125137
'collation' => null,
126-
'nullable' => $result['IS_NULLABLE'] !== 'NO',
127-
'default' => $result['COLUMN_DEFAULT'],
138+
'nullable' => $result->nullable === 'YES',
139+
'default' => $result->default,
140+
// TODO check IS_IDENTITY and set auto_increment accordingly
128141
'auto_increment' => false,
129142
'comment' => null,
143+
'generation' => null,
130144
];
131145
}, $results);
132146
}
133147

134148
/**
135-
* {@inheritDoc}
136-
* @param list<array<string, mixed>> $results
137-
* @return list<array{name: string, columns: list<string>, type: string, unique: bool, primary: bool}>
149+
* @inheritDoc
138150
*/
139151
public function processIndexes($results)
140152
{
@@ -152,14 +164,22 @@ public function processIndexes($results)
152164
}
153165

154166
/**
155-
* {@inheritDoc}
156-
* @param array{key_name: string}&array<string, mixed> $results
157-
* @return array<array-key, string>
167+
* @inheritDoc
158168
*/
159169
public function processForeignKeys($results)
160170
{
161171
return array_map(function ($result) {
162-
return ((object) $result)->key_name;
172+
$result = (object) $result;
173+
174+
return [
175+
'name' => $result->name,
176+
'columns' => explode(',', $result->columns),
177+
'foreign_schema' => $result->foreign_schema,
178+
'foreign_table' => $result->foreign_table,
179+
'foreign_columns' => explode(',', $result->foreign_columns),
180+
'on_update' => strtolower($result->on_update),
181+
'on_delete' => strtolower($result->on_delete),
182+
];
163183
}, $results);
164184
}
165185
}

src/Schema/Builder.php

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,6 @@ class Builder extends BaseBuilder
4040
*/
4141
public static $defaultMorphKeyType = 'uuid';
4242

43-
/**
44-
* @param null $schema
45-
* @inheritDoc Adds a parent key, for tracking interleaving
46-
*
47-
* @return list<array{ name: string, type: string, parent: string }>
48-
*/
49-
public function getTables($schema = null)
50-
{
51-
/** @var list<array{ name: string, type: string, parent: string }> */
52-
return $this->connection->select(
53-
$this->grammar->compileTables(null),
54-
);
55-
}
56-
5743
/**
5844
* @param string $table
5945
* @param string $name
@@ -73,7 +59,7 @@ public function dropIndex($table, $name)
7359
*/
7460
public function dropIndexIfExist($table, $name)
7561
{
76-
if (in_array($name, $this->getIndexes($table), true)) {
62+
if (in_array($name, $this->getIndexListing($table), true)) {
7763
$blueprint = $this->createBlueprint($table);
7864
$blueprint->dropIndex($name);
7965
$this->build($blueprint);
@@ -85,7 +71,6 @@ public function dropIndexIfExist($table, $name)
8571
*/
8672
protected function createBlueprint($table, ?Closure $callback = null)
8773
{
88-
/** @phpstan-ignore isset.property */
8974
return isset($this->resolver)
9075
? ($this->resolver)($table, $callback)
9176
: new Blueprint($this->connection, $table, $callback);
@@ -97,6 +82,17 @@ protected function createBlueprint($table, ?Closure $callback = null)
9782
public function dropAllTables()
9883
{
9984
$connection = $this->connection;
85+
/** @var list<array{
86+
* name: string,
87+
* schema: string|null,
88+
* schema_qualified_name: string,
89+
* size: int|null,
90+
* comment: string|null,
91+
* collation: string|null,
92+
* engine: string|null,
93+
* parent: string|null
94+
* }> $tables
95+
*/
10096
$tables = $this->getTables();
10197

10298
if (count($tables) === 0) {
@@ -134,7 +130,7 @@ public function dropAllTables()
134130
$foreigns = $this->getForeignKeys($tableName);
135131
$blueprint = $this->createBlueprint($tableName);
136132
foreach ($foreigns as $foreign) {
137-
$blueprint->dropForeign($foreign);
133+
$blueprint->dropForeign($foreign['name']);
138134
}
139135
array_push($queries, ...$blueprint->toSql());
140136
}

src/Schema/Grammar.php

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,38 @@ class Grammar extends BaseGrammar
4747
*/
4848
public function compileTables($schema)
4949
{
50-
return 'select `table_name` as name, `table_type` as type, `parent_table_name` as parent from information_schema.tables where table_schema = \'\' and table_type = \'BASE TABLE\'';
50+
return implode(' ', [
51+
'select',
52+
implode(', ', [
53+
'table_name as name',
54+
'table_schema as `schema`',
55+
'parent_table_name as parent',
56+
]),
57+
'from information_schema.tables',
58+
'where table_type = \'BASE TABLE\'',
59+
'and table_schema = \'\'',
60+
]);
61+
}
62+
63+
/**
64+
* Compile the query to determine the columns.
65+
*
66+
* @param string $table
67+
* @return string
68+
*/
69+
public function compileColumns($schema, $table)
70+
{
71+
return implode(' ', [
72+
'select',
73+
implode(', ', [
74+
'column_name as `name`',
75+
'spanner_type as `type`',
76+
'is_nullable as `nullable`',
77+
'column_default as `default`',
78+
]),
79+
'from information_schema.columns',
80+
'where table_name = ' . $this->quoteString($table),
81+
]);
5182
}
5283

5384
/**
@@ -84,25 +115,24 @@ public function compileIndexes($schema, $table)
84115
*/
85116
public function compileForeignKeys($schema, $table)
86117
{
87-
return sprintf(
88-
'select constraint_name as `key_name` from information_schema.table_constraints where constraint_type = "FOREIGN KEY" and table_schema = \'\' and table_name = %s',
89-
$this->quoteString($table),
90-
);
91-
}
92-
93-
/**
94-
* Compile the query to determine the columns.
95-
*
96-
* @param string|null $schema
97-
* @param $table
98-
* @return string
99-
*/
100-
public function compileColumns($schema, $table)
101-
{
102-
return sprintf(
103-
'select * from information_schema.columns where table_schema = \'\' and table_name = %s',
104-
$this->quoteString($table),
105-
);
118+
return implode(' ', [
119+
'select',
120+
implode(', ', [
121+
'kc.constraint_name as `name`',
122+
'string_agg(kc.column_name) as `columns`',
123+
'cc.table_schema as `foreign_schema`',
124+
'cc.table_name as `foreign_table`',
125+
'string_agg(cc.column_name) as `foreign_columns`',
126+
'rc.update_rule as `on_update`',
127+
'rc.delete_rule as `on_delete`',
128+
]),
129+
'from information_schema.key_column_usage kc',
130+
'join information_schema.referential_constraints rc on kc.constraint_name = rc.constraint_name',
131+
'join information_schema.constraint_column_usage cc on kc.constraint_name = cc.constraint_name',
132+
'where kc.table_schema = ""',
133+
'and kc.table_name = ' . $this->quoteString($table),
134+
'group by kc.constraint_name, cc.table_schema, cc.table_name, rc.update_rule, rc.delete_rule'
135+
]);
106136
}
107137

108138
/**

tests/Schema/BuilderTestLast.php

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -213,35 +213,63 @@ public function test_getTables(): void
213213
$table->primary('id');
214214
});
215215

216-
/** @var array{ name: string, type: string } $row */
217-
$row = Arr::first(
218-
$sb->getTables(),
219-
static fn(array $row): bool => $row['name'] === $table,
220-
);
216+
$row = Arr::first($sb->getTables(), fn ($row) => $row['name'] === $table);
221217

222-
$this->assertSame($table, $row['name']);
218+
$this->assertSame([
219+
'name' => $table,
220+
'schema' => null,
221+
'schema_qualified_name' => $table,
222+
'parent' => null,
223+
'size' => null,
224+
'comment' => null,
225+
'collation' => null,
226+
'engine' => null,
227+
], $row);
223228
}
224229

225-
public function test_getColumns(): void
230+
public function test_getColumns_with_nullable(): void
226231
{
227232
$conn = $this->getDefaultConnection();
228233
$sb = $conn->getSchemaBuilder();
229234
$table = $this->generateTableName(class_basename(__CLASS__));
230235

231236
$sb->create($table, function (Blueprint $table) {
232-
$table->uuid('id');
233-
$table->primary('id');
237+
$table->integer('id')->nullable()->primary();
238+
});
239+
240+
$this->assertSame([
241+
'name' => 'id',
242+
'type_name' => 'INT64',
243+
'type' => 'INT64',
244+
'collation' => null,
245+
'nullable' => true,
246+
'default' => null,
247+
'auto_increment' => false,
248+
'comment' => null,
249+
'generation' => null,
250+
], Arr::first($sb->getColumns($table)));
251+
}
252+
253+
public function test_getColumns_with_default(): void
254+
{
255+
$conn = $this->getDefaultConnection();
256+
$sb = $conn->getSchemaBuilder();
257+
$table = $this->generateTableName(class_basename(__CLASS__));
258+
259+
$sb->create($table, function (Blueprint $table) {
260+
$table->string('id', 1)->default('a')->primary();
234261
});
235262

236263
$this->assertSame([
237264
'name' => 'id',
238265
'type_name' => 'STRING',
239-
'type' => 'STRING(36)',
266+
'type' => 'STRING(1)',
240267
'collation' => null,
241268
'nullable' => false,
242-
'default' => null,
269+
'default' => '"a"',
243270
'auto_increment' => false,
244271
'comment' => null,
272+
'generation' => null,
245273
], Arr::first($sb->getColumns($table)));
246274
}
247275

@@ -305,6 +333,36 @@ public function test_getIndexListing(): void
305333
], $sb->getIndexListing($table));
306334
}
307335

336+
public function test_getForeignKeys(): void
337+
{
338+
$conn = $this->getDefaultConnection();
339+
$sb = $conn->getSchemaBuilder();
340+
$table1 = $this->generateTableName(class_basename(__CLASS__). '_1');
341+
$sb->create($table1, function (Blueprint $table) {
342+
$table->uuid('id')->primary();
343+
$table->uuid('something');
344+
$table->index('something');
345+
});
346+
347+
$table2 = $this->generateTableName(class_basename(__CLASS__). '_2');
348+
$sb->create($table2, function (Blueprint $table) use ($table1) {
349+
$table->uuid('table2_id')->primary();
350+
$table->uuid('other_id');
351+
$table->index('other_id');
352+
$table->foreign('other_id')->references('id')->on($table1);
353+
});
354+
355+
$this->assertSame([[
356+
'name' => strtolower($table2) . '_other_id_foreign',
357+
'columns' => ['other_id'],
358+
'foreign_schema' => '',
359+
'foreign_table' => $table1,
360+
'foreign_columns' => ['id'],
361+
'on_update' => "no action",
362+
'on_delete' => "no action",
363+
]], $sb->getForeignKeys($table2));
364+
}
365+
308366
public function test_dropAllTables(): void
309367
{
310368
$conn = $this->getDefaultConnection();

0 commit comments

Comments
 (0)