First Local Commit - After Clean up.
Signed-off-by: Rick Hays <rhays@haysgang.com>
This commit is contained in:
111
system/Database/SQLite3/Builder.php
Normal file
111
system/Database/SQLite3/Builder.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
|
||||
/**
|
||||
* Builder for SQLite3
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escapeChar = '`';
|
||||
|
||||
/**
|
||||
* Default installs of SQLite typically do not
|
||||
* support limiting delete clauses.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $canLimitDeletes = false;
|
||||
|
||||
/**
|
||||
* Default installs of SQLite do no support
|
||||
* limiting update queries in combo with WHERE.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $canLimitWhereUpdates = false;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Replace statement
|
||||
*
|
||||
* Generates a platform-specific replace string from the supplied data
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param array $keys the insert keys
|
||||
* @param array $values the insert values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _replace(string $table, array $keys, array $values): string
|
||||
{
|
||||
return 'INSERT OR ' . parent::_replace($table, $keys, $values);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Truncate statement
|
||||
*
|
||||
* Generates a platform-specific truncate string from the supplied data
|
||||
*
|
||||
* If the database does not support the TRUNCATE statement,
|
||||
* then this method maps to 'DELETE FROM table'
|
||||
*
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
protected function _truncate(string $table): string
|
||||
{
|
||||
return 'DELETE FROM ' . $table;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
542
system/Database/SQLite3/Connection.php
Normal file
542
system/Database/SQLite3/Connection.php
Normal file
@@ -0,0 +1,542 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Connection for SQLite3
|
||||
*/
|
||||
class Connection extends BaseConnection implements ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Database driver
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $DBDriver = 'SQLite3';
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ORDER BY random keyword
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_random_keyword = [
|
||||
'RANDOM()',
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function connect(bool $persistent = false)
|
||||
{
|
||||
if ($persistent && $this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('SQLite3 doesn\'t support persistent connections.');
|
||||
}
|
||||
try
|
||||
{
|
||||
return (! $this->password)
|
||||
? new \SQLite3($this->database)
|
||||
: new \SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
throw new DatabaseException('SQLite3 error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Keep or establish the connection if no queries have been sent for
|
||||
* a length of time exceeding the server's idle timeout.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reconnect()
|
||||
{
|
||||
$this->close();
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _close()
|
||||
{
|
||||
$this->connID->close();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Select a specific database table to use.
|
||||
*
|
||||
* @param string $databaseName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setDatabase(string $databaseName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a string containing the version of the database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
if (isset($this->dataCache['version']))
|
||||
{
|
||||
return $this->dataCache['version'];
|
||||
}
|
||||
|
||||
$version = \SQLite3::version();
|
||||
|
||||
return $this->dataCache['version'] = $version['versionString'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute the query
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed \SQLite3Result object or bool
|
||||
*/
|
||||
public function execute(string $sql)
|
||||
{
|
||||
return $this->isWriteType($sql)
|
||||
? $this->connID->exec($sql)
|
||||
: $this->connID->query($sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the total number of rows affected by this query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function affectedRows(): int
|
||||
{
|
||||
return $this->connID->changes();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform-dependant string escape
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _escapeString(string $str): string
|
||||
{
|
||||
return $this->connID->escapeString($str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates the SQL for listing tables in a platform-dependent manner.
|
||||
*
|
||||
* @param boolean $prefixLimit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listTables(bool $prefixLimit = false): string
|
||||
{
|
||||
return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\''
|
||||
. ' AND "NAME" NOT LIKE \'sqlite!_%\' ESCAPE \'!\''
|
||||
. (($prefixLimit !== false && $this->DBPrefix !== '')
|
||||
? ' AND "NAME" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . '%\' ' . sprintf($this->likeEscapeStr,
|
||||
$this->likeEscapeChar)
|
||||
: '');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates a platform-specific query string so that the column names can be fetched.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listColumns(string $table = ''): string
|
||||
{
|
||||
return 'PRAGMA TABLE_INFO(' . $this->protectIdentifiers($table, true, null, false) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch Field Names
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return array|false
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function getFieldNames(string $table)
|
||||
{
|
||||
// Is there a cached result?
|
||||
if (isset($this->dataCache['field_names'][$table]))
|
||||
{
|
||||
return $this->dataCache['field_names'][$table];
|
||||
}
|
||||
|
||||
if (empty($this->connID))
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if (false === ($sql = $this->_listColumns($table)))
|
||||
{
|
||||
if ($this->DBDebug)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.featureUnavailable'));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->query($sql);
|
||||
$this->dataCache['field_names'][$table] = [];
|
||||
|
||||
foreach ($query->getResultArray() as $row)
|
||||
{
|
||||
// Do we know from where to get the column's name?
|
||||
if (! isset($key))
|
||||
{
|
||||
if (isset($row['column_name']))
|
||||
{
|
||||
$key = 'column_name';
|
||||
}
|
||||
elseif (isset($row['COLUMN_NAME']))
|
||||
{
|
||||
$key = 'COLUMN_NAME';
|
||||
}
|
||||
elseif (isset($row['name']))
|
||||
{
|
||||
$key = 'name';
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have no other choice but to just get the first element's key.
|
||||
$key = key($row);
|
||||
}
|
||||
}
|
||||
|
||||
$this->dataCache['field_names'][$table][] = $row[$key];
|
||||
}
|
||||
|
||||
return $this->dataCache['field_names'][$table];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with field data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _fieldData(string $table): array
|
||||
{
|
||||
if (($query = $this->query('PRAGMA TABLE_INFO(' . $this->protectIdentifiers($table, true, null,
|
||||
false) . ')')) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
if (empty($query))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
$retVal = [];
|
||||
for ($i = 0, $c = count($query); $i < $c; $i++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $query[$i]->name;
|
||||
$retVal[$i]->type = $query[$i]->type;
|
||||
$retVal[$i]->max_length = null;
|
||||
$retVal[$i]->default = $query[$i]->dflt_value;
|
||||
$retVal[$i]->primary_key = isset($query[$i]->pk) ? (bool)$query[$i]->pk : false;
|
||||
$retVal[$i]->nullable = isset($query[$i]->notnull) ? ! (bool)$query[$i]->notnull : false;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with index data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _indexData(string $table): array
|
||||
{
|
||||
// Get indexes
|
||||
// Don't use PRAGMA index_list, so we can preserve index order
|
||||
$sql = "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=" . $this->escape(strtolower($table));
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->name = $row->name;
|
||||
|
||||
// Get fields for index
|
||||
$obj->fields = [];
|
||||
if (($fields = $this->query('PRAGMA index_info(' . $this->escape(strtolower($row->name)) . ')')) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
$fields = $fields->getResultObject();
|
||||
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
$obj->fields[] = $field->name;
|
||||
}
|
||||
|
||||
$retVal[$obj->name] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with Foreign key data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function _foreignKeyData(string $table): array
|
||||
{
|
||||
if ($this->supportsForeignKeys() !== true)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$tables = $this->listTables();
|
||||
|
||||
if (empty($tables))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$retVal = [];
|
||||
|
||||
foreach ($tables as $table)
|
||||
{
|
||||
$query = $this->query("PRAGMA foreign_key_list({$table})")->getResult();
|
||||
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->constraint_name = $row->from . ' to ' . $row->table . '.' . $row->to;
|
||||
$obj->table_name = $table;
|
||||
$obj->foreign_table_name = $row->table;
|
||||
$obj->sequence = $row->seq;
|
||||
|
||||
$retVal[] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to disable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _disableForeignKeyChecks()
|
||||
{
|
||||
return 'PRAGMA foreign_keys = OFF';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to enable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _enableForeignKeyChecks()
|
||||
{
|
||||
return 'PRAGMA foreign_keys = ON';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error code and message.
|
||||
*
|
||||
* Must return an array with keys 'code' and 'message':
|
||||
*
|
||||
* return ['code' => null, 'message' => null);
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function error(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->connID->lastErrorCode(),
|
||||
'message' => $this->connID->lastErrorMsg(),
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Insert ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function insertID(): int
|
||||
{
|
||||
return $this->connID->lastInsertRowID();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transBegin(): bool
|
||||
{
|
||||
return $this->connID->exec('BEGIN TRANSACTION');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Commit Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transCommit(): bool
|
||||
{
|
||||
return $this->connID->exec('END TRANSACTION');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rollback Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transRollback(): bool
|
||||
{
|
||||
return $this->connID->exec('ROLLBACK');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the statement is a write-type query or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriteType($sql): bool
|
||||
{
|
||||
return (bool)preg_match(
|
||||
'/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i',
|
||||
$sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks to see if the current install supports Foreign Keys
|
||||
* and has them enabled.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportsForeignKeys(): bool
|
||||
{
|
||||
$result = $this->simpleQuery('PRAGMA foreign_keys');
|
||||
|
||||
return (bool)$result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
333
system/Database/SQLite3/Forge.php
Normal file
333
system/Database/SQLite3/Forge.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Forge for SQLite3
|
||||
*/
|
||||
class Forge extends \CodeIgniter\Database\Forge
|
||||
{
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
*
|
||||
* @var boolean|array
|
||||
*/
|
||||
protected $_unsigned = false;
|
||||
|
||||
/**
|
||||
* NULL value representation in CREATE/ALTER TABLE statements
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_null = 'NULL';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param $db ConnectionInterface
|
||||
*/
|
||||
public function __construct(ConnectionInterface $db)
|
||||
{
|
||||
parent::__construct($db);
|
||||
|
||||
if (version_compare($this->db->getVersion(), '3.3', '<'))
|
||||
{
|
||||
$this->createTableIfStr = false;
|
||||
$this->dropTableIfStr = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create database
|
||||
*
|
||||
* @param string $dbName
|
||||
* @param boolean $ifNotExists Whether to add IF NOT EXISTS condition
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function createDatabase(string $dbName, bool $ifNotExists = false): bool
|
||||
{
|
||||
// In SQLite, a database is created when you connect to the database.
|
||||
// We'll return TRUE so that an error isn't generated.
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Drop database
|
||||
*
|
||||
* @param string $dbName
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function dropDatabase(string $dbName): bool
|
||||
{
|
||||
// In SQLite, a database is dropped when we delete a file
|
||||
if (! is_file($dbName))
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unable to drop the specified database.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to close the pseudo-connection first
|
||||
$this->db->close();
|
||||
if (! @unlink($dbName))
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unable to drop the specified database.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! empty($this->db->dataCache['db_names']))
|
||||
{
|
||||
$key = array_search(strtolower($dbName), array_map('strtolower', $this->db->dataCache['db_names']), true);
|
||||
if ($key !== false)
|
||||
{
|
||||
unset($this->db->dataCache['db_names'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @param string $alter_type ALTER type
|
||||
* @param string $table Table name
|
||||
* @param mixed $field Column definition
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
protected function _alterTable(string $alter_type, string $table, $field)
|
||||
{
|
||||
switch ($alter_type)
|
||||
{
|
||||
case 'DROP':
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
$sqlTable->fromTable($table)
|
||||
->dropColumn($field)
|
||||
->run();
|
||||
|
||||
return '';
|
||||
break;
|
||||
case 'CHANGE':
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
$sqlTable->fromTable($table)
|
||||
->modifyColumn($field)
|
||||
->run();
|
||||
|
||||
return null;
|
||||
break;
|
||||
default:
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*
|
||||
* @param array $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _processColumn(array $field): string
|
||||
{
|
||||
if ($field['type'] === 'TEXT' && strpos($field['length'], "('") === 0)
|
||||
{
|
||||
$field['type'] .= ' CHECK(' . $this->db->escapeIdentifiers($field['name'])
|
||||
. ' IN ' . $field['length'] . ')';
|
||||
}
|
||||
|
||||
return $this->db->escapeIdentifiers($field['name'])
|
||||
. ' ' . $field['type']
|
||||
. $field['auto_increment']
|
||||
. $field['null']
|
||||
. $field['unique']
|
||||
. $field['default'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process indexes
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _processIndexes(string $table): array
|
||||
{
|
||||
$sqls = [];
|
||||
|
||||
for ($i = 0, $c = count($this->keys); $i < $c; $i++)
|
||||
{
|
||||
$this->keys[$i] = (array)$this->keys[$i];
|
||||
|
||||
for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
|
||||
{
|
||||
if (! isset($this->fields[$this->keys[$i][$i2]]))
|
||||
{
|
||||
unset($this->keys[$i][$i2]);
|
||||
}
|
||||
}
|
||||
if (count($this->keys[$i]) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($i, $this->uniqueKeys))
|
||||
{
|
||||
$sqls[] = 'CREATE UNIQUE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]))
|
||||
. ' ON ' . $this->db->escapeIdentifiers($table)
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');';
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqls[] = 'CREATE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]))
|
||||
. ' ON ' . $this->db->escapeIdentifiers($table)
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');';
|
||||
}
|
||||
|
||||
return $sqls;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Field attribute TYPE
|
||||
*
|
||||
* Performs a data type mapping between different databases.
|
||||
*
|
||||
* @param array &$attributes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeType(array &$attributes)
|
||||
{
|
||||
switch (strtoupper($attributes['TYPE']))
|
||||
{
|
||||
case 'ENUM':
|
||||
case 'SET':
|
||||
$attributes['TYPE'] = 'TEXT';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Field attribute AUTO_INCREMENT
|
||||
*
|
||||
* @param array &$attributes
|
||||
* @param array &$field
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeAutoIncrement(array &$attributes, array &$field)
|
||||
{
|
||||
if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true
|
||||
&& stripos($field['type'], 'int') !== false)
|
||||
{
|
||||
$field['type'] = 'INTEGER PRIMARY KEY';
|
||||
$field['default'] = '';
|
||||
$field['null'] = '';
|
||||
$field['unique'] = '';
|
||||
$field['auto_increment'] = ' AUTOINCREMENT';
|
||||
|
||||
$this->primaryKeys = [];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Foreign Key Drop
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param string $foreignName Foreign name
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function dropForeignKey(string $table, string $foreignName): bool
|
||||
{
|
||||
// If this version of SQLite doesn't support it, we're done here
|
||||
if ($this->db->supportsForeignKeys() !== true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise we have to copy the table and recreate
|
||||
// without the foreign key being involved now
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
return $sqlTable->fromTable($this->db->DBPrefix . $table)
|
||||
->dropForeignKey($foreignName)
|
||||
->run();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
143
system/Database/SQLite3/PreparedQuery.php
Normal file
143
system/Database/SQLite3/PreparedQuery.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\PreparedQueryInterface;
|
||||
use CodeIgniter\Database\BasePreparedQuery;
|
||||
|
||||
/**
|
||||
* Prepared query for SQLite3
|
||||
*/
|
||||
|
||||
class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* The SQLite3Result resource, or false.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* NOTE: This version is based on SQL code. Child classes should
|
||||
* override this method.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
* Unused in the MySQLi driver.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _prepare(string $sql, array $options = [])
|
||||
{
|
||||
if (! ($this->statement = $this->db->connID->prepare($sql)))
|
||||
{
|
||||
$this->errorCode = $this->db->connID->lastErrorCode();
|
||||
$this->errorString = $this->db->connID->lastErrorMsg();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @todo finalize()
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function _execute(array $data): bool
|
||||
{
|
||||
if (is_null($this->statement))
|
||||
{
|
||||
throw new \BadMethodCallException('You must call prepare before trying to execute a prepared statement.');
|
||||
}
|
||||
|
||||
foreach ($data as $key => $item)
|
||||
{
|
||||
// Determine the type string
|
||||
if (is_integer($item))
|
||||
{
|
||||
$bindType = SQLITE3_INTEGER;
|
||||
}
|
||||
elseif (is_float($item))
|
||||
{
|
||||
$bindType = SQLITE3_FLOAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bindType = SQLITE3_TEXT;
|
||||
}
|
||||
|
||||
// Bind it
|
||||
$this->statement->bindValue($key + 1, $item, $bindType);
|
||||
}
|
||||
|
||||
$this->result = $this->statement->execute();
|
||||
|
||||
return $this->result !== false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
205
system/Database/SQLite3/Result.php
Normal file
205
system/Database/SQLite3/Result.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Database\ResultInterface;
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
/**
|
||||
* Result for SQLite3
|
||||
*/
|
||||
class Result extends BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return $this->resultID->numColumns();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
$fieldNames = [];
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$fieldNames[] = $this->resultID->columnName($i);
|
||||
}
|
||||
|
||||
return $fieldNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array
|
||||
{
|
||||
static $data_types = [
|
||||
SQLITE3_INTEGER => 'integer',
|
||||
SQLITE3_FLOAT => 'float',
|
||||
SQLITE3_TEXT => 'text',
|
||||
SQLITE3_BLOB => 'blob',
|
||||
SQLITE3_NULL => 'null',
|
||||
];
|
||||
|
||||
$retVal = [];
|
||||
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $this->resultID->columnName($i);
|
||||
$type = $this->resultID->columnType($i);
|
||||
$retVal[$i]->type = isset($data_types[$type]) ? $data_types[$type] : $type;
|
||||
$retVal[$i]->max_length = null;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult()
|
||||
{
|
||||
if (is_object($this->resultID))
|
||||
{
|
||||
$this->resultID->finalize();
|
||||
$this->resultID = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the desired offset. This is called
|
||||
* internally before fetching results to make sure the result set
|
||||
* starts at zero.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function dataSeek(int $n = 0)
|
||||
{
|
||||
if ($n !== 0)
|
||||
{
|
||||
throw new DatabaseException('SQLite3 doesn\'t support seeking to other offset.');
|
||||
}
|
||||
|
||||
return $this->resultID->reset();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchAssoc()
|
||||
{
|
||||
return $this->resultID->fetchArray(SQLITE3_ASSOC);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object|boolean
|
||||
*/
|
||||
protected function fetchObject(string $className = 'stdClass')
|
||||
{
|
||||
// No native support for fetching rows as objects
|
||||
if (($row = $this->fetchAssoc()) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif ($className === 'stdClass')
|
||||
{
|
||||
return (object) $row;
|
||||
}
|
||||
|
||||
$classObj = new $className();
|
||||
|
||||
if (is_subclass_of($className, Entity::class))
|
||||
{
|
||||
return $classObj->setAttributes($row);
|
||||
}
|
||||
|
||||
$classSet = \Closure::bind(function ($key, $value) {
|
||||
$this->$key = $value;
|
||||
}, $classObj, $className
|
||||
);
|
||||
foreach (array_keys($row) as $key)
|
||||
{
|
||||
$classSet($key, $row[$key]);
|
||||
}
|
||||
return $classObj;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
426
system/Database/SQLite3/Table.php
Normal file
426
system/Database/SQLite3/Table.php
Normal file
@@ -0,0 +1,426 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DataException;
|
||||
|
||||
/**
|
||||
* Class Table
|
||||
*
|
||||
* Provides missing features for altering tables that are common
|
||||
* in other supported databases, but are missing from SQLite.
|
||||
* These are needed in order to support migrations during testing
|
||||
* when another database is used as the primary engine, but
|
||||
* SQLite in memory databases are used for faster test execution.
|
||||
*
|
||||
* @package CodeIgniter\Database\SQLite3
|
||||
*/
|
||||
class Table
|
||||
{
|
||||
/**
|
||||
* All of the fields this table represents.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = [];
|
||||
|
||||
/**
|
||||
* All of the unique/primary keys in the table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $keys = [];
|
||||
|
||||
/**
|
||||
* All of the foreign keys in the table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $foreignKeys = [];
|
||||
|
||||
/**
|
||||
* The name of the table we're working with.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
/**
|
||||
* The name of the table, with database prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefixedTableName;
|
||||
|
||||
/**
|
||||
* Database connection.
|
||||
*
|
||||
* @var Connection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Handle to our forge.
|
||||
*
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
/**
|
||||
* Table constructor.
|
||||
*
|
||||
* @param Connection $db
|
||||
* @param Forge $forge
|
||||
*/
|
||||
public function __construct(Connection $db, Forge $forge)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->forge = $forge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an existing database table and
|
||||
* collects all of the information needed to
|
||||
* recreate this table.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function fromTable(string $table)
|
||||
{
|
||||
$this->prefixedTableName = $table;
|
||||
|
||||
// Remove the prefix, if any, since it's
|
||||
// already been added by the time we get here...
|
||||
$prefix = $this->db->DBPrefix;
|
||||
if (! empty($prefix))
|
||||
{
|
||||
if (strpos($table, $prefix) === 0)
|
||||
{
|
||||
$table = substr($table, strlen($prefix));
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->db->tableExists($this->prefixedTableName))
|
||||
{
|
||||
throw DataException::forTableNotFound($this->prefixedTableName);
|
||||
}
|
||||
|
||||
$this->tableName = $table;
|
||||
|
||||
$this->fields = $this->formatFields($this->db->getFieldData($table));
|
||||
|
||||
$this->keys = array_merge($this->keys, $this->formatKeys($this->db->getIndexData($table)));
|
||||
|
||||
$this->foreignKeys = $this->db->getForeignKeyData($table);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after `fromTable` and any actions, like `dropColumn`, etc,
|
||||
* to finalize the action. It creates a temp table, creates the new
|
||||
* table with modifications, and copies the data over to the new table.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function run(): bool
|
||||
{
|
||||
$this->db->query('PRAGMA foreign_keys = OFF');
|
||||
|
||||
$this->db->transStart();
|
||||
|
||||
$this->forge->renameTable($this->tableName, "temp_{$this->tableName}");
|
||||
|
||||
$this->forge->reset();
|
||||
|
||||
$this->createTable();
|
||||
|
||||
$this->copyData();
|
||||
|
||||
$this->forge->dropTable("temp_{$this->tableName}");
|
||||
|
||||
$success = $this->db->transComplete();
|
||||
|
||||
$this->db->query('PRAGMA foreign_keys = ON');
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a column from the table.
|
||||
*
|
||||
* @param string $column
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function dropColumn(string $column)
|
||||
{
|
||||
unset($this->fields[$column]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a field, including changing data type,
|
||||
* renaming, etc.
|
||||
*
|
||||
* @param array $field
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function modifyColumn(array $field)
|
||||
{
|
||||
$field = $field[0];
|
||||
|
||||
$oldName = $field['name'];
|
||||
unset($field['name']);
|
||||
|
||||
$this->fields[$oldName] = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a foreign key from this table so that
|
||||
* it won't be recreated in the future.
|
||||
*
|
||||
* @param string $column
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function dropForeignKey(string $column)
|
||||
{
|
||||
if (empty($this->foreignKeys))
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($this->foreignKeys); $i++)
|
||||
{
|
||||
if ($this->foreignKeys[$i]->table_name !== $this->tableName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The column name should be the first thing in the constraint name
|
||||
if (strpos($this->foreignKeys[$i]->constraint_name, $column) !== 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($this->foreignKeys[$i]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the new table based on our current fields.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function createTable()
|
||||
{
|
||||
$this->dropIndexes();
|
||||
$this->db->resetDataCache();
|
||||
|
||||
// Handle any modified columns.
|
||||
$fields = [];
|
||||
foreach ($this->fields as $name => $field)
|
||||
{
|
||||
if (isset($field['new_name']))
|
||||
{
|
||||
$fields[$field['new_name']] = $field;
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields[$name] = $field;
|
||||
}
|
||||
|
||||
$this->forge->addField($fields);
|
||||
|
||||
// Unique/Index keys
|
||||
if (is_array($this->keys))
|
||||
{
|
||||
foreach ($this->keys as $key)
|
||||
{
|
||||
switch ($key['type'])
|
||||
{
|
||||
case 'primary':
|
||||
$this->forge->addPrimaryKey($key['fields']);
|
||||
break;
|
||||
case 'unique':
|
||||
$this->forge->addUniqueKey($key['fields']);
|
||||
break;
|
||||
case 'index':
|
||||
$this->forge->addKey($key['fields']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Foreign Keys
|
||||
|
||||
return $this->forge->createTable($this->tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies data from our old table to the new one,
|
||||
* taking care map data correctly based on any columns
|
||||
* that have been renamed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function copyData()
|
||||
{
|
||||
$exFields = [];
|
||||
$newFields = [];
|
||||
|
||||
foreach ($this->fields as $name => $details)
|
||||
{
|
||||
// Are we modifying the column?
|
||||
if (isset($details['new_name']))
|
||||
{
|
||||
$newFields[] = $details['new_name'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$newFields[] = $name;
|
||||
}
|
||||
|
||||
$exFields[] = $name;
|
||||
}
|
||||
|
||||
$exFields = implode(', ', $exFields);
|
||||
$newFields = implode(', ', $newFields);
|
||||
|
||||
$this->db->query("INSERT INTO {$this->prefixedTableName}({$newFields}) SELECT {$exFields} FROM {$this->db->DBPrefix}temp_{$this->tableName}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts fields retrieved from the database to
|
||||
* the format needed for creating fields with Forge.
|
||||
*
|
||||
* @param array|boolean $fields
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function formatFields($fields)
|
||||
{
|
||||
if (! is_array($fields))
|
||||
{
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
$return[$field->name] = [
|
||||
'type' => $field->type,
|
||||
'default' => $field->default,
|
||||
'nullable' => $field->nullable,
|
||||
];
|
||||
|
||||
if ($field->primary_key)
|
||||
{
|
||||
$this->keys[$field->name] = [
|
||||
'fields' => [$field->name],
|
||||
'type' => 'primary',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts keys retrieved from the database to
|
||||
* the format needed to create later.
|
||||
*
|
||||
* @param mixed $keys
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function formatKeys($keys)
|
||||
{
|
||||
if (! is_array($keys))
|
||||
{
|
||||
return $keys;
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
foreach ($keys as $name => $key)
|
||||
{
|
||||
$return[$name] = [
|
||||
'fields' => $key->fields,
|
||||
'type' => 'index',
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to drop all indexes and constraints
|
||||
* from the database for this table.
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
protected function dropIndexes()
|
||||
{
|
||||
if (! is_array($this->keys) || ! count($this->keys))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->keys as $name => $key)
|
||||
{
|
||||
if ($key['type'] === 'primary' || $key['type'] === 'unique')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->query("DROP INDEX IF EXISTS '{$name}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
73
system/Database/SQLite3/Utils.php
Normal file
73
system/Database/SQLite3/Utils.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Utils for SQLite3
|
||||
*/
|
||||
class Utils extends BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = 'REINDEX %s';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
* @param array|null $prefs
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _backup(array $prefs = null)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
Reference in New Issue
Block a user