Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add functionality to pass spanner types to insert/update #154

31 changes: 25 additions & 6 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [], array $types = []): bool
{
// is SELECT query
if (0 === stripos(ltrim($query), 'select')) {
Expand All @@ -291,7 +291,7 @@ 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
Expand All @@ -301,11 +301,27 @@ public function statement($query, $bindings = []): bool
/**
* @inheritDoc
*/
public function affectingStatement($query, $bindings = []): int
public function insert($query, $bindings = [], array $types = []): bool
{
return $this->statement($query, $bindings, $types);
}

/**
* @inheritDoc
*/
public function update($query, $bindings = [], array $types = []): int
{
return $this->affectingStatement($query, $bindings, $types);
}

/**
* @inheritDoc
*/
public function affectingStatement($query, $bindings = [], array $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;
}
Expand All @@ -316,7 +332,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);

Expand Down
19 changes: 19 additions & 0 deletions src/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,6 +40,24 @@ class Model extends BaseModel
*/
public $incrementing = false;

/**
* @var string[]
*/
protected $types = [];

/**
* @inheritDoc
*/
public function newModelQuery()
{
/** @var Builder */
$baseQueryBuilder = $this->newBaseQueryBuilder();

return $this->newEloquentBuilder(
$baseQueryBuilder->setTypes($this->types)
)->setModel($this);
}

/**
* @param BaseModel|Relation $query
* @param mixed $value
Expand Down
85 changes: 84 additions & 1 deletion src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,95 @@ class Builder extends BaseBuilder
*/
public $connection;

/**
* @var string[]
*/
protected $types = [];

/**
* @param string[] $types
* @return $this
*/
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);

if (empty($values))
return true;

if (! is_array(reset($values))) {
$values = [$values];
} else {
foreach ($values as $key => $value) {
/** @var array $value */
ksort($value);
$values[$key] = $value;
}
}

$sql = $this->grammar->compileInsert($this, $values);

// Detect spanner types from values and create 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, $sql);
if(count($type)) $types[$type[0]] = $type[1];
$i++;
}
}

$this->applyBeforeQueryCallbacks();

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);

// Detect spanner types from values and create a binding compatible array of types
$types = [];
$i = 0;
foreach ($values as $key => $value) {
$type = $this->checkForType($i, $key, $value, $sql);
if(count($type)) $types[$type[0]] = $type[1];
$i++;
}

return $this->connection->update($sql, $this->cleanBindings(
$this->grammar->prepareBindingsForUpdate($this->bindings, $values)
), $types);
}

/**
* @return string[]
*/
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)) {
/** @var array $value */
if(!count($value)) return [];
}
if (is_string($value) && Parameterizer::hasLikeWildcard($sql, $value)) return [];

return ["p$i", $this->types[$key]];
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Parameterizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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, ['%', '_'])
Expand Down