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

DB Insert Ignore (Tada5hi/database-feature) #2446

Merged
merged 12 commits into from
Dec 10, 2019
84 changes: 77 additions & 7 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ class BaseBuilder
*/
protected $QBWhereGroupCount = 0;

/**
* Ignore data that cause certain
* exceptions, for example in case of
* duplicate keys.
*
* @var boolean
*/
protected $QBIgnore = false;

/**
* A reference to the database connection.
*
Expand Down Expand Up @@ -220,6 +229,14 @@ class BaseBuilder
*/
protected $canLimitWhereUpdates = true;

/**
* Specifies which sql statements
* support the ignore option.
*
* @var array
*/
protected $supportedIgnoreStatements = [];

/**
* Builder testing mode status.
*
Expand Down Expand Up @@ -291,6 +308,25 @@ public function getBinds(): array

//--------------------------------------------------------------------

/**
* Ignore
*
* Set ignore Flag for next insert,
* update or delete query.
*
* @param boolean $ignore
*
* @return BaseBuilder
*/
public function ignore(bool $ignore = true)
{
$this->QBIgnore = $ignore;

return $this;
}

//--------------------------------------------------------------------

/**
* Select
*
Expand Down Expand Up @@ -2037,7 +2073,7 @@ public function insertBatch(array $set = null, bool $escape = null, int $batchSi
*/
protected function _insertBatch(string $table, array $keys, array $values): string
{
return 'INSERT INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES ' . implode(', ', $values);
return 'INSERT ' . $this->compileIgnore('insert') . 'INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES ' . implode(', ', $values);
}

//--------------------------------------------------------------------
Expand Down Expand Up @@ -2106,6 +2142,8 @@ public function setInsertBatch($key, string $value = '', bool $escape = null)
*
* @param boolean $reset TRUE: reset QB values; FALSE: leave QB values alone
*
* @throws DatabaseException
*
* @return string
*/
public function getCompiledInsert(bool $reset = true): string
Expand Down Expand Up @@ -2139,6 +2177,8 @@ public function getCompiledInsert(bool $reset = true): string
* @param array $set An associative array of insert values
* @param boolean $escape Whether to escape values and identifiers
*
* @throws DatabaseException
*
* @return BaseResult|Query|false
*/
public function insert(array $set = null, bool $escape = null)
Expand Down Expand Up @@ -2216,7 +2256,7 @@ protected function validateInsert(): bool
*/
protected function _insert(string $table, array $keys, array $unescapedKeys): string
{
return 'INSERT INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES (' . implode(', ', $unescapedKeys) . ')';
return 'INSERT ' . $this->compileIgnore('insert') . 'INTO ' . $table . ' (' . implode(', ', $keys) . ') VALUES (' . implode(', ', $unescapedKeys) . ')';
}

//--------------------------------------------------------------------
Expand Down Expand Up @@ -2330,6 +2370,8 @@ public function getCompiledUpdate(bool $reset = true): string
* @param mixed $where
* @param integer $limit
*
* @throws DatabaseException
*
* @return boolean TRUE on success, FALSE on failure
*/
public function update(array $set = null, $where = null, int $limit = null): bool
Expand Down Expand Up @@ -2393,14 +2435,14 @@ public function update(array $set = null, $where = null, int $limit = null): boo
*/
protected function _update(string $table, array $values): string
{
$valstr = [];
$valStr = [];

foreach ($values as $key => $val)
{
$valstr[] = $key . ' = ' . $val;
$valStr[] = $key . ' = ' . $val;
}

return 'UPDATE ' . $table . ' SET ' . implode(', ', $valstr)
return 'UPDATE ' . $this->compileIgnore('update') . $table . ' SET ' . implode(', ', $valStr)
. $this->compileWhereHaving('QBWhere')
. $this->compileOrderBy()
. ($this->QBLimit ? $this->_limit(' ') : '');
Expand Down Expand Up @@ -2530,6 +2572,7 @@ protected function _updateBatch(string $table, array $values, string $index): st
{
$ids = [];
$final = [];

foreach ($values as $key => $val)
{
$ids[] = $val[$index];
Expand All @@ -2553,7 +2596,7 @@ protected function _updateBatch(string $table, array $values, string $index): st

$this->where($index . ' IN(' . implode(',', $ids) . ')', null, false);

return 'UPDATE ' . $table . ' SET ' . substr($cases, 0, -2) . $this->compileWhereHaving('QBWhere');
return 'UPDATE ' . $this->compileIgnore('update') . $table . ' SET ' . substr($cases, 0, -2) . $this->compileWhereHaving('QBWhere');
}

//--------------------------------------------------------------------
Expand Down Expand Up @@ -2806,7 +2849,7 @@ public function decrement(string $column, int $value = 1)
*/
protected function _delete(string $table): string
{
return 'DELETE FROM ' . $table . $this->compileWhereHaving('QBWhere')
return 'DELETE ' . $this->compileIgnore('delete') . 'FROM ' . $table . $this->compileWhereHaving('QBWhere')
. ($this->QBLimit ? ' LIMIT ' . $this->QBLimit : '');
}

Expand Down Expand Up @@ -2922,6 +2965,32 @@ protected function compileSelect($select_override = false): string

//--------------------------------------------------------------------

/**
* Compile Ignore Statement
*
* Checks if the ignore option is supported by
* the Database Driver for the specific statement.
*
* @param string $statement
*
* @return string
*/
protected function compileIgnore(string $statement)
{
$sql = '';

if ($this->QBIgnore &&
isset($this->supportedIgnoreStatements[$statement])
)
{
$sql = trim($this->supportedIgnoreStatements[$statement]) . ' ';
}

return $sql;
}

//--------------------------------------------------------------------

/**
* Compile WHERE, HAVING statements
*
Expand Down Expand Up @@ -3242,6 +3311,7 @@ protected function resetWrite()
'QBOrderBy' => [],
'QBKeys' => [],
'QBLimit' => false,
'QBIgnore' => false,
]);
}

Expand Down
13 changes: 12 additions & 1 deletion system/Database/MySQLi/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ class Builder extends BaseBuilder
*/
protected $escapeChar = '`';

/**
* Specifies which sql statements
* support the ignore option.
*
* @var array
*/
protected $supportedIgnoreStatements = [
'update' => 'IGNORE',
'insert' => 'IGNORE',
'delete' => 'IGNORE',
];

/**
* FROM tables
*
Expand All @@ -73,5 +85,4 @@ protected function _fromTables(): string

return implode(', ', $this->QBFrom);
}

}
39 changes: 39 additions & 0 deletions system/Database/Postgre/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Database\Exceptions\DatabaseException;
use http\Encoding\Stream\Inflate;

/**
* Builder for Postgre
Expand All @@ -56,6 +57,40 @@ class Builder extends BaseBuilder
'RANDOM()',
];

/**
* Specifies which sql statements
* support the ignore option.
*
* @var array
*/
protected $supportedIgnoreStatements = [
'insert' => 'ON CONFLICT DO NOTHING',
];

//--------------------------------------------------------------------

/**
* Compile Ignore Statement
*
* Checks if the ignore option is supported by
* the Database Driver for the specific statement.
*
* @param string $statement
*
* @return string
*/
protected function compileIgnore(string $statement)
{
$sql = parent::compileIgnore($statement);

if (! empty($sql))
{
$sql = ' ' . trim($sql);
}

return $sql;
}

//--------------------------------------------------------------------

/**
Expand Down Expand Up @@ -98,6 +133,8 @@ public function orderBy(string $orderBy, string $direction = '', bool $escape =
* @param string $column
* @param integer $value
*
* @throws DatabaseException
*
* @return mixed
*/
public function increment(string $column, int $value = 1)
Expand All @@ -117,6 +154,8 @@ public function increment(string $column, int $value = 1)
* @param string $column
* @param integer $value
*
* @throws DatabaseException
*
* @return mixed
*/
public function decrement(string $column, int $value = 1)
Expand Down
9 changes: 7 additions & 2 deletions system/Database/SQLite3/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ class Builder extends BaseBuilder
*/
protected $canLimitWhereUpdates = false;

/**
* @var array
*/
protected $supportedIgnoreStatements = [
'insert' => 'OR IGNORE',
];

//--------------------------------------------------------------------

/**
Expand Down Expand Up @@ -106,6 +113,4 @@ protected function _truncate(string $table): string
return 'DELETE FROM ' . $table;
}

//--------------------------------------------------------------------

}
1 change: 0 additions & 1 deletion tests/system/Database/Builder/DeleteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,4 @@ public function testDelete()
$this->assertEquals($expectedBinds, $builder->getBinds());
}

//--------------------------------------------------------------------
}
16 changes: 16 additions & 0 deletions user_guide_src/source/database/query_builder.rst
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,22 @@ The first parameter is an object.

.. note:: All values are escaped automatically producing safer queries.

**$builder->ignore()**

Generates an insert ignore string based on the data you supply, and runs the
query. So if an entry with the same primary key already exists, the query won't be inserted.
You can optionally pass an **boolean** to the function. Here is an example using the array of the above example::

$data = [
'title' => 'My title',
'name' => 'My Name',
'date' => 'My date'
];

$builder->ignore(true)->insert($data);
// Produces: INSERT OR IGNORE INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date')


**$builder->getCompiledInsert()**

Compiles the insertion query just like $builder->insert() but does not
Expand Down