From 22a154df326a594fe7af2e7b6b34379b857054c6 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 14:58:14 +1030 Subject: [PATCH 01/12] add functionality to pass spanner types to insert/update --- src/Connection.php | 24 ++++++++++++----- src/Eloquent/Model.php | 7 +++++ src/Query/Builder.php | 60 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index 41cdb87f..bfd44f78 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -280,7 +280,7 @@ public function cursorWithOptions(string $query, array $bindings, array $options /** * @inheritDoc */ - public function statement($query, $bindings = []): bool + public function statement($query, $bindings = [], $types = []): bool { // is SELECT query if (0 === stripos(ltrim($query), 'select')) { @@ -291,21 +291,30 @@ public function statement($query, $bindings = []): bool if (0 === stripos(ltrim($query), 'insert') || 0 === stripos(ltrim($query), 'update') || 0 === stripos(ltrim($query), 'delete')) { - return $this->affectingStatement($query, $bindings) !== null; + return $this->affectingStatement($query, $bindings, $types) !== null; } // is DDL Query return $this->runDdlBatch([$query]) !== null; } + public function insert($query, $bindings = [], $types = []) + { + return $this->statement($query, $bindings, $types); + } + + public function update($query, $bindings = [], $types = []) + { + return $this->affectingStatement($query, $bindings, $types); + } /** * @inheritDoc */ - public function affectingStatement($query, $bindings = []): int + public function affectingStatement($query, $bindings = [], $types = []): int { /** @var Closure(): int $runQueryCall */ - $runQueryCall = function () use ($query, $bindings) { - return $this->run($query, $bindings, function ($query, $bindings) { + $runQueryCall = function () use ($query, $bindings, $types) { + return $this->run($query, $bindings, function ($query, $bindings) use ($types) { if ($this->pretending()) { return 0; } @@ -316,7 +325,10 @@ public function affectingStatement($query, $bindings = []): int throw new RuntimeException('Tried to run update outside of transaction! Affecting statements must be done inside a transaction'); } - $rowCount = $transaction->executeUpdate($query, ['parameters' => $this->prepareBindings($bindings)]); + $rowCount = $transaction->executeUpdate($query, [ + 'parameters' => $this->prepareBindings($bindings), + 'types' => $types, + ]); $this->recordsHaveBeenModified($rowCount > 0); diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index 5cc79542..c85114d2 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -39,6 +39,13 @@ class Model extends BaseModel */ public $incrementing = false; + protected $types = []; + + public function newModelQuery() + { + return parent::newModelQuery()->setTypes($this->types); + } + /** * @param BaseModel|Relation $query * @param mixed $value diff --git a/src/Query/Builder.php b/src/Query/Builder.php index b7488832..4754e31a 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -34,12 +34,70 @@ class Builder extends BaseBuilder */ public $connection; + protected $types = []; + public function setTypes($types) + { + $this->types = $types; + return $this; + } + /** * @inheritDoc */ public function insert(array $values) { - return parent::insert($this->prepareInsertForDml($values)); + $values = $this->prepareInsertForDml($values); + $types = []; + $i = 0; + + if (empty($values)) + return true; + + if (! is_array(reset($values))) + $values = [$values]; + else { + foreach ($values as $key => $value) { + ksort($value); + $values[$key] = $value; + foreach ($value as $k => $v) { + [$p, $type] = $this->checkForType($i, $k, $v); + if($type) $types[$p] = $type; + $i++; + } + } + } + + $this->applyBeforeQueryCallbacks(); + + $sql = $this->grammar->compileInsert($this, $values); + return $this->connection->insert($sql, $this->cleanBindings(Arr::flatten($values, 1)), $types); + } + + public function update(array $values) + { + $this->applyBeforeQueryCallbacks(); + + $sql = $this->grammar->compileUpdate($this, $values); + $types = []; + $i = 0; + foreach ($values as $key => $value) { + [$p, $type] = $this->checkForType($i, $key, $value, $sql); + if($type) $types[$p] = $type; + $i++; + } + + return $this->connection->update($sql, $this->cleanBindings( + $this->grammar->prepareBindingsForUpdate($this->bindings, $values) + ), $types); + } + + public function checkForType($i, $key, $value, $sql = '') + { + if(!array_key_exists($key, $this->types)) return false; + if($value == null) return false; + if (is_array($value) && empty($value)) return false; + if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return false; + return ["p$i", $this->types[$key]]; } /** From ae2291c034e445922fc7b2079fa3f195ad9c3719 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 15:50:41 +1030 Subject: [PATCH 02/12] fix some unit test errors --- src/Connection.php | 15 +++++++++++---- src/Eloquent/Model.php | 6 ++++++ src/Query/Builder.php | 10 +++++++++- src/Query/Parameterizer.php | 2 +- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index bfd44f78..c231246c 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -280,7 +280,7 @@ public function cursorWithOptions(string $query, array $bindings, array $options /** * @inheritDoc */ - public function statement($query, $bindings = [], $types = []): bool + public function statement($query, $bindings = [], array $types = []): bool { // is SELECT query if (0 === stripos(ltrim($query), 'select')) { @@ -298,19 +298,26 @@ public function statement($query, $bindings = [], $types = []): bool return $this->runDdlBatch([$query]) !== null; } - public function insert($query, $bindings = [], $types = []) + /** + * @inheritDoc + */ + public function insert($query, $bindings = [], array $types = []): bool { return $this->statement($query, $bindings, $types); } - public function update($query, $bindings = [], $types = []) + /** + * @inheritDoc + */ + public function update($query, $bindings = [], array $types = []): int { return $this->affectingStatement($query, $bindings, $types); } + /** * @inheritDoc */ - public function affectingStatement($query, $bindings = [], $types = []): int + public function affectingStatement($query, $bindings = [], array $types = []): int { /** @var Closure(): int $runQueryCall */ $runQueryCall = function () use ($query, $bindings, $types) { diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index c85114d2..153e528e 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -39,8 +39,14 @@ class Model extends BaseModel */ public $incrementing = false; + /** + * @var string[] + */ protected $types = []; + /** + * @inheritDoc + */ public function newModelQuery() { return parent::newModelQuery()->setTypes($this->types); diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 4754e31a..9771e707 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -34,7 +34,15 @@ class Builder extends BaseBuilder */ public $connection; + /** + * @var string[] + */ protected $types = []; + + /** + * @param string[] $types + * @return self + */ public function setTypes($types) { $this->types = $types; @@ -91,7 +99,7 @@ public function update(array $values) ), $types); } - public function checkForType($i, $key, $value, $sql = '') + public function checkForType(int $i, string $key, mixed $value, string $sql = '') { if(!array_key_exists($key, $this->types)) return false; if($value == null) return false; diff --git a/src/Query/Parameterizer.php b/src/Query/Parameterizer.php index 7b4e6957..ed293c2a 100644 --- a/src/Query/Parameterizer.php +++ b/src/Query/Parameterizer.php @@ -72,7 +72,7 @@ public function parameterizeQuery(string $query, array $bindings): array * @param string $value * @return bool */ - private static function hasLikeWildcard(string $query, string $value) + public static function hasLikeWildcard(string $query, string $value) { return Str::contains(strtolower($query), 'like') && Str::contains($value, ['%', '_']) From fcc986f6026c9457630139583768f11296010ea9 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 16:10:38 +1030 Subject: [PATCH 03/12] fix some unit test errors --- src/Eloquent/Model.php | 15 ++++++++++----- src/Query/Builder.php | 5 ++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index 153e528e..3d8d07e6 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -17,6 +17,7 @@ namespace Colopl\Spanner\Eloquent; +use Colopl\Spanner\Query\Builder; use Illuminate\Database\Eloquent\Model as BaseModel; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Str; @@ -44,12 +45,16 @@ class Model extends BaseModel */ protected $types = []; - /** - * @inheritDoc - */ - public function newModelQuery() + public function newModelQuery(): Model + { + return $this->newEloquentBuilder( + $this->newBaseQueryBuilder()->setTypes($this) + )->setModel($this); + } + + protected function newBaseQueryBuilder(): Builder { - return parent::newModelQuery()->setTypes($this->types); + return $this->getConnection()->query(); } /** diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 9771e707..cb63729d 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -99,7 +99,10 @@ public function update(array $values) ), $types); } - public function checkForType(int $i, string $key, mixed $value, string $sql = '') + /** + * @return string[]|false + */ + public function checkForType(int $i, string|int $key, mixed $value, string $sql = '') { if(!array_key_exists($key, $this->types)) return false; if($value == null) return false; From 8b5bf50a2e8c95dd428e7ff2081a521f5806c6ff Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 16:50:16 +1030 Subject: [PATCH 04/12] fix some unit test errors --- src/Eloquent/Model.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index 3d8d07e6..3490e11f 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -45,13 +45,14 @@ class Model extends BaseModel */ protected $types = []; - public function newModelQuery(): Model + public function newModelQuery(): Builder|static { return $this->newEloquentBuilder( - $this->newBaseQueryBuilder()->setTypes($this) + $this->newBaseQueryBuilder()->setTypes($this->types) )->setModel($this); } + protected function newBaseQueryBuilder(): Builder { return $this->getConnection()->query(); From 4a030fbbc6ba724c710b62bc02e2e70b23c81a2f Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 19:22:12 +1030 Subject: [PATCH 05/12] try to fix some unit test errors --- src/Eloquent/Model.php | 16 ++++++++-------- src/Query/Builder.php | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index 3490e11f..d25450ec 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -45,19 +45,19 @@ class Model extends BaseModel */ protected $types = []; - public function newModelQuery(): Builder|static + /** + * @inheritDoc + */ + public function newModelQuery() { + /** @var Builder */ + $baseQueryBuilder = $this->newBaseQueryBuilder(); + return $this->newEloquentBuilder( - $this->newBaseQueryBuilder()->setTypes($this->types) + $baseQueryBuilder->setTypes($this->types) )->setModel($this); } - - protected function newBaseQueryBuilder(): Builder - { - return $this->getConnection()->query(); - } - /** * @param BaseModel|Relation $query * @param mixed $value diff --git a/src/Query/Builder.php b/src/Query/Builder.php index cb63729d..4e2e7618 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -41,7 +41,7 @@ class Builder extends BaseBuilder /** * @param string[] $types - * @return self + * @return $this */ public function setTypes($types) { From cb556b4750189c7a77ed713d74b1f318299f66bf Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 19:47:01 +1030 Subject: [PATCH 06/12] try to fix some unit test errors --- src/Query/Builder.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 4e2e7618..83d39deb 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -64,6 +64,7 @@ public function insert(array $values) if (! is_array(reset($values))) $values = [$values]; else { + /** @var array $value */ foreach ($values as $key => $value) { ksort($value); $values[$key] = $value; @@ -100,14 +101,14 @@ public function update(array $values) } /** - * @return string[]|false + * @return string[] */ public function checkForType(int $i, string|int $key, mixed $value, string $sql = '') { - if(!array_key_exists($key, $this->types)) return false; - if($value == null) return false; - if (is_array($value) && empty($value)) return false; - if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return false; + if(!array_key_exists($key, $this->types)) return []; + if($value == null) return []; + if (is_array($value) && empty($value)) return []; + if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return []; return ["p$i", $this->types[$key]]; } From ec207945d09645f5661a79334854f993b5eb8b46 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 20:07:38 +1030 Subject: [PATCH 07/12] try to fix some unit test errors --- src/Query/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 83d39deb..3ed53582 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -107,7 +107,7 @@ public function checkForType(int $i, string|int $key, mixed $value, string $sql { if(!array_key_exists($key, $this->types)) return []; if($value == null) return []; - if (is_array($value) && empty($value)) return []; + if (is_array($value) && !count($value)) return []; if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return []; return ["p$i", $this->types[$key]]; } From 1e575d5b3780969fae9518d6592636d6e83ab441 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 20:32:29 +1030 Subject: [PATCH 08/12] damn phpstan --- src/Query/Builder.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 3ed53582..6451d467 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -107,7 +107,11 @@ public function checkForType(int $i, string|int $key, mixed $value, string $sql { if(!array_key_exists($key, $this->types)) return []; if($value == null) return []; - if (is_array($value) && !count($value)) return []; + + if (is_array($value)) { + /** @var array $value */ + if(!count($value)) return []; + } if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return []; return ["p$i", $this->types[$key]]; } From 323e47ba1cfdf82cebc7af3a49fc87578938bda5 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 9 Dec 2023 21:17:16 +1030 Subject: [PATCH 09/12] tweak type check, remove destructuring --- src/Query/Builder.php | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 6451d467..c3f3c5d9 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -55,24 +55,29 @@ public function setTypes($types) public function insert(array $values) { $values = $this->prepareInsertForDml($values); - $types = []; - $i = 0; if (empty($values)) return true; - if (! is_array(reset($values))) + if (! is_array(reset($values))) { $values = [$values]; - else { - /** @var array $value */ + } else { foreach ($values as $key => $value) { + /** @var array $value */ ksort($value); $values[$key] = $value; - foreach ($value as $k => $v) { - [$p, $type] = $this->checkForType($i, $k, $v); - if($type) $types[$p] = $type; - $i++; - } + } + } + + // Detect spanner types from values and greate a binding compatible array of types + $types = []; + $i = 0; + foreach ($values as $key => $value) { + /** @var array $value */ + foreach ($value as $k => $v) { + $type = $this->checkForType($i, $k, $v); + if(count($type)) $types[$type[0]] = $type[1]; + $i++; } } @@ -87,11 +92,13 @@ public function update(array $values) $this->applyBeforeQueryCallbacks(); $sql = $this->grammar->compileUpdate($this, $values); + + // Detect spanner types from values and greate a binding compatible array of types $types = []; $i = 0; foreach ($values as $key => $value) { - [$p, $type] = $this->checkForType($i, $key, $value, $sql); - if($type) $types[$p] = $type; + $type = $this->checkForType($i, $key, $value, $sql); + if(count($type)) $types[$type[0]] = $type[1]; $i++; } From a6745128696f0df8bdf6f9ea4b6f5ac84ba9aa7e Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 10 Dec 2023 16:24:50 +1030 Subject: [PATCH 10/12] generate insert sql before checkForType --- src/Query/Builder.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index c3f3c5d9..62c7efa3 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -69,13 +69,15 @@ public function insert(array $values) } } + $sql = $this->grammar->compileInsert($this, $values); + // Detect spanner types from values and greate a binding compatible array of types $types = []; $i = 0; foreach ($values as $key => $value) { /** @var array $value */ foreach ($value as $k => $v) { - $type = $this->checkForType($i, $k, $v); + $type = $this->checkForType($i, $k, $v, $sql); if(count($type)) $types[$type[0]] = $type[1]; $i++; } @@ -83,7 +85,6 @@ public function insert(array $values) $this->applyBeforeQueryCallbacks(); - $sql = $this->grammar->compileInsert($this, $values); return $this->connection->insert($sql, $this->cleanBindings(Arr::flatten($values, 1)), $types); } From 18303fd3def7c87b0e751ce0619b9159409524ed Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 10 Dec 2023 16:27:16 +1030 Subject: [PATCH 11/12] dingus --- src/Query/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 62c7efa3..423d211a 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -71,7 +71,7 @@ public function insert(array $values) $sql = $this->grammar->compileInsert($this, $values); - // Detect spanner types from values and greate a binding compatible array of types + // Detect spanner types from values and create a binding compatible array of types $types = []; $i = 0; foreach ($values as $key => $value) { @@ -94,7 +94,7 @@ public function update(array $values) $sql = $this->grammar->compileUpdate($this, $values); - // Detect spanner types from values and greate a binding compatible array of types + // Detect spanner types from values and create a binding compatible array of types $types = []; $i = 0; foreach ($values as $key => $value) { From 808de619b8cd6566530ddd270d33bb93026a9cf9 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 10 Dec 2023 16:30:16 +1030 Subject: [PATCH 12/12] foo --- src/Query/Builder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 423d211a..f887f000 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -121,6 +121,7 @@ public function checkForType(int $i, string|int $key, mixed $value, string $sql if(!count($value)) return []; } if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return []; + return ["p$i", $this->types[$key]]; }