First Local Commit - After Clean up.
Signed-off-by: Rick Hays <rhays@haysgang.com>
This commit is contained in:
3352
system/Database/BaseBuilder.php
Normal file
3352
system/Database/BaseBuilder.php
Normal file
File diff suppressed because it is too large
Load Diff
1882
system/Database/BaseConnection.php
Normal file
1882
system/Database/BaseConnection.php
Normal file
File diff suppressed because it is too large
Load Diff
274
system/Database/BasePreparedQuery.php
Normal file
274
system/Database/BasePreparedQuery.php
Normal file
@@ -0,0 +1,274 @@
|
||||
<?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;
|
||||
|
||||
use CodeIgniter\Database\MySQLi\Connection;
|
||||
use CodeIgniter\Events\Events;
|
||||
|
||||
/**
|
||||
* Base prepared query
|
||||
*/
|
||||
abstract class BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* The prepared statement itself.
|
||||
*
|
||||
* @var resource|\mysqli_stmt
|
||||
*/
|
||||
protected $statement;
|
||||
|
||||
/**
|
||||
* The error code, if any.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $errorCode;
|
||||
|
||||
/**
|
||||
* The error message, if any.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $errorString;
|
||||
|
||||
/**
|
||||
* Holds the prepared query object
|
||||
* that is cloned during execute.
|
||||
*
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* A reference to the db connection to use.
|
||||
*
|
||||
* @var BaseConnection|MySQLi\Connection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \CodeIgniter\Database\ConnectionInterface $db
|
||||
*/
|
||||
public function __construct(ConnectionInterface $db)
|
||||
{
|
||||
$this->db = &$db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param string $queryClass
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function prepare(string $sql, array $options = [], string $queryClass = 'CodeIgniter\\Database\\Query')
|
||||
{
|
||||
// We only supports positional placeholders (?)
|
||||
// in order to work with the execute method below, so we
|
||||
// need to replace our named placeholders (:name)
|
||||
$sql = preg_replace('/:[^\s,)]+/', '?', $sql);
|
||||
|
||||
/**
|
||||
* @var \CodeIgniter\Database\Query $query
|
||||
*/
|
||||
$query = new $queryClass($this->db);
|
||||
|
||||
$query->setQuery($sql);
|
||||
|
||||
if (! empty($this->db->swapPre) && ! empty($this->db->DBPrefix))
|
||||
{
|
||||
$query->swapPrefix($this->db->DBPrefix, $this->db->swapPre);
|
||||
}
|
||||
|
||||
$this->query = $query;
|
||||
|
||||
return $this->_prepare($query->getOriginalQuery(), $options);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The database-dependent portion of the prepare statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function _prepare(string $sql, array $options = []);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return ResultInterface
|
||||
*/
|
||||
public function execute(...$data)
|
||||
{
|
||||
// Execute the Query.
|
||||
$startTime = microtime(true);
|
||||
|
||||
$result = $this->_execute($data);
|
||||
|
||||
// Update our query object
|
||||
$query = clone $this->query;
|
||||
$query->setBinds($data);
|
||||
|
||||
$query->setDuration($startTime);
|
||||
|
||||
// Let others do something with this query
|
||||
Events::trigger('DBQuery', $query);
|
||||
|
||||
// Return a result object
|
||||
$resultClass = str_replace('PreparedQuery', 'Result', get_class($this));
|
||||
|
||||
$resultID = $this->_getResult();
|
||||
|
||||
return new $resultClass($this->db->connID, $resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The database dependant version of the execute method.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function _execute(array $data): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function _getResult();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Explicitly closes the statement.
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (! is_object($this->statement))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->statement->close();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the SQL that has been prepared.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString(): string
|
||||
{
|
||||
if (! $this->query instanceof QueryInterface)
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot call getQueryString on a prepared query until after the query has been prepared.');
|
||||
}
|
||||
|
||||
return $this->query->getQuery();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A helper to determine if any error exists.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasError(): bool
|
||||
{
|
||||
return ! empty($this->errorString);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int
|
||||
{
|
||||
return $this->errorCode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string
|
||||
{
|
||||
return $this->errorString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
629
system/Database/BaseResult.php
Normal file
629
system/Database/BaseResult.php
Normal file
@@ -0,0 +1,629 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Class BaseResult
|
||||
*/
|
||||
abstract class BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Connection ID
|
||||
*
|
||||
* @var resource|object
|
||||
*/
|
||||
public $connID;
|
||||
|
||||
/**
|
||||
* Result ID
|
||||
*
|
||||
* @var resource|object
|
||||
*/
|
||||
public $resultID;
|
||||
|
||||
/**
|
||||
* Result Array
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
public $resultArray = [];
|
||||
|
||||
/**
|
||||
* Result Object
|
||||
*
|
||||
* @var object[]
|
||||
*/
|
||||
public $resultObject = [];
|
||||
|
||||
/**
|
||||
* Custom Result Object
|
||||
*
|
||||
* @var object[]
|
||||
*/
|
||||
public $customResultObject = [];
|
||||
|
||||
/**
|
||||
* Current Row index
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $currentRow = 0;
|
||||
|
||||
/**
|
||||
* Number of rows
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $numRows;
|
||||
|
||||
/**
|
||||
* Row data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $rowData;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param object|resource $connID
|
||||
* @param object|resource $resultID
|
||||
*/
|
||||
public function __construct(&$connID, &$resultID)
|
||||
{
|
||||
$this->connID = $connID;
|
||||
$this->resultID = $resultID;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Retrieve the results of the query. Typically an array of
|
||||
* individual data rows, which can be either an 'array', an
|
||||
* 'object', or a custom class name.
|
||||
*
|
||||
* @param string $type The row type. Either 'array', 'object', or a class name to use
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResult(string $type = 'object'): array
|
||||
{
|
||||
if ($type === 'array')
|
||||
{
|
||||
return $this->getResultArray();
|
||||
}
|
||||
elseif ($type === 'object')
|
||||
{
|
||||
return $this->getResultObject();
|
||||
}
|
||||
|
||||
return $this->getCustomResultObject($type);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of custom objects.
|
||||
*
|
||||
* @param string $className The name of the class to use.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomResultObject(string $className)
|
||||
{
|
||||
if (isset($this->customResultObject[$className]))
|
||||
{
|
||||
return $this->customResultObject[$className];
|
||||
}
|
||||
|
||||
if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
// Don't fetch the result set again if we already have it
|
||||
$_data = null;
|
||||
if (($c = count($this->resultArray)) > 0)
|
||||
{
|
||||
$_data = 'result_array';
|
||||
}
|
||||
elseif (($c = count($this->resultObject)) > 0)
|
||||
{
|
||||
$_data = 'result_object';
|
||||
}
|
||||
|
||||
if ($_data !== null)
|
||||
{
|
||||
for ($i = 0; $i < $c; $i ++)
|
||||
{
|
||||
$this->customResultObject[$className][$i] = new $className();
|
||||
|
||||
foreach ($this->{$_data}[$i] as $key => $value)
|
||||
{
|
||||
$this->customResultObject[$className][$i]->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->customResultObject[$className];
|
||||
}
|
||||
|
||||
is_null($this->rowData) || $this->dataSeek(0);
|
||||
$this->customResultObject[$className] = [];
|
||||
|
||||
while ($row = $this->fetchObject($className))
|
||||
{
|
||||
if (method_exists($row, 'syncOriginal'))
|
||||
{
|
||||
$row->syncOriginal();
|
||||
}
|
||||
|
||||
$this->customResultObject[$className][] = $row;
|
||||
}
|
||||
|
||||
return $this->customResultObject[$className];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of arrays.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultArray(): array
|
||||
{
|
||||
if (! empty($this->resultArray))
|
||||
{
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
// In the event that query caching is on, the result_id variable
|
||||
// will not be a valid resource so we'll simply return an empty
|
||||
// array.
|
||||
if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($this->resultObject)
|
||||
{
|
||||
foreach ($this->resultObject as $row)
|
||||
{
|
||||
$this->resultArray[] = (array) $row;
|
||||
}
|
||||
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
is_null($this->rowData) || $this->dataSeek(0);
|
||||
while ($row = $this->fetchAssoc())
|
||||
{
|
||||
$this->resultArray[] = $row;
|
||||
}
|
||||
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of objects.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultObject(): array
|
||||
{
|
||||
if (! empty($this->resultObject))
|
||||
{
|
||||
return $this->resultObject;
|
||||
}
|
||||
|
||||
// In the event that query caching is on, the result_id variable
|
||||
// will not be a valid resource so we'll simply return an empty
|
||||
// array.
|
||||
if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($this->resultArray)
|
||||
{
|
||||
foreach ($this->resultArray as $row)
|
||||
{
|
||||
$this->resultObject[] = (object) $row;
|
||||
}
|
||||
|
||||
return $this->resultObject;
|
||||
}
|
||||
|
||||
is_null($this->rowData) || $this->dataSeek(0);
|
||||
while ($row = $this->fetchObject())
|
||||
{
|
||||
if (method_exists($row, 'syncOriginal'))
|
||||
{
|
||||
$row->syncOriginal();
|
||||
}
|
||||
|
||||
$this->resultObject[] = $row;
|
||||
}
|
||||
|
||||
return $this->resultObject;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wrapper object to return a row as either an array, an object, or
|
||||
* a custom class.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param mixed $n The index of the results to return
|
||||
* @param string $type The type of result object. 'array', 'object' or class name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRow($n = 0, string $type = 'object')
|
||||
{
|
||||
if (! is_numeric($n))
|
||||
{
|
||||
// We cache the row data for subsequent uses
|
||||
is_array($this->rowData) || $this->rowData = $this->getRowArray(0);
|
||||
|
||||
// array_key_exists() instead of isset() to allow for NULL values
|
||||
if (empty($this->rowData) || ! array_key_exists($n, $this->rowData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->rowData[$n];
|
||||
}
|
||||
|
||||
if ($type === 'object')
|
||||
{
|
||||
return $this->getRowObject($n);
|
||||
}
|
||||
elseif ($type === 'array')
|
||||
{
|
||||
return $this->getRowArray($n);
|
||||
}
|
||||
|
||||
return $this->getCustomRowObject($n, $type);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a row as a custom class instance.
|
||||
*
|
||||
* If row doesn't exists, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
* @param string $className
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomRowObject(int $n, string $className)
|
||||
{
|
||||
isset($this->customResultObject[$className]) || $this->getCustomResultObject($className);
|
||||
|
||||
if (empty($this->customResultObject[$className]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($n !== $this->currentRow && isset($this->customResultObject[$className][$n]))
|
||||
{
|
||||
$this->currentRow = $n;
|
||||
}
|
||||
|
||||
return $this->customResultObject[$className][$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an array.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowArray(int $n = 0)
|
||||
{
|
||||
$result = $this->getResultArray();
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($n !== $this->currentRow && isset($result[$n]))
|
||||
{
|
||||
$this->currentRow = $n;
|
||||
}
|
||||
|
||||
return $result[$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an object.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowObject(int $n = 0)
|
||||
{
|
||||
$result = $this->getResultObject();
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($n !== $this->customResultObject && isset($result[$n]))
|
||||
{
|
||||
$this->currentRow = $n;
|
||||
}
|
||||
|
||||
return $result[$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assigns an item into a particular column slot.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setRow($key, $value = null)
|
||||
{
|
||||
// We cache the row data for subsequent uses
|
||||
if (! is_array($this->rowData))
|
||||
{
|
||||
$this->rowData = $this->getRowArray(0);
|
||||
}
|
||||
|
||||
if (is_array($key))
|
||||
{
|
||||
foreach ($key as $k => $v)
|
||||
{
|
||||
$this->rowData[$k] = $v;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($key !== '' && $value !== null)
|
||||
{
|
||||
$this->rowData[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "first" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
|
||||
return (empty($result)) ? null : $result[0];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "last" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
|
||||
return (empty($result)) ? null : $result[count($result) - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "next" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return isset($result[$this->currentRow + 1]) ? $result[++ $this->currentRow] : null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "previous" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPreviousRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($result[$this->currentRow - 1]))
|
||||
{
|
||||
-- $this->currentRow;
|
||||
}
|
||||
|
||||
return $result[$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an unbuffered row and move the pointer to the next row.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUnbufferedRow(string $type = 'object')
|
||||
{
|
||||
if ($type === 'array')
|
||||
{
|
||||
return $this->fetchAssoc();
|
||||
}
|
||||
elseif ($type === 'object')
|
||||
{
|
||||
return $this->fetchObject();
|
||||
}
|
||||
|
||||
return $this->fetchObject($type);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
abstract public function getFieldCount(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getFieldNames(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getFieldData(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function freeResult();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
abstract public function dataSeek(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function fetchAssoc();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
abstract protected function fetchObject(string $className = 'stdClass');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
458
system/Database/BaseUtils.php
Normal file
458
system/Database/BaseUtils.php
Normal file
@@ -0,0 +1,458 @@
|
||||
<?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;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Class BaseUtils
|
||||
*/
|
||||
abstract class BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Database object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List databases statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $listDatabases = false;
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = false;
|
||||
|
||||
/**
|
||||
* REPAIR TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $repairTable = false;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param ConnectionInterface|object $db
|
||||
*/
|
||||
public function __construct(ConnectionInterface &$db)
|
||||
{
|
||||
$this->db = & $db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List databases
|
||||
*
|
||||
* @return array|boolean
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function listDatabases()
|
||||
{
|
||||
// Is there a cached result?
|
||||
if (isset($this->db->dataCache['db_names']))
|
||||
{
|
||||
return $this->db->dataCache['db_names'];
|
||||
}
|
||||
elseif ($this->listDatabases === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->dataCache['db_names'] = [];
|
||||
|
||||
$query = $this->db->query($this->listDatabases);
|
||||
if ($query === false)
|
||||
{
|
||||
return $this->db->dataCache['db_names'];
|
||||
}
|
||||
|
||||
for ($i = 0, $query = $query->getResultArray(), $c = count($query); $i < $c; $i ++)
|
||||
{
|
||||
$this->db->dataCache['db_names'][] = current($query[$i]);
|
||||
}
|
||||
|
||||
return $this->db->dataCache['db_names'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if a particular database exists
|
||||
*
|
||||
* @param string $database_name
|
||||
* @return boolean
|
||||
*/
|
||||
public function databaseExists(string $database_name): bool
|
||||
{
|
||||
return in_array($database_name, $this->listDatabases());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Optimize Table
|
||||
*
|
||||
* @param string $table_name
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function optimizeTable(string $table_name)
|
||||
{
|
||||
if ($this->optimizeTable === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name)));
|
||||
if ($query !== false)
|
||||
{
|
||||
$query = $query->getResultArray();
|
||||
return current($query);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Optimize Database
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function optimizeDatabase()
|
||||
{
|
||||
if ($this->optimizeTable === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->db->listTables() as $table_name)
|
||||
{
|
||||
$res = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name)));
|
||||
if (is_bool($res))
|
||||
{
|
||||
return $res;
|
||||
}
|
||||
|
||||
// Build the result array...
|
||||
|
||||
$res = $res->getResultArray();
|
||||
|
||||
// Postgre & SQLite3 returns empty array
|
||||
if (empty($res))
|
||||
{
|
||||
$key = $table_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$res = current($res);
|
||||
$key = str_replace($this->db->database . '.', '', current($res));
|
||||
$keys = array_keys($res);
|
||||
unset($res[$keys[0]]);
|
||||
}
|
||||
|
||||
$result[$key] = $res;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Repair Table
|
||||
*
|
||||
* @param string $table_name
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function repairTable(string $table_name)
|
||||
{
|
||||
if ($this->repairTable === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->db->query(sprintf($this->repairTable, $this->db->escapeIdentifiers($table_name)));
|
||||
if (is_bool($query))
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query = $query->getResultArray();
|
||||
return current($query);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate CSV from a query result object
|
||||
*
|
||||
* @param ResultInterface $query Query result object
|
||||
* @param string $delim Delimiter (default: ,)
|
||||
* @param string $newline Newline character (default: \n)
|
||||
* @param string $enclosure Enclosure (default: ")
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCSVFromResult(ResultInterface $query, string $delim = ',', string $newline = "\n", string $enclosure = '"')
|
||||
{
|
||||
$out = '';
|
||||
// First generate the headings from the table column names
|
||||
foreach ($query->getFieldNames() as $name)
|
||||
{
|
||||
$out .= $enclosure . str_replace($enclosure, $enclosure . $enclosure, $name) . $enclosure . $delim;
|
||||
}
|
||||
|
||||
$out = substr($out, 0, -strlen($delim)) . $newline;
|
||||
|
||||
// Next blast through the result array and build out the rows
|
||||
while ($row = $query->getUnbufferedRow('array'))
|
||||
{
|
||||
$line = [];
|
||||
foreach ($row as $item)
|
||||
{
|
||||
$line[] = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $item) . $enclosure;
|
||||
}
|
||||
$out .= implode($delim, $line) . $newline;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate XML data from a query result object
|
||||
*
|
||||
* @param ResultInterface $query Query result object
|
||||
* @param array $params Any preferences
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getXMLFromResult(ResultInterface $query, array $params = []): string
|
||||
{
|
||||
// Set our default values
|
||||
foreach (['root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t"] as $key => $val)
|
||||
{
|
||||
if (! isset($params[$key]))
|
||||
{
|
||||
$params[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// Create variables for convenience
|
||||
extract($params);
|
||||
|
||||
// Load the xml helper
|
||||
helper('xml');
|
||||
// Generate the result
|
||||
$xml = '<' . $root . '>' . $newline;
|
||||
while ($row = $query->getUnbufferedRow())
|
||||
{
|
||||
$xml .= $tab . '<' . $element . '>' . $newline;
|
||||
foreach ($row as $key => $val)
|
||||
{
|
||||
$val = (! empty($val)) ? xml_convert($val) : '';
|
||||
$xml .= $tab . $tab . '<' . $key . '>' . $val . '</' . $key . '>' . $newline;
|
||||
}
|
||||
$xml .= $tab . '</' . $element . '>' . $newline;
|
||||
}
|
||||
|
||||
return $xml . '</' . $root . '>' . $newline;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Database Backup
|
||||
*
|
||||
* @param array|string $params
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function backup($params = [])
|
||||
{
|
||||
// If the parameters have not been submitted as an
|
||||
// array then we know that it is simply the table
|
||||
// name, which is a valid short cut.
|
||||
if (is_string($params))
|
||||
{
|
||||
$params = ['tables' => $params];
|
||||
}
|
||||
|
||||
// Set up our default preferences
|
||||
$prefs = [
|
||||
'tables' => [],
|
||||
'ignore' => [],
|
||||
'filename' => '',
|
||||
'format' => 'gzip', // gzip, zip, txt
|
||||
'add_drop' => true,
|
||||
'add_insert' => true,
|
||||
'newline' => "\n",
|
||||
'foreign_key_checks' => true,
|
||||
];
|
||||
|
||||
// Did the user submit any preferences? If so set them....
|
||||
if (! empty($params))
|
||||
{
|
||||
foreach ($prefs as $key => $val)
|
||||
{
|
||||
if (isset($params[$key]))
|
||||
{
|
||||
$prefs[$key] = $params[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Are we backing up a complete database or individual tables?
|
||||
// If no table names were submitted we'll fetch the entire table list
|
||||
if (empty($prefs['tables']))
|
||||
{
|
||||
$prefs['tables'] = $this->db->listTables();
|
||||
}
|
||||
|
||||
// Validate the format
|
||||
if (! in_array($prefs['format'], ['gzip', 'zip', 'txt'], true))
|
||||
{
|
||||
$prefs['format'] = 'txt';
|
||||
}
|
||||
|
||||
// Is the encoder supported? If not, we'll either issue an
|
||||
// error or use plain text depending on the debug settings
|
||||
if (($prefs['format'] === 'gzip' && ! function_exists('gzencode'))
|
||||
|| ( $prefs['format'] === 'zip' && ! function_exists('gzcompress')))
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('The file compression format you chose is not supported by your server.');
|
||||
}
|
||||
|
||||
$prefs['format'] = 'txt';
|
||||
}
|
||||
|
||||
// Was a Zip file requested?
|
||||
if ($prefs['format'] === 'zip')
|
||||
{
|
||||
// Set the filename if not provided (only needed with Zip files)
|
||||
if ($prefs['filename'] === '')
|
||||
{
|
||||
$prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database)
|
||||
. date('Y-m-d_H-i', time()) . '.sql';
|
||||
}
|
||||
else
|
||||
{
|
||||
// If they included the .zip file extension we'll remove it
|
||||
if (preg_match('|.+?\.zip$|', $prefs['filename']))
|
||||
{
|
||||
$prefs['filename'] = str_replace('.zip', '', $prefs['filename']);
|
||||
}
|
||||
|
||||
// Tack on the ".sql" file extension if needed
|
||||
if (! preg_match('|.+?\.sql$|', $prefs['filename']))
|
||||
{
|
||||
$prefs['filename'] .= '.sql';
|
||||
}
|
||||
}
|
||||
|
||||
// Load the Zip class and output it
|
||||
// $CI =& get_instance();
|
||||
// $CI->load->library('zip');
|
||||
// $CI->zip->add_data($prefs['filename'], $this->_backup($prefs));
|
||||
// return $CI->zip->get_zip();
|
||||
}
|
||||
elseif ($prefs['format'] === 'txt') // Was a text file requested?
|
||||
{
|
||||
return $this->_backup($prefs);
|
||||
}
|
||||
elseif ($prefs['format'] === 'gzip') // Was a Gzip file requested?
|
||||
{
|
||||
return gzencode($this->_backup($prefs));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
* @param array|null $prefs
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function _backup(array $prefs = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
199
system/Database/Config.php
Normal file
199
system/Database/Config.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?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;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
*/
|
||||
class Config extends BaseConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* Cache for instance of any connections that
|
||||
* have been requested as a "shared" instance.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $instances = [];
|
||||
|
||||
/**
|
||||
* The main instance used to manage all of
|
||||
* our open database connections.
|
||||
*
|
||||
* @var \CodeIgniter\Database\Database
|
||||
*/
|
||||
static protected $factory;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates the default
|
||||
*
|
||||
* @param string|array $group The name of the connection group to use,
|
||||
* or an array of configuration settings.
|
||||
* @param boolean $getShared Whether to return a shared instance of the connection.
|
||||
*
|
||||
* @return BaseConnection
|
||||
*/
|
||||
public static function connect($group = null, bool $getShared = true)
|
||||
{
|
||||
// If a DB connection is passed in, just pass it back
|
||||
if ($group instanceof BaseConnection)
|
||||
{
|
||||
return $group;
|
||||
}
|
||||
|
||||
if (is_array($group))
|
||||
{
|
||||
$config = $group;
|
||||
$group = 'custom-' . md5(json_encode($config));
|
||||
}
|
||||
|
||||
$config = $config ?? config('Database');
|
||||
|
||||
if (empty($group))
|
||||
{
|
||||
$group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup;
|
||||
}
|
||||
|
||||
if (is_string($group) && ! isset($config->$group) && strpos($group, 'custom-') !== 0)
|
||||
{
|
||||
throw new \InvalidArgumentException($group . ' is not a valid database connection group.');
|
||||
}
|
||||
|
||||
if ($getShared && isset(static::$instances[$group]))
|
||||
{
|
||||
return static::$instances[$group];
|
||||
}
|
||||
|
||||
static::ensureFactory();
|
||||
|
||||
if (isset($config->$group))
|
||||
{
|
||||
$config = $config->$group;
|
||||
}
|
||||
|
||||
$connection = static::$factory->load($config, $group);
|
||||
|
||||
static::$instances[$group] = & $connection;
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of all db connections currently made.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getConnections(): array
|
||||
{
|
||||
return static::$instances;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads and returns an instance of the Forge for the specified
|
||||
* database group, and loads the group if it hasn't been loaded yet.
|
||||
*
|
||||
* @param string|array|null $group
|
||||
*
|
||||
* @return Forge
|
||||
*/
|
||||
public static function forge($group = null)
|
||||
{
|
||||
$db = static::connect($group);
|
||||
|
||||
return static::$factory->loadForge($db);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a new instance of the Database Utilities class.
|
||||
*
|
||||
* @param string|array|null $group
|
||||
*
|
||||
* @return BaseUtils
|
||||
*/
|
||||
public static function utils($group = null)
|
||||
{
|
||||
$db = static::connect($group);
|
||||
|
||||
return static::$factory->loadUtils($db);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a new instance of the Database Seeder.
|
||||
*
|
||||
* @param string|null $group
|
||||
*
|
||||
* @return Seeder
|
||||
*/
|
||||
public static function seeder(string $group = null)
|
||||
{
|
||||
$config = config('Database');
|
||||
|
||||
return new Seeder($config, static::connect($group));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensures the database Connection Manager/Factory is loaded and ready to use.
|
||||
*/
|
||||
protected static function ensureFactory()
|
||||
{
|
||||
if (static::$factory instanceof Database)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static::$factory = new Database();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
225
system/Database/ConnectionInterface.php
Normal file
225
system/Database/ConnectionInterface.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Interface ConnectionInterface
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
interface ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes the database connection/settings.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function initialize();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
* @return mixed
|
||||
*/
|
||||
public function connect(bool $persistent = false);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a persistent database connection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function persistentConnect();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Keep or establish the connection if no queries have been sent for
|
||||
* a length of time exceeding the server's idle timeout.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function reconnect();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the actual connection object. If both a 'read' and 'write'
|
||||
* connection has been specified, you can pass either term in to
|
||||
* get that connection. If you pass either alias in and only a single
|
||||
* connection is present, it must return the sole connection.
|
||||
*
|
||||
* @param string|null $alias
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConnection(string $alias = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Select a specific database table to use.
|
||||
*
|
||||
* @param string $databaseName
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setDatabase(string $databaseName);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the name of the current database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error encountered by this connection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getError();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The name of the platform in use (MySQLi, mssql, etc)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPlatform(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a string containing the version of the database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Orchestrates a query against the database. Queries must use
|
||||
* Database\Statement objects to store the query and build it.
|
||||
* This method works with the cache.
|
||||
*
|
||||
* Should automatically handle different connections for read/write
|
||||
* queries if needed.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param mixed ...$binds
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function query(string $sql, $binds = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs a basic query against the database. No binding or caching
|
||||
* is performed, nor are transactions handled. Simply takes a raw
|
||||
* query string and returns the database-specific result id.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function simpleQuery(string $sql);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an instance of the query builder for this connection.
|
||||
*
|
||||
* @param string|array $tableName Table name.
|
||||
*
|
||||
* @return BaseBuilder Builder.
|
||||
*/
|
||||
public function table($tableName);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last query's statement object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastQuery();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* "Smart" Escaping
|
||||
*
|
||||
* Escapes data based on type.
|
||||
* Sets boolean and null types.
|
||||
*
|
||||
* @param mixed $str
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function escape($str);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Allows for custom calls to the database engine that are not
|
||||
* supported through our database layer.
|
||||
*
|
||||
* @param string $functionName
|
||||
* @param array ...$params
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function callFunction(string $functionName, ...$params);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
142
system/Database/Database.php
Normal file
142
system/Database/Database.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Database Connection Factory
|
||||
*
|
||||
* Creates and returns an instance of the appropriate DatabaseConnection
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
|
||||
/**
|
||||
* Maintains an array of the instances of all connections
|
||||
* that have been created. Helps to keep track of all open
|
||||
* connections for performance monitoring, logging, etc.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $connections = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses the connection binds and returns an instance of
|
||||
* the driver ready to go.
|
||||
*
|
||||
* @param array $params
|
||||
* @param string $alias
|
||||
*
|
||||
* @return mixed
|
||||
* @internal param bool $useBuilder
|
||||
*/
|
||||
public function load(array $params = [], string $alias)
|
||||
{
|
||||
// No DB specified? Beat them senseless...
|
||||
if (empty($params['DBDriver']))
|
||||
{
|
||||
throw new \InvalidArgumentException('You have not selected a database type to connect to.');
|
||||
}
|
||||
|
||||
$className = strpos($params['DBDriver'], '\\') === false
|
||||
? '\CodeIgniter\Database\\' . $params['DBDriver'] . '\\Connection'
|
||||
: $params['DBDriver'] . '\\Connection';
|
||||
|
||||
$class = new $className($params);
|
||||
|
||||
// Store the connection
|
||||
$this->connections[$alias] = $class;
|
||||
|
||||
return $this->connections[$alias];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new Forge instance for the current database type.
|
||||
*
|
||||
* @param ConnectionInterface|BaseConnection $db
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadForge(ConnectionInterface $db)
|
||||
{
|
||||
$className = strpos($db->DBDriver, '\\') === false ? '\CodeIgniter\Database\\' . $db->DBDriver . '\\Forge' : $db->DBDriver . '\\Forge';
|
||||
|
||||
// Make sure a connection exists
|
||||
if (! $db->connID)
|
||||
{
|
||||
$db->initialize();
|
||||
}
|
||||
|
||||
$class = new $className($db);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the Database Utilities class.
|
||||
*
|
||||
* @param ConnectionInterface|BaseConnection $db
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadUtils(ConnectionInterface $db)
|
||||
{
|
||||
$className = strpos($db->DBDriver, '\\') === false ? '\CodeIgniter\Database\\' . $db->DBDriver . '\\Utils' : $db->DBDriver . '\\Utils';
|
||||
|
||||
// Make sure a connection exists
|
||||
if (! $db->connID)
|
||||
{
|
||||
$db->initialize();
|
||||
}
|
||||
|
||||
$class = new $className($db);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
65
system/Database/Exceptions/DataException.php
Normal file
65
system/Database/Exceptions/DataException.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace CodeIgniter\Database\Exceptions;
|
||||
|
||||
class DataException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Used by the Model's trigger() method when the callback cannot be found.
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @return \CodeIgniter\Database\Exceptions\DataException
|
||||
*/
|
||||
public static function forInvalidMethodTriggered(string $method)
|
||||
{
|
||||
return new static(lang('Database.invalidEvent', [$method]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by Model's insert/update methods when there isn't
|
||||
* any data to actually work with.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return \CodeIgniter\Database\Exceptions\DataException
|
||||
*/
|
||||
public static function forEmptyDataset(string $mode)
|
||||
{
|
||||
return new static(lang('Database.emptyDataset', [$mode]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an argument for one of the Model's methods
|
||||
* were empty or otherwise invalid, and they could not be
|
||||
* to work correctly for that method.
|
||||
*
|
||||
* @param string $argument
|
||||
*
|
||||
* @return \CodeIgniter\Database\Exceptions\DataException
|
||||
*/
|
||||
public static function forInvalidArgument(string $argument)
|
||||
{
|
||||
return new static(lang('Database.invalidArgument', [$argument]));
|
||||
}
|
||||
|
||||
public static function forInvalidAllowedFields(string $model)
|
||||
{
|
||||
return new static(lang('Database.invalidAllowedFields', [$model]));
|
||||
}
|
||||
|
||||
public static function forTableNotFound(string $table)
|
||||
{
|
||||
return new static(lang('Database.tableNotFound', [$table]));
|
||||
}
|
||||
|
||||
public static function forEmptyInputGiven(string $argument)
|
||||
{
|
||||
return new static(lang('Database.forEmptyInputGiven', [$argument]));
|
||||
}
|
||||
|
||||
public static function forFindColumnHaveMultipleColumns()
|
||||
{
|
||||
return new static(lang('Database.forFindColumnHaveMultipleColumns'));
|
||||
}
|
||||
}
|
||||
51
system/Database/Exceptions/DatabaseException.php
Normal file
51
system/Database/Exceptions/DatabaseException.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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
|
||||
*
|
||||
* 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\Exceptions;
|
||||
|
||||
use CodeIgniter\Exceptions\ExceptionInterface;
|
||||
|
||||
class DatabaseException extends \Error implements ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Exit status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 8;
|
||||
}
|
||||
13
system/Database/Exceptions/ExceptionInterface.php
Normal file
13
system/Database/Exceptions/ExceptionInterface.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace CodeIgniter\Database\Exceptions;
|
||||
|
||||
/**
|
||||
* Provides a domain-level interface for broad capture
|
||||
* of all database-related exceptions.
|
||||
*
|
||||
* catch (\CodeIgniter\Database\Exceptions\ExceptionInterface) { ... }
|
||||
*/
|
||||
|
||||
interface ExceptionInterface extends \CodeIgniter\Exceptions\ExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
1321
system/Database/Forge.php
Normal file
1321
system/Database/Forge.php
Normal file
File diff suppressed because it is too large
Load Diff
110
system/Database/Migration.php
Normal file
110
system/Database/Migration.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Class Migration
|
||||
*/
|
||||
abstract class Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the database group to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $DBGroup;
|
||||
|
||||
/**
|
||||
* Database Connection instance
|
||||
*
|
||||
* @var BaseConnection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Database Forge instance.
|
||||
*
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \CodeIgniter\Database\Forge $forge
|
||||
*/
|
||||
public function __construct(Forge $forge = null)
|
||||
{
|
||||
$this->forge = ! is_null($forge) ? $forge : \Config\Database::forge($this->DBGroup ?? config('Database')->defaultGroup);
|
||||
|
||||
$this->db = $this->forge->getConnection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the database group name this migration uses.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDBGroup(): ?string
|
||||
{
|
||||
return $this->DBGroup;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Perform a migration step.
|
||||
*/
|
||||
abstract public function up();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Revert a migration step.
|
||||
*/
|
||||
abstract public function down();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
1049
system/Database/MigrationRunner.php
Normal file
1049
system/Database/MigrationRunner.php
Normal file
File diff suppressed because it is too large
Load Diff
77
system/Database/MySQLi/Builder.php
Normal file
77
system/Database/MySQLi/Builder.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
|
||||
/**
|
||||
* Builder for MySQLi
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escapeChar = '`';
|
||||
|
||||
/**
|
||||
* FROM tables
|
||||
*
|
||||
* Groups tables in FROM clauses if needed, so there is no confusion
|
||||
* about operator precedence.
|
||||
*
|
||||
* Note: This is only used (and overridden) by MySQL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _fromTables(): string
|
||||
{
|
||||
if (! empty($this->QBJoin) && count($this->QBFrom) > 1)
|
||||
{
|
||||
return '(' . implode(', ', $this->QBFrom) . ')';
|
||||
}
|
||||
|
||||
return implode(', ', $this->QBFrom);
|
||||
}
|
||||
|
||||
}
|
||||
732
system/Database/MySQLi/Connection.php
Normal file
732
system/Database/MySQLi/Connection.php
Normal file
@@ -0,0 +1,732 @@
|
||||
<?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\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Connection for MySQLi
|
||||
*/
|
||||
class Connection extends BaseConnection implements ConnectionInterface
|
||||
{
|
||||
/**
|
||||
* Database driver
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $DBDriver = 'MySQLi';
|
||||
/**
|
||||
* DELETE hack flag
|
||||
*
|
||||
* Whether to use the MySQL "delete hack" which allows the number
|
||||
* of affected rows to be shown. Uses a preg_replace when enabled,
|
||||
* adding a bit more processing to all queries.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $deleteHack = true;
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $escapeChar = '`';
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* MySQLi object
|
||||
*
|
||||
* Has to be preserved without being assigned to $conn_id.
|
||||
*
|
||||
* @var \MySQLi
|
||||
*/
|
||||
public $mysqli;
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function connect(bool $persistent = false)
|
||||
{
|
||||
// Do we have a socket path?
|
||||
if ($this->hostname[0] === '/')
|
||||
{
|
||||
$hostname = null;
|
||||
$port = null;
|
||||
$socket = $this->hostname;
|
||||
}
|
||||
else
|
||||
{
|
||||
$hostname = ($persistent === true) ? 'p:' . $this->hostname : $this->hostname;
|
||||
$port = empty($this->port) ? null : $this->port;
|
||||
$socket = null;
|
||||
}
|
||||
|
||||
$client_flags = ($this->compress === true) ? MYSQLI_CLIENT_COMPRESS : 0;
|
||||
$this->mysqli = mysqli_init();
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ALL & ~MYSQLI_REPORT_INDEX);
|
||||
|
||||
$this->mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10);
|
||||
|
||||
if (isset($this->strictOn))
|
||||
{
|
||||
if ($this->strictOn)
|
||||
{
|
||||
$this->mysqli->options(MYSQLI_INIT_COMMAND,
|
||||
'SET SESSION sql_mode = CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode =
|
||||
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
|
||||
@@sql_mode,
|
||||
"STRICT_ALL_TABLES,", ""),
|
||||
",STRICT_ALL_TABLES", ""),
|
||||
"STRICT_ALL_TABLES", ""),
|
||||
"STRICT_TRANS_TABLES,", ""),
|
||||
",STRICT_TRANS_TABLES", ""),
|
||||
"STRICT_TRANS_TABLES", "")'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($this->encrypt))
|
||||
{
|
||||
$ssl = [];
|
||||
empty($this->encrypt['ssl_key']) || $ssl['key'] = $this->encrypt['ssl_key'];
|
||||
empty($this->encrypt['ssl_cert']) || $ssl['cert'] = $this->encrypt['ssl_cert'];
|
||||
empty($this->encrypt['ssl_ca']) || $ssl['ca'] = $this->encrypt['ssl_ca'];
|
||||
empty($this->encrypt['ssl_capath']) || $ssl['capath'] = $this->encrypt['ssl_capath'];
|
||||
empty($this->encrypt['ssl_cipher']) || $ssl['cipher'] = $this->encrypt['ssl_cipher'];
|
||||
|
||||
if (! empty($ssl))
|
||||
{
|
||||
if (isset($this->encrypt['ssl_verify']))
|
||||
{
|
||||
if ($this->encrypt['ssl_verify'])
|
||||
{
|
||||
defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT') &&
|
||||
$this->mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true);
|
||||
}
|
||||
// Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT
|
||||
// to FALSE didn't do anything, so PHP 5.6.16 introduced yet another
|
||||
// constant ...
|
||||
//
|
||||
// https://secure.php.net/ChangeLog-5.php#5.6.16
|
||||
// https://bugs.php.net/bug.php?id=68344
|
||||
elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT') && version_compare($this->mysqli->client_info, '5.6', '>='))
|
||||
{
|
||||
$client_flags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
|
||||
}
|
||||
}
|
||||
|
||||
$client_flags += MYSQLI_CLIENT_SSL;
|
||||
$this->mysqli->ssl_set(
|
||||
$ssl['key'] ?? null, $ssl['cert'] ?? null, $ssl['ca'] ?? null,
|
||||
$ssl['capath'] ?? null, $ssl['cipher'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if ($this->mysqli->real_connect($hostname, $this->username, $this->password,
|
||||
$this->database, $port, $socket, $client_flags)
|
||||
)
|
||||
{
|
||||
// Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails
|
||||
if (($client_flags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, '5.7.3', '<=')
|
||||
&& empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")
|
||||
->fetch_object()->Value)
|
||||
)
|
||||
{
|
||||
$this->mysqli->close();
|
||||
$message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!';
|
||||
log_message('error', $message);
|
||||
|
||||
if ($this->DBDebug)
|
||||
{
|
||||
throw new DatabaseException($message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->mysqli->set_charset($this->charset))
|
||||
{
|
||||
log_message('error',
|
||||
"Database: Unable to set the configured connection charset ('{$this->charset}').");
|
||||
$this->mysqli->close();
|
||||
|
||||
if ($this->db->debug)
|
||||
{
|
||||
throw new DatabaseException('Unable to set client connection character set: ' . $this->charset);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->mysqli;
|
||||
}
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
// Clean sensitive information from errors.
|
||||
$msg = $e->getMessage();
|
||||
|
||||
$msg = str_replace($this->username, '****', $msg);
|
||||
$msg = str_replace($this->password, '****', $msg);
|
||||
|
||||
throw new \mysqli_sql_exception($msg, $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
if ($databaseName === '')
|
||||
{
|
||||
$databaseName = $this->database;
|
||||
}
|
||||
|
||||
if (empty($this->connID))
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if ($this->connID->select_db($databaseName))
|
||||
{
|
||||
$this->database = $databaseName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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'];
|
||||
}
|
||||
|
||||
if (empty($this->mysqli))
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->dataCache['version'] = $this->mysqli->server_info;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes the query against the database.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute(string $sql)
|
||||
{
|
||||
while ($this->connID->more_results())
|
||||
{
|
||||
$this->connID->next_result();
|
||||
if ($res = $this->connID->store_result())
|
||||
{
|
||||
$res->free();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->connID->query($this->prepQuery($sql));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prep the query
|
||||
*
|
||||
* If needed, each database adapter can prep the query string
|
||||
*
|
||||
* @param string $sql an SQL query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepQuery(string $sql): string
|
||||
{
|
||||
// mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack
|
||||
// modifies the query so that it a proper number of affected rows is returned.
|
||||
if ($this->deleteHack === true && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
|
||||
{
|
||||
return trim($sql) . ' WHERE 1=1';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the total number of rows affected by this query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function affectedRows(): int
|
||||
{
|
||||
return $this->connID->affected_rows ?? 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform-dependant string escape
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function _escapeString(string $str): string
|
||||
{
|
||||
if (is_bool($str))
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
if (! $this->connID)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->connID->real_escape_string($str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Escape Like String Direct
|
||||
* There are a few instances where MySQLi queries cannot take the
|
||||
* additional "ESCAPE x" parameter for specifying the escape character
|
||||
* in "LIKE" strings, and this handles those directly with a backslash.
|
||||
*
|
||||
* @param string|string[] $str Input string
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function escapeLikeStringDirect($str)
|
||||
{
|
||||
if (is_array($str))
|
||||
{
|
||||
foreach ($str as $key => $val)
|
||||
{
|
||||
$str[$key] = $this->escapeLikeStringDirect($val);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
$str = $this->_escapeString($str);
|
||||
|
||||
// Escape LIKE condition wildcards
|
||||
return str_replace([
|
||||
$this->likeEscapeChar,
|
||||
'%',
|
||||
'_',
|
||||
], [
|
||||
'\\' . $this->likeEscapeChar,
|
||||
'\\' . '%',
|
||||
'\\' . '_',
|
||||
], $str
|
||||
);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates the SQL for listing tables in a platform-dependent manner.
|
||||
* Uses escapeLikeStringDirect().
|
||||
*
|
||||
* @param boolean $prefixLimit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listTables(bool $prefixLimit = false): string
|
||||
{
|
||||
$sql = 'SHOW TABLES FROM ' . $this->escapeIdentifiers($this->database);
|
||||
|
||||
if ($prefixLimit !== false && $this->DBPrefix !== '')
|
||||
{
|
||||
return $sql . " LIKE '" . $this->escapeLikeStringDirect($this->DBPrefix) . "%'";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 'SHOW COLUMNS FROM ' . $this->protectIdentifiers($table, true, null, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with field data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _fieldData(string $table): array
|
||||
{
|
||||
$table = $this->protectIdentifiers($table, true, null, false);
|
||||
|
||||
if (($query = $this->query('SHOW COLUMNS FROM ' . $table)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
for ($i = 0, $c = count($query); $i < $c; $i++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $query[$i]->Field;
|
||||
|
||||
sscanf($query[$i]->Type, '%[a-z](%d)', $retVal[$i]->type, $retVal[$i]->max_length);
|
||||
|
||||
$retVal[$i]->default = $query[$i]->Default;
|
||||
$retVal[$i]->primary_key = (int)($query[$i]->Key === 'PRI');
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with index data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function _indexData(string $table): array
|
||||
{
|
||||
$table = $this->protectIdentifiers($table, true, null, false);
|
||||
|
||||
if (($query = $this->query('SHOW INDEX FROM ' . $table)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
|
||||
if (! $indexes = $query->getResultArray())
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
|
||||
foreach ($indexes as $index)
|
||||
{
|
||||
if (empty($keys[$index['Key_name']]))
|
||||
{
|
||||
$keys[$index['Key_name']] = new \stdClass();
|
||||
$keys[$index['Key_name']]->name = $index['Key_name'];
|
||||
|
||||
if ($index['Key_name'] === 'PRIMARY')
|
||||
{
|
||||
$type = 'PRIMARY';
|
||||
}
|
||||
elseif ($index['Index_type'] === 'FULLTEXT')
|
||||
{
|
||||
$type = 'FULLTEXT';
|
||||
}
|
||||
elseif ($index['Non_unique'])
|
||||
{
|
||||
if ($index['Index_type'] === 'SPATIAL')
|
||||
{
|
||||
$type = 'SPATIAL';
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = 'INDEX';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = 'UNIQUE';
|
||||
}
|
||||
|
||||
$keys[$index['Key_name']]->type = $type;
|
||||
}
|
||||
|
||||
$keys[$index['Key_name']]->fields[] = $index['Column_name'];
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with Foreign key data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _foreignKeyData(string $table): array
|
||||
{
|
||||
$sql = '
|
||||
SELECT
|
||||
tc.CONSTRAINT_NAME,
|
||||
tc.TABLE_NAME,
|
||||
kcu.COLUMN_NAME,
|
||||
rc.REFERENCED_TABLE_NAME,
|
||||
kcu.REFERENCED_COLUMN_NAME
|
||||
FROM information_schema.TABLE_CONSTRAINTS AS tc
|
||||
INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS rc
|
||||
ON tc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
||||
INNER JOIN information_schema.KEY_COLUMN_USAGE AS kcu
|
||||
ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
||||
WHERE
|
||||
tc.CONSTRAINT_TYPE = ' . $this->escape('FOREIGN KEY') . ' AND
|
||||
tc.TABLE_SCHEMA = ' . $this->escape($this->database) . ' AND
|
||||
tc.TABLE_NAME = ' . $this->escape($table);
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetForeignKeyData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->constraint_name = $row->CONSTRAINT_NAME;
|
||||
$obj->table_name = $row->TABLE_NAME;
|
||||
$obj->column_name = $row->COLUMN_NAME;
|
||||
$obj->foreign_table_name = $row->REFERENCED_TABLE_NAME;
|
||||
$obj->foreign_column_name = $row->REFERENCED_COLUMN_NAME;
|
||||
|
||||
$retVal[] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to disable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _disableForeignKeyChecks()
|
||||
{
|
||||
return 'SET FOREIGN_KEY_CHECKS=0';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to enable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _enableForeignKeyChecks()
|
||||
{
|
||||
return 'SET FOREIGN_KEY_CHECKS=1';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
if (! empty($this->mysqli->connect_errno))
|
||||
{
|
||||
return [
|
||||
'code' => $this->mysqli->connect_errno,
|
||||
'message' => $this->mysqli->connect_error,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => $this->connID->errno,
|
||||
'message' => $this->connID->error,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Insert ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function insertID(): int
|
||||
{
|
||||
return $this->connID->insert_id;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transBegin(): bool
|
||||
{
|
||||
$this->connID->autocommit(false);
|
||||
|
||||
return $this->connID->begin_transaction();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Commit Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transCommit(): bool
|
||||
{
|
||||
if ($this->connID->commit())
|
||||
{
|
||||
$this->connID->autocommit(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rollback Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transRollback(): bool
|
||||
{
|
||||
if ($this->connID->rollback())
|
||||
{
|
||||
$this->connID->autocommit(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
279
system/Database/MySQLi/Forge.php
Normal file
279
system/Database/MySQLi/Forge.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?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\MySQLi;
|
||||
|
||||
/**
|
||||
* Forge for MySQLi
|
||||
*/
|
||||
class Forge extends \CodeIgniter\Database\Forge
|
||||
{
|
||||
|
||||
/**
|
||||
* CREATE DATABASE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createDatabaseStr = 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';
|
||||
|
||||
/**
|
||||
* CREATE DATABASE IF statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createDatabaseIfStr = 'CREATE DATABASE IF NOT EXISTS %s CHARACTER SET %s COLLATE %s';
|
||||
|
||||
/**
|
||||
* DROP CONSTRAINT statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dropConstraintStr = 'ALTER TABLE %s DROP FOREIGN KEY %s';
|
||||
|
||||
/**
|
||||
* CREATE TABLE keys flag
|
||||
*
|
||||
* Whether table keys are created from within the
|
||||
* CREATE TABLE statement.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $createTableKeys = true;
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_unsigned = [
|
||||
'TINYINT',
|
||||
'SMALLINT',
|
||||
'MEDIUMINT',
|
||||
'INT',
|
||||
'INTEGER',
|
||||
'BIGINT',
|
||||
'REAL',
|
||||
'DOUBLE',
|
||||
'DOUBLE PRECISION',
|
||||
'FLOAT',
|
||||
'DECIMAL',
|
||||
'NUMERIC',
|
||||
];
|
||||
|
||||
/**
|
||||
* Table Options list which required to be quoted
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_quoted_table_options = [
|
||||
'COMMENT',
|
||||
'COMPRESSION',
|
||||
'CONNECTION',
|
||||
'DATA DIRECTORY',
|
||||
'INDEX DIRECTORY',
|
||||
'ENCRYPTION',
|
||||
'PASSWORD',
|
||||
];
|
||||
|
||||
/**
|
||||
* NULL value representation in CREATE/ALTER TABLE statements
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_null = 'NULL';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CREATE TABLE attributes
|
||||
*
|
||||
* @param array $attributes Associative array of table attributes
|
||||
* @return string
|
||||
*/
|
||||
protected function _createTableAttributes(array $attributes): string
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
foreach (array_keys($attributes) as $key)
|
||||
{
|
||||
if (is_string($key))
|
||||
{
|
||||
$sql .= ' ' . strtoupper($key) . ' = ';
|
||||
|
||||
if (in_array(strtoupper($key), $this->_quoted_table_options))
|
||||
{
|
||||
$sql .= $this->db->escape($attributes[$key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= $this->db->escapeString($attributes[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($this->db->charset) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))
|
||||
{
|
||||
$sql .= ' DEFAULT CHARACTER SET = ' . $this->db->escapeString($this->db->charset);
|
||||
}
|
||||
|
||||
if (! empty($this->db->DBCollat) && ! strpos($sql, 'COLLATE'))
|
||||
{
|
||||
$sql .= ' COLLATE = ' . $this->db->escapeString($this->db->DBCollat);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @param string $alter_type ALTER type
|
||||
* @param string $table Table name
|
||||
* @param mixed $field Column definition
|
||||
* @return string|string[]
|
||||
*/
|
||||
protected function _alterTable(string $alter_type, string $table, $field)
|
||||
{
|
||||
if ($alter_type === 'DROP')
|
||||
{
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table);
|
||||
foreach ($field as $i => $data)
|
||||
{
|
||||
if ($data['_literal'] !== false)
|
||||
{
|
||||
$field[$i] = ($alter_type === 'ADD') ? "\n\tADD " . $data['_literal'] : "\n\tMODIFY " . $data['_literal'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($alter_type === 'ADD')
|
||||
{
|
||||
$field[$i]['_literal'] = "\n\tADD ";
|
||||
}
|
||||
else
|
||||
{
|
||||
$field[$i]['_literal'] = empty($data['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE ";
|
||||
}
|
||||
|
||||
$field[$i] = $field[$i]['_literal'] . $this->_processColumn($field[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return [$sql . implode(',', $field)];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected function _processColumn(array $field): string
|
||||
{
|
||||
$extra_clause = isset($field['after']) ? ' AFTER ' . $this->db->escapeIdentifiers($field['after']) : '';
|
||||
|
||||
if (empty($extra_clause) && isset($field['first']) && $field['first'] === true)
|
||||
{
|
||||
$extra_clause = ' FIRST';
|
||||
}
|
||||
|
||||
return $this->db->escapeIdentifiers($field['name'])
|
||||
. (empty($field['new_name']) ? '' : ' ' . $this->db->escapeIdentifiers($field['new_name']))
|
||||
. ' ' . $field['type'] . $field['length']
|
||||
. $field['unsigned']
|
||||
. $field['null']
|
||||
. $field['default']
|
||||
. $field['auto_increment']
|
||||
. $field['unique']
|
||||
. (empty($field['comment']) ? '' : ' COMMENT ' . $field['comment'])
|
||||
. $extra_clause;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process indexes
|
||||
*
|
||||
* @param string $table (ignored)
|
||||
* @return string
|
||||
*/
|
||||
protected function _processIndexes(string $table): string
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
for ($i = 0, $c = count($this->keys); $i < $c; $i ++)
|
||||
{
|
||||
if (is_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]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (! isset($this->fields[$this->keys[$i]]))
|
||||
{
|
||||
unset($this->keys[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
is_array($this->keys[$i]) || $this->keys[$i] = [$this->keys[$i]];
|
||||
|
||||
$unique = in_array($i, $this->uniqueKeys) ? 'UNIQUE ' : '';
|
||||
|
||||
$sql .= ",\n\t{$unique}KEY " . $this->db->escapeIdentifiers(implode('_', $this->keys[$i]))
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')';
|
||||
}
|
||||
|
||||
$this->keys = [];
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
137
system/Database/MySQLi/PreparedQuery.php
Normal file
137
system/Database/MySQLi/PreparedQuery.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?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\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\PreparedQueryInterface;
|
||||
use CodeIgniter\Database\BasePreparedQuery;
|
||||
|
||||
/**
|
||||
* Prepared query for MySQLi
|
||||
*/
|
||||
class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 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 = [])
|
||||
{
|
||||
// Mysqli driver doesn't like statements
|
||||
// with terminating semicolons.
|
||||
$sql = rtrim($sql, ';');
|
||||
|
||||
if (! $this->statement = $this->db->mysqli->prepare($sql))
|
||||
{
|
||||
$this->errorCode = $this->db->mysqli->errno;
|
||||
$this->errorString = $this->db->mysqli->error;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @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.');
|
||||
}
|
||||
|
||||
// First off -bind the parameters
|
||||
$bindTypes = '';
|
||||
|
||||
// Determine the type string
|
||||
foreach ($data as $item)
|
||||
{
|
||||
if (is_integer($item))
|
||||
{
|
||||
$bindTypes .= 'i';
|
||||
}
|
||||
elseif (is_numeric($item))
|
||||
{
|
||||
$bindTypes .= 'd';
|
||||
}
|
||||
else
|
||||
{
|
||||
$bindTypes .= 's';
|
||||
}
|
||||
}
|
||||
|
||||
// Bind it
|
||||
$this->statement->bind_param($bindTypes, ...$data);
|
||||
|
||||
$success = $this->statement->execute();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _getResult()
|
||||
{
|
||||
return $this->statement->get_result();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
173
system/Database/MySQLi/Result.php
Normal file
173
system/Database/MySQLi/Result.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?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\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\Database\ResultInterface;
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
/**
|
||||
* Result for MySQLi
|
||||
*/
|
||||
class Result extends BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return $this->resultID->field_count;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
$fieldNames = [];
|
||||
$this->resultID->field_seek(0);
|
||||
while ($field = $this->resultID->fetch_field())
|
||||
{
|
||||
$fieldNames[] = $field->name;
|
||||
}
|
||||
|
||||
return $fieldNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array
|
||||
{
|
||||
$retVal = [];
|
||||
$fieldData = $this->resultID->fetch_fields();
|
||||
|
||||
foreach ($fieldData as $i => $data)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $data->name;
|
||||
$retVal[$i]->type = $data->type;
|
||||
$retVal[$i]->max_length = $data->max_length;
|
||||
$retVal[$i]->primary_key = (int) ($data->flags & 2);
|
||||
$retVal[$i]->default = $data->def;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult()
|
||||
{
|
||||
if (is_object($this->resultID))
|
||||
{
|
||||
$this->resultID->free();
|
||||
$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
|
||||
*/
|
||||
public function dataSeek(int $n = 0)
|
||||
{
|
||||
return $this->resultID->data_seek($n);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchAssoc()
|
||||
{
|
||||
return $this->resultID->fetch_assoc();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object|boolean|Entity
|
||||
*/
|
||||
protected function fetchObject(string $className = 'stdClass')
|
||||
{
|
||||
if (is_subclass_of($className, Entity::class))
|
||||
{
|
||||
return empty($data = $this->fetchAssoc()) ? false : (new $className())->setAttributes($data);
|
||||
}
|
||||
return $this->resultID->fetch_object($className);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
80
system/Database/MySQLi/Utils.php
Normal file
80
system/Database/MySQLi/Utils.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?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\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Utils for MySQLi
|
||||
*/
|
||||
class Utils extends BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* List databases statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $listDatabases = 'SHOW DATABASES';
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = 'OPTIMIZE TABLE %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.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
368
system/Database/Postgre/Builder.php
Normal file
368
system/Database/Postgre/Builder.php
Normal file
@@ -0,0 +1,368 @@
|
||||
<?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\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Builder for Postgre
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* ORDER BY random keyword
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $randomKeyword = [
|
||||
'RANDOM()',
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ORDER BY
|
||||
*
|
||||
* @param string $orderBy
|
||||
* @param string $direction ASC, DESC or RANDOM
|
||||
* @param boolean $escape
|
||||
*
|
||||
* @return BaseBuilder
|
||||
*/
|
||||
public function orderBy(string $orderBy, string $direction = '', bool $escape = null)
|
||||
{
|
||||
$direction = strtoupper(trim($direction));
|
||||
if ($direction === 'RANDOM')
|
||||
{
|
||||
if (! is_float($orderBy) && ctype_digit((string) $orderBy))
|
||||
{
|
||||
$orderBy = (float) ($orderBy > 1 ? "0.{$orderBy}" : $orderBy);
|
||||
}
|
||||
|
||||
if (is_float($orderBy))
|
||||
{
|
||||
$this->db->simpleQuery("SET SEED {$orderBy}");
|
||||
}
|
||||
|
||||
$orderBy = $this->randomKeyword[0];
|
||||
$direction = '';
|
||||
$escape = false;
|
||||
}
|
||||
|
||||
return parent::orderBy($orderBy, $direction, $escape);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Increments a numeric column by the specified value.
|
||||
*
|
||||
* @param string $column
|
||||
* @param integer $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $column, int $value = 1)
|
||||
{
|
||||
$column = $this->db->protectIdentifiers($column);
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Decrements a numeric column by the specified value.
|
||||
*
|
||||
* @param string $column
|
||||
* @param integer $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $column, int $value = 1)
|
||||
{
|
||||
$column = $this->db->protectIdentifiers($column);
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') - {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Replace
|
||||
*
|
||||
* Compiles an replace into string and runs the query.
|
||||
* Because PostgreSQL doesn't support the replace into command,
|
||||
* we simply do a DELETE and an INSERT on the first key/value
|
||||
* combo, assuming that it's either the primary key or a unique key.
|
||||
*
|
||||
* @param array $set An associative array of insert values
|
||||
*
|
||||
* @return mixed
|
||||
* @throws DatabaseException
|
||||
* @internal param true $bool returns the generated SQL, false executes the query.
|
||||
*/
|
||||
public function replace(array $set = null)
|
||||
{
|
||||
if ($set !== null)
|
||||
{
|
||||
$this->set($set);
|
||||
}
|
||||
|
||||
if (! $this->QBSet)
|
||||
{
|
||||
if (CI_DEBUG)
|
||||
{
|
||||
throw new DatabaseException('You must use the "set" method to update an entry.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$table = $this->QBFrom[0];
|
||||
|
||||
$set = $this->binds;
|
||||
|
||||
// We need to grab out the actual values from
|
||||
// the way binds are stored with escape flag.
|
||||
array_walk($set, function (&$item) {
|
||||
$item = $item[0];
|
||||
});
|
||||
|
||||
$keys = array_keys($set);
|
||||
$values = array_values($set);
|
||||
|
||||
$builder = $this->db->table($table);
|
||||
$exists = $builder->where("$keys[0] = $values[0]", null, false)->get()->getFirstRow();
|
||||
|
||||
if (empty($exists))
|
||||
{
|
||||
$result = $builder->insert($set);
|
||||
}
|
||||
else
|
||||
{
|
||||
array_pop($set);
|
||||
$result = $builder->update($set, "$keys[0] = $values[0]");
|
||||
}
|
||||
|
||||
unset($builder);
|
||||
$this->resetWrite();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*
|
||||
* Compiles a delete string and runs the query
|
||||
*
|
||||
* @param mixed $where
|
||||
* @param integer $limit
|
||||
* @param boolean $reset_data
|
||||
*
|
||||
* @return mixed
|
||||
* @throws DatabaseException
|
||||
* @internal param the $mixed where clause
|
||||
* @internal param the $mixed limit clause
|
||||
* @internal param $bool
|
||||
*/
|
||||
public function delete($where = '', int $limit = null, bool $reset_data = true)
|
||||
{
|
||||
if (! empty($limit) || ! empty($this->QBLimit))
|
||||
{
|
||||
throw new DatabaseException('PostgreSQL does not allow LIMITs on DELETE queries.');
|
||||
}
|
||||
|
||||
return parent::delete($where, $limit, $reset_data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* LIMIT string
|
||||
*
|
||||
* Generates a platform-specific LIMIT clause.
|
||||
*
|
||||
* @param string $sql SQL Query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _limit(string $sql): string
|
||||
{
|
||||
return $sql . ' LIMIT ' . $this->QBLimit . ($this->QBOffset ? " OFFSET {$this->QBOffset}" : '');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Update statement
|
||||
*
|
||||
* Generates a platform-specific update string from the supplied data
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $values
|
||||
*
|
||||
* @return string
|
||||
* @throws DatabaseException
|
||||
* @internal param the $string table name
|
||||
* @internal param the $array update data
|
||||
*/
|
||||
protected function _update(string $table, array $values): string
|
||||
{
|
||||
if (! empty($this->QBLimit))
|
||||
{
|
||||
throw new DatabaseException('Postgres does not support LIMITs with UPDATE queries.');
|
||||
}
|
||||
|
||||
$this->QBOrderBy = [];
|
||||
return parent::_update($table, $values);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Update_Batch statement
|
||||
*
|
||||
* Generates a platform-specific batch update string from the supplied data
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param array $values Update data
|
||||
* @param string $index WHERE key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _updateBatch(string $table, array $values, string $index): string
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($values as $key => $val)
|
||||
{
|
||||
$ids[] = $val[$index];
|
||||
|
||||
foreach (array_keys($val) as $field)
|
||||
{
|
||||
if ($field !== $index)
|
||||
{
|
||||
$final[$field][] = "WHEN {$val[$index]} THEN {$val[$field]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cases = '';
|
||||
foreach ($final as $k => $v)
|
||||
{
|
||||
$cases .= "{$k} = (CASE {$index}\n"
|
||||
. implode("\n", $v)
|
||||
. "\nELSE {$k} END), ";
|
||||
}
|
||||
|
||||
$this->where("{$index} IN(" . implode(',', $ids) . ')', null, false);
|
||||
|
||||
return "UPDATE {$table} SET " . substr($cases, 0, -2) . $this->compileWhereHaving('QBWhere');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delete statement
|
||||
*
|
||||
* Generates a platform-specific delete string from the supplied data
|
||||
*
|
||||
* @param string $table The table name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _delete(string $table): string
|
||||
{
|
||||
$this->QBLimit = false;
|
||||
return parent::_delete($table);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Truncate statement
|
||||
*
|
||||
* Generates a platform-specific truncate string from the supplied data
|
||||
*
|
||||
* If the database does not support the truncate() command,
|
||||
* then this method maps to 'DELETE FROM table'
|
||||
*
|
||||
* @param string $table The table name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _truncate(string $table): string
|
||||
{
|
||||
return 'TRUNCATE ' . $table . ' RESTART IDENTITY';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform independent LIKE statement builder.
|
||||
*
|
||||
* In PostgreSQL, the ILIKE operator will perform case insensitive
|
||||
* searches according to the current locale.
|
||||
*
|
||||
* @see https://www.postgresql.org/docs/9.2/static/functions-matching.html
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $column
|
||||
* @param string $not
|
||||
* @param string $bind
|
||||
* @param boolean $insensitiveSearch
|
||||
*
|
||||
* @return string $like_statement
|
||||
*/
|
||||
public function _like_statement(string $prefix = null, string $column, string $not = null, string $bind, bool $insensitiveSearch = false): string
|
||||
{
|
||||
$op = $insensitiveSearch === true ? 'ILIKE' : 'LIKE';
|
||||
|
||||
return "{$prefix} {$column} {$not} {$op} :{$bind}:";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
614
system/Database/Postgre/Connection.php
Normal file
614
system/Database/Postgre/Connection.php
Normal file
@@ -0,0 +1,614 @@
|
||||
<?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\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Connection for Postgre
|
||||
*/
|
||||
class Connection extends BaseConnection implements ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Database driver
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $DBDriver = 'postgre';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Database schema
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $schema = 'public';
|
||||
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $escapeChar = '"';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
* @return mixed
|
||||
*/
|
||||
public function connect(bool $persistent = false)
|
||||
{
|
||||
if (empty($this->DSN))
|
||||
{
|
||||
$this->buildDSN();
|
||||
}
|
||||
|
||||
// Strip pgsql if exists
|
||||
if (mb_strpos($this->DSN, 'pgsql:') === 0)
|
||||
{
|
||||
$this->DSN = mb_substr($this->DSN, 6);
|
||||
}
|
||||
|
||||
// Convert semicolons to spaces.
|
||||
$this->DSN = str_replace(';', ' ', $this->DSN);
|
||||
|
||||
$this->connID = $persistent === true ? pg_pconnect($this->DSN) : pg_connect($this->DSN);
|
||||
|
||||
if ($this->connID !== false)
|
||||
{
|
||||
if ($persistent === true && pg_connection_status($this->connID) === PGSQL_CONNECTION_BAD && pg_ping($this->connID) === false
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
empty($this->schema) || $this->simpleQuery("SET search_path TO {$this->schema},public");
|
||||
|
||||
if ($this->setClientEncoding($this->charset) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->connID;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (pg_ping($this->connID) === false)
|
||||
{
|
||||
$this->connID = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _close()
|
||||
{
|
||||
pg_close($this->connID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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'];
|
||||
}
|
||||
|
||||
if (! $this->connID || ( $pgVersion = pg_version($this->connID)) === false)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return isset($pgVersion['server']) ? $this->dataCache['version'] = $pgVersion['server'] : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes the query against the database.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function execute(string $sql)
|
||||
{
|
||||
return pg_query($this->connID, $sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the total number of rows affected by this query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function affectedRows(): int
|
||||
{
|
||||
return pg_affected_rows($this->resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* "Smart" Escape String
|
||||
*
|
||||
* Escapes data based on type
|
||||
*
|
||||
* @param mixed $str
|
||||
* @return mixed
|
||||
*/
|
||||
public function escape($str)
|
||||
{
|
||||
if (! $this->connID)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if (is_string($str) || ( is_object($str) && method_exists($str, '__toString')))
|
||||
{
|
||||
return pg_escape_literal($this->connID, $str);
|
||||
}
|
||||
elseif (is_bool($str))
|
||||
{
|
||||
return $str ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
|
||||
return parent::escape($str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform-dependant string escape
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function _escapeString(string $str): string
|
||||
{
|
||||
if (! $this->connID)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return pg_escape_string($this->connID, $str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates the SQL for listing tables in a platform-dependent manner.
|
||||
*
|
||||
* @param boolean $prefixLimit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listTables(bool $prefixLimit = false): string
|
||||
{
|
||||
$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \'' . $this->schema . "'";
|
||||
|
||||
if ($prefixLimit !== false && $this->DBPrefix !== '')
|
||||
{
|
||||
return $sql . ' AND "table_name" LIKE \''
|
||||
. $this->escapeLikeString($this->DBPrefix) . "%' "
|
||||
. sprintf($this->likeEscapeStr, $this->likeEscapeChar);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 'SELECT "column_name"
|
||||
FROM "information_schema"."columns"
|
||||
WHERE LOWER("table_name") = '
|
||||
. $this->escape($this->DBPrefix . strtolower($table));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with field data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _fieldData(string $table): array
|
||||
{
|
||||
$sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default"
|
||||
FROM "information_schema"."columns"
|
||||
WHERE LOWER("table_name") = '
|
||||
. $this->escape(strtolower($table));
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
for ($i = 0, $c = count($query); $i < $c; $i ++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $query[$i]->column_name;
|
||||
$retVal[$i]->type = $query[$i]->data_type;
|
||||
$retVal[$i]->default = $query[$i]->column_default;
|
||||
$retVal[$i]->max_length = $query[$i]->character_maximum_length > 0 ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with index data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _indexData(string $table): array
|
||||
{
|
||||
$sql = 'SELECT "indexname", "indexdef"
|
||||
FROM "pg_indexes"
|
||||
WHERE LOWER("tablename") = ' . $this->escape(strtolower($table)) . '
|
||||
AND "schemaname" = ' . $this->escape('public');
|
||||
|
||||
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->indexname;
|
||||
$_fields = explode(',', preg_replace('/^.*\((.+?)\)$/', '$1', trim($row->indexdef)));
|
||||
$obj->fields = array_map(function ($v) {
|
||||
return trim($v);
|
||||
}, $_fields);
|
||||
|
||||
if (strpos($row->indexdef, 'CREATE UNIQUE INDEX pk') === 0)
|
||||
{
|
||||
$obj->type = 'PRIMARY';
|
||||
}
|
||||
else
|
||||
{
|
||||
$obj->type = (strpos($row->indexdef, 'CREATE UNIQUE') === 0) ? 'UNIQUE' : 'INDEX';
|
||||
}
|
||||
|
||||
$retVal[$obj->name] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with Foreign key data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _foreignKeyData(string $table): array
|
||||
{
|
||||
$sql = 'SELECT
|
||||
tc.constraint_name, tc.table_name, kcu.column_name,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE constraint_type = ' . $this->escape('FOREIGN KEY') . ' AND
|
||||
tc.table_name = ' . $this->escape($table);
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetForeignKeyData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->constraint_name = $row->constraint_name;
|
||||
$obj->table_name = $row->table_name;
|
||||
$obj->column_name = $row->column_name;
|
||||
$obj->foreign_table_name = $row->foreign_table_name;
|
||||
$obj->foreign_column_name = $row->foreign_column_name;
|
||||
$retVal[] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to disable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _disableForeignKeyChecks()
|
||||
{
|
||||
return 'SET CONSTRAINTS ALL DEFERRED';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to enable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _enableForeignKeyChecks()
|
||||
{
|
||||
return 'SET CONSTRAINTS ALL IMMEDIATE;';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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' => '',
|
||||
'message' => pg_last_error($this->connID),
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Insert ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function insertID(): int
|
||||
{
|
||||
$v = pg_version($this->connID);
|
||||
// 'server' key is only available since PostgreSQL 7.4
|
||||
$v = $v['server'] ?? 0;
|
||||
|
||||
$table = func_num_args() > 0 ? func_get_arg(0) : null;
|
||||
$column = func_num_args() > 1 ? func_get_arg(1) : null;
|
||||
|
||||
if ($table === null && $v >= '8.1')
|
||||
{
|
||||
$sql = 'SELECT LASTVAL() AS ins_id';
|
||||
}
|
||||
elseif ($table !== null)
|
||||
{
|
||||
if ($column !== null && $v >= '8.0')
|
||||
{
|
||||
$sql = "SELECT pg_get_serial_sequence('{$table}', '{$column}') AS seq";
|
||||
$query = $this->query($sql);
|
||||
$query = $query->getRow();
|
||||
$seq = $query->seq;
|
||||
}
|
||||
else
|
||||
{
|
||||
// seq_name passed in table parameter
|
||||
$seq = $table;
|
||||
}
|
||||
|
||||
$sql = "SELECT CURRVAL('{$seq}') AS ins_id";
|
||||
}
|
||||
else
|
||||
{
|
||||
return pg_last_oid($this->resultID);
|
||||
}
|
||||
|
||||
$query = $this->query($sql);
|
||||
$query = $query->getRow();
|
||||
return (int) $query->ins_id;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build a DSN from the provided parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function buildDSN()
|
||||
{
|
||||
$this->DSN === '' || $this->DSN = '';
|
||||
|
||||
// If UNIX sockets are used, we shouldn't set a port
|
||||
if (strpos($this->hostname, '/') !== false)
|
||||
{
|
||||
$this->port = '';
|
||||
}
|
||||
|
||||
$this->hostname === '' || $this->DSN = "host={$this->hostname} ";
|
||||
|
||||
if (! empty($this->port) && ctype_digit($this->port))
|
||||
{
|
||||
$this->DSN .= "port={$this->port} ";
|
||||
}
|
||||
|
||||
if ($this->username !== '')
|
||||
{
|
||||
$this->DSN .= "user={$this->username} ";
|
||||
|
||||
// An empty password is valid!
|
||||
// password must be set to null to ignore it.
|
||||
|
||||
$this->password === null || $this->DSN .= "password='{$this->password}' ";
|
||||
}
|
||||
|
||||
$this->database === '' || $this->DSN .= "dbname={$this->database} ";
|
||||
|
||||
// We don't have these options as elements in our standard configuration
|
||||
// array, but they might be set by parse_url() if the configuration was
|
||||
// provided via string> Example:
|
||||
//
|
||||
// postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1
|
||||
foreach (['connect_timeout', 'options', 'sslmode', 'service'] as $key)
|
||||
{
|
||||
if (isset($this->{$key}) && is_string($this->{$key}) && $this->{$key} !== '')
|
||||
{
|
||||
$this->DSN .= "{$key}='{$this->{$key}}' ";
|
||||
}
|
||||
}
|
||||
|
||||
$this->DSN = rtrim($this->DSN);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set client encoding
|
||||
*
|
||||
* @param string $charset The client encoding to which the data will be converted.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function setClientEncoding(string $charset): bool
|
||||
{
|
||||
return pg_set_client_encoding($this->connID, $charset) === 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transBegin(): bool
|
||||
{
|
||||
return (bool) pg_query($this->connID, 'BEGIN');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Commit Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transCommit(): bool
|
||||
{
|
||||
return (bool) pg_query($this->connID, 'COMMIT');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rollback Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transRollback(): bool
|
||||
{
|
||||
return (bool) pg_query($this->connID, 'ROLLBACK');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
}
|
||||
261
system/Database/Postgre/Forge.php
Normal file
261
system/Database/Postgre/Forge.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?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\Postgre;
|
||||
|
||||
/**
|
||||
* Forge for Postgre
|
||||
*/
|
||||
class Forge extends \CodeIgniter\Database\Forge
|
||||
{
|
||||
|
||||
/**
|
||||
* CHECK DATABASE EXIST statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $checkDatabaseExistStr = 'SELECT 1 FROM pg_database WHERE datname = ?';
|
||||
|
||||
/**
|
||||
* DROP CONSTRAINT statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dropConstraintStr = 'ALTER TABLE %s DROP CONSTRAINT %s';
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_unsigned = [
|
||||
'INT2' => 'INTEGER',
|
||||
'SMALLINT' => 'INTEGER',
|
||||
'INT' => 'BIGINT',
|
||||
'INT4' => 'BIGINT',
|
||||
'INTEGER' => 'BIGINT',
|
||||
'INT8' => 'NUMERIC',
|
||||
'BIGINT' => 'NUMERIC',
|
||||
'REAL' => 'DOUBLE PRECISION',
|
||||
'FLOAT' => 'DOUBLE PRECISION',
|
||||
];
|
||||
|
||||
/**
|
||||
* NULL value representation in CREATE/ALTER TABLE statements
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_null = 'NULL';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CREATE TABLE attributes
|
||||
*
|
||||
* @param array $attributes Associative array of table attributes
|
||||
* @return string
|
||||
*/
|
||||
protected function _createTableAttributes(array $attributes): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (in_array($alter_type, ['DROP', 'ADD'], true))
|
||||
{
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table);
|
||||
$sqls = [];
|
||||
foreach ($field as $data)
|
||||
{
|
||||
if ($data['_literal'] !== false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version_compare($this->db->getVersion(), '8', '>=') && isset($data['type']))
|
||||
{
|
||||
$sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. " TYPE {$data['type']}{$data['length']}";
|
||||
}
|
||||
|
||||
if (! empty($data['default']))
|
||||
{
|
||||
$sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. " SET DEFAULT {$data['default']}";
|
||||
}
|
||||
|
||||
if (isset($data['null']))
|
||||
{
|
||||
$sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. ($data['null'] === true ? ' DROP' : ' SET') . ' NOT NULL';
|
||||
}
|
||||
|
||||
if (! empty($data['new_name']))
|
||||
{
|
||||
$sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. ' TO ' . $this->db->escapeIdentifiers($data['new_name']);
|
||||
}
|
||||
|
||||
if (! empty($data['comment']))
|
||||
{
|
||||
$sqls[] = 'COMMENT ON COLUMN' . $this->db->escapeIdentifiers($table)
|
||||
. '.' . $this->db->escapeIdentifiers($data['name'])
|
||||
. " IS {$data['comment']}";
|
||||
}
|
||||
}
|
||||
|
||||
return $sqls;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected function _processColumn(array $field): string
|
||||
{
|
||||
return $this->db->escapeIdentifiers($field['name'])
|
||||
. ' ' . $field['type'] . $field['length']
|
||||
. $field['default']
|
||||
. $field['null']
|
||||
. $field['auto_increment']
|
||||
. $field['unique'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Field attribute TYPE
|
||||
*
|
||||
* Performs a data type mapping between different databases.
|
||||
*
|
||||
* @param array &$attributes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeType(array &$attributes)
|
||||
{
|
||||
// Reset field lengths for data types that don't support it
|
||||
if (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== false)
|
||||
{
|
||||
$attributes['CONSTRAINT'] = null;
|
||||
}
|
||||
|
||||
switch (strtoupper($attributes['TYPE']))
|
||||
{
|
||||
case 'TINYINT':
|
||||
$attributes['TYPE'] = 'SMALLINT';
|
||||
$attributes['UNSIGNED'] = false;
|
||||
break;
|
||||
case 'MEDIUMINT':
|
||||
$attributes['TYPE'] = 'INTEGER';
|
||||
$attributes['UNSIGNED'] = false;
|
||||
break;
|
||||
case 'DATETIME':
|
||||
$attributes['TYPE'] = 'TIMESTAMP';
|
||||
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)
|
||||
{
|
||||
$field['type'] = $field['type'] === 'NUMERIC' ? 'BIGSERIAL' : 'SERIAL';
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Drop Table
|
||||
*
|
||||
* Generates a platform-specific DROP TABLE string
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param boolean $if_exists Whether to add an IF EXISTS condition
|
||||
* @param boolean $cascade
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _dropTable(string $table, bool $if_exists, bool $cascade): string
|
||||
{
|
||||
$sql = parent::_dropTable($table, $if_exists, $cascade);
|
||||
|
||||
if ($cascade === true)
|
||||
{
|
||||
$sql .= ' CASCADE';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
160
system/Database/Postgre/PreparedQuery.php
Normal file
160
system/Database/Postgre/PreparedQuery.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?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\Postgre;
|
||||
|
||||
use CodeIgniter\Database\PreparedQueryInterface;
|
||||
use CodeIgniter\Database\BasePreparedQuery;
|
||||
|
||||
/**
|
||||
* Prepared query for Postgre
|
||||
*/
|
||||
class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Stores the name this query can be
|
||||
* used under by postgres. Only used internally.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The result resource from a successful
|
||||
* pg_exec. 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
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _prepare(string $sql, array $options = [])
|
||||
{
|
||||
$this->name = random_int(1, 10000000000000000);
|
||||
|
||||
$sql = $this->parameterize($sql);
|
||||
|
||||
// Update the query object since the parameters are slightly different
|
||||
// than what was put in.
|
||||
$this->query->setQuery($sql);
|
||||
|
||||
if (! $this->statement = pg_prepare($this->db->connID, $this->name, $sql))
|
||||
{
|
||||
$this->errorCode = 0;
|
||||
$this->errorString = pg_last_error($this->db->connID);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @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.');
|
||||
}
|
||||
|
||||
$this->result = pg_execute($this->db->connID, $this->name, $data);
|
||||
|
||||
return (bool) $this->result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Replaces the ? placeholders with $1, $2, etc parameters for use
|
||||
* within the prepared query.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function parameterize(string $sql): string
|
||||
{
|
||||
// Track our current value
|
||||
$count = 0;
|
||||
|
||||
$sql = preg_replace_callback('/\?/', function ($matches) use (&$count) {
|
||||
$count ++;
|
||||
return "\${$count}";
|
||||
}, $sql);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
171
system/Database/Postgre/Result.php
Normal file
171
system/Database/Postgre/Result.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?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\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\Database\ResultInterface;
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
/**
|
||||
* Result for Postgre
|
||||
*/
|
||||
class Result extends BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return pg_num_fields($this->resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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[] = pg_field_name($this->resultID, $i);
|
||||
}
|
||||
|
||||
return $fieldNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array
|
||||
{
|
||||
$retVal = [];
|
||||
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = pg_field_name($this->resultID, $i);
|
||||
$retVal[$i]->type = pg_field_type($this->resultID, $i);
|
||||
$retVal[$i]->max_length = pg_field_size($this->resultID, $i);
|
||||
// $retVal[$i]->primary_key = (int)($fieldData[$i]->flags & 2);
|
||||
// $retVal[$i]->default = $fieldData[$i]->def;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult()
|
||||
{
|
||||
if (is_resource($this->resultID))
|
||||
{
|
||||
pg_free_result($this->resultID);
|
||||
$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
|
||||
*/
|
||||
public function dataSeek(int $n = 0)
|
||||
{
|
||||
return pg_result_seek($this->resultID, $n);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchAssoc()
|
||||
{
|
||||
return pg_fetch_assoc($this->resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object|boolean|Entity
|
||||
*/
|
||||
protected function fetchObject(string $className = 'stdClass')
|
||||
{
|
||||
if (is_subclass_of($className, Entity::class))
|
||||
{
|
||||
return empty($data = $this->fetchAssoc()) ? false : (new $className())->setAttributes($data);
|
||||
}
|
||||
return pg_fetch_object($this->resultID, null, $className);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
79
system/Database/Postgre/Utils.php
Normal file
79
system/Database/Postgre/Utils.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?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\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Utils for Postgre
|
||||
*/
|
||||
class Utils extends BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* List databases statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $listDatabases = 'SELECT datname FROM pg_database';
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = 'REINDEX TABLE %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.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
106
system/Database/PreparedQueryInterface.php
Normal file
106
system/Database/PreparedQueryInterface.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Prepared query interface
|
||||
*/
|
||||
interface PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return ResultInterface
|
||||
*/
|
||||
public function execute(...$data);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function prepare(string $sql, array $options = []);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Explicity closes the statement.
|
||||
*/
|
||||
public function close();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the SQL that has been prepared.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
506
system/Database/Query.php
Normal file
506
system/Database/Query.php
Normal file
@@ -0,0 +1,506 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Query builder
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
class Query implements QueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* The query string, as provided by the user.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalQueryString;
|
||||
|
||||
/**
|
||||
* The final query string after binding, etc.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $finalQueryString;
|
||||
|
||||
/**
|
||||
* The binds and their values used for binding.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $binds = [];
|
||||
|
||||
/**
|
||||
* Bind marker
|
||||
*
|
||||
* Character used to identify values in a prepared statement.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bindMarker = '?';
|
||||
|
||||
/**
|
||||
* The start time in seconds with microseconds
|
||||
* for when this query was executed.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $startTime;
|
||||
|
||||
/**
|
||||
* The end time in seconds with microseconds
|
||||
* for when this query was executed.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $endTime;
|
||||
|
||||
/**
|
||||
* The error code, if any.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $errorCode;
|
||||
|
||||
/**
|
||||
* The error message, if any.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $errorString;
|
||||
|
||||
/**
|
||||
* Pointer to database connection.
|
||||
* Mainly for escaping features.
|
||||
*
|
||||
* @var BaseConnection
|
||||
*/
|
||||
public $db;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* BaseQuery constructor.
|
||||
*
|
||||
* @param $db ConnectionInterface
|
||||
*/
|
||||
public function __construct(&$db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the raw query string to use for this statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param mixed $binds
|
||||
* @param boolean $setEscape
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuery(string $sql, $binds = null, bool $setEscape = true)
|
||||
{
|
||||
$this->originalQueryString = $sql;
|
||||
|
||||
if (! is_null($binds))
|
||||
{
|
||||
if (! is_array($binds))
|
||||
{
|
||||
$binds = [$binds];
|
||||
}
|
||||
|
||||
if ($setEscape)
|
||||
{
|
||||
array_walk($binds, function (&$item) {
|
||||
$item = [
|
||||
$item,
|
||||
true,
|
||||
];
|
||||
});
|
||||
}
|
||||
$this->binds = $binds;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will store the variables to bind into the query later.
|
||||
*
|
||||
* @param array $binds
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBinds(array $binds)
|
||||
{
|
||||
$this->binds = $binds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the final, processed query string after binding, etal
|
||||
* has been performed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery(): string
|
||||
{
|
||||
if (empty($this->finalQueryString))
|
||||
{
|
||||
$this->finalQueryString = $this->originalQueryString;
|
||||
}
|
||||
|
||||
$this->compileBinds();
|
||||
|
||||
return $this->finalQueryString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Records the execution time of the statement using microtime(true)
|
||||
* for it's start and end values. If no end value is present, will
|
||||
* use the current time to determine total duration.
|
||||
*
|
||||
* @param float $start
|
||||
* @param float $end
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDuration(float $start, float $end = null)
|
||||
{
|
||||
$this->startTime = $start;
|
||||
|
||||
if (is_null($end))
|
||||
{
|
||||
$end = microtime(true);
|
||||
}
|
||||
|
||||
$this->endTime = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the start time in seconds with microseconds.
|
||||
*
|
||||
* @param boolean $returnRaw
|
||||
* @param integer $decimals
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStartTime(bool $returnRaw = false, int $decimals = 6): string
|
||||
{
|
||||
if ($returnRaw)
|
||||
{
|
||||
return $this->startTime;
|
||||
}
|
||||
|
||||
return number_format($this->startTime, $decimals);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Returns the duration of this query during execution, or null if
|
||||
* the query has not been executed yet.
|
||||
*
|
||||
* @param integer $decimals The accuracy of the returned time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDuration(int $decimals = 6): string
|
||||
{
|
||||
return number_format(($this->endTime - $this->startTime), $decimals);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stores the error description that happened for this query.
|
||||
*
|
||||
* @param integer $code
|
||||
* @param string $error
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setError(int $code, string $error)
|
||||
{
|
||||
$this->errorCode = $code;
|
||||
$this->errorString = $error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reports whether this statement created an error not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasError(): bool
|
||||
{
|
||||
return ! empty($this->errorString);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int
|
||||
{
|
||||
return $this->errorCode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string
|
||||
{
|
||||
return $this->errorString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the statement is a write-type query or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriteType(): 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', $this->originalQueryString);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Swaps out one table prefix for a new one.
|
||||
*
|
||||
* @param string $orig
|
||||
* @param string $swap
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function swapPrefix(string $orig, string $swap)
|
||||
{
|
||||
$sql = empty($this->finalQueryString) ? $this->originalQueryString : $this->finalQueryString;
|
||||
|
||||
$this->finalQueryString = preg_replace('/(\W)' . $orig . '(\S+?)/', '\\1' . $swap . '\\2', $sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the original SQL that was passed into the system.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalQuery(): string
|
||||
{
|
||||
return $this->originalQueryString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Escapes and inserts any binds into the finalQueryString object.
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
protected function compileBinds()
|
||||
{
|
||||
$sql = $this->finalQueryString;
|
||||
|
||||
$hasNamedBinds = strpos($sql, ':') !== false;
|
||||
|
||||
if (empty($this->binds) || empty($this->bindMarker) ||
|
||||
(strpos($sql, $this->bindMarker) === false &&
|
||||
$hasNamedBinds === false)
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (! is_array($this->binds))
|
||||
{
|
||||
$binds = [$this->binds];
|
||||
$bindCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$binds = $this->binds;
|
||||
$bindCount = count($binds);
|
||||
}
|
||||
|
||||
// Reverse the binds so that duplicate named binds
|
||||
// will be processed prior to the original binds.
|
||||
if (! is_numeric(key(array_slice($binds, 0, 1))))
|
||||
{
|
||||
$binds = array_reverse($binds);
|
||||
}
|
||||
|
||||
// We'll need marker length later
|
||||
$ml = strlen($this->bindMarker);
|
||||
|
||||
if ($hasNamedBinds)
|
||||
{
|
||||
$sql = $this->matchNamedBinds($sql, $binds);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = $this->matchSimpleBinds($sql, $binds, $bindCount, $ml);
|
||||
}
|
||||
|
||||
$this->finalQueryString = $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Match bindings
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $binds
|
||||
* @return string
|
||||
*/
|
||||
protected function matchNamedBinds(string $sql, array $binds): string
|
||||
{
|
||||
$replacers = [];
|
||||
|
||||
foreach ($binds as $placeholder => $value)
|
||||
{
|
||||
// $value[1] contains the boolean whether should be escaped or not
|
||||
$escapedValue = $value[1] ? $this->db->escape($value[0]) : $value[0];
|
||||
|
||||
// In order to correctly handle backlashes in saved strings
|
||||
// we will need to preg_quote, so remove the wrapping escape characters
|
||||
// otherwise it will get escaped.
|
||||
if (is_array($value[0]))
|
||||
{
|
||||
$escapedValue = '(' . implode(',', $escapedValue) . ')';
|
||||
}
|
||||
|
||||
$replacers[":{$placeholder}:"] = $escapedValue;
|
||||
}
|
||||
|
||||
$sql = strtr($sql, $replacers);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Match bindings
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $binds
|
||||
* @param integer $bindCount
|
||||
* @param integer $ml
|
||||
* @return string
|
||||
*/
|
||||
protected function matchSimpleBinds(string $sql, array $binds, int $bindCount, int $ml): string
|
||||
{
|
||||
// Make sure not to replace a chunk inside a string that happens to match the bind marker
|
||||
if ($c = preg_match_all("/'[^']*'/i", $sql, $matches))
|
||||
{
|
||||
$c = preg_match_all('/' . preg_quote($this->bindMarker, '/') . '/i', str_replace($matches[0], str_replace($this->bindMarker, str_repeat(' ', $ml), $matches[0]), $sql, $c), $matches, PREG_OFFSET_CAPTURE);
|
||||
|
||||
// Bind values' count must match the count of markers in the query
|
||||
if ($bindCount !== $c)
|
||||
{
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
// Number of binds must match bindMarkers in the string.
|
||||
else if (($c = preg_match_all('/' . preg_quote($this->bindMarker, '/') . '/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bindCount)
|
||||
{
|
||||
return $sql;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
$c--;
|
||||
$escapedValue = $binds[$c][1] ? $this->db->escape($binds[$c][0]) : $binds[$c][0];
|
||||
if (is_array($escapedValue))
|
||||
{
|
||||
$escapedValue = '(' . implode(',', $escapedValue) . ')';
|
||||
}
|
||||
$sql = substr_replace($sql, $escapedValue, $matches[0][$c][1], $ml);
|
||||
}
|
||||
while ($c !== 0);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return text representation of the query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getQuery();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
159
system/Database/QueryInterface.php
Normal file
159
system/Database/QueryInterface.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Interface QueryInterface
|
||||
*
|
||||
* Represents a single statement that can be executed against the database.
|
||||
* Statements are platform-specific and can handle binding of binds.
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
interface QueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Sets the raw query string to use for this statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param mixed $binds
|
||||
* @param boolean $setEscape
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setQuery(string $sql, $binds = null, bool $setEscape = true);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the final, processed query string after binding, etal
|
||||
* has been performed.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQuery();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Records the execution time of the statement using microtime(true)
|
||||
* for it's start and end values. If no end value is present, will
|
||||
* use the current time to determine total duration.
|
||||
*
|
||||
* @param float $start
|
||||
* @param float $end
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setDuration(float $start, float $end = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the duration of this query during execution, or null if
|
||||
* the query has not been executed yet.
|
||||
*
|
||||
* @param integer $decimals The accuracy of the returned time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDuration(int $decimals = 6): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stores the error description that happened for this query.
|
||||
*
|
||||
* @param integer $code
|
||||
* @param string $error
|
||||
*/
|
||||
public function setError(int $code, string $error);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reports whether this statement created an error not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasError(): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the statement is a write-type query or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriteType(): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Swaps out one table prefix for a new one.
|
||||
*
|
||||
* @param string $orig
|
||||
* @param string $swap
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function swapPrefix(string $orig, string $swap);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
264
system/Database/ResultInterface.php
Normal file
264
system/Database/ResultInterface.php
Normal file
@@ -0,0 +1,264 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Interface ResultInterface
|
||||
*/
|
||||
interface ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Retrieve the results of the query. Typically an array of
|
||||
* individual data rows, which can be either an 'array', an
|
||||
* 'object', or a custom class name.
|
||||
*
|
||||
* @param string $type The row type. Either 'array', 'object', or a class name to use
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult(string $type = 'object'): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of custom objects.
|
||||
*
|
||||
* @param string $className The name of the class to use.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomResultObject(string $className);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of arrays.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultArray(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of objects.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultObject(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wrapper object to return a row as either an array, an object, or
|
||||
* a custom class.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param mixed $n The index of the results to return
|
||||
* @param string $type The type of result object. 'array', 'object' or class name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRow($n = 0, string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a row as a custom class instance.
|
||||
*
|
||||
* If row doesn't exists, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
* @param string $className
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomRowObject(int $n, string $className);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an array.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowArray(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an object.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowObject(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assigns an item into a particular column slot.
|
||||
*
|
||||
* @param $key
|
||||
* @param null $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setRow($key, $value = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "first" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "last" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "next" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "previous" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPreviousRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an unbuffered row and move the pointer to the next row.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUnbufferedRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function dataSeek(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
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.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
228
system/Database/Seeder.php
Normal file
228
system/Database/Seeder.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?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;
|
||||
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Class Seeder
|
||||
*/
|
||||
class Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the database group to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $DBGroup;
|
||||
|
||||
/**
|
||||
* Where we can find the Seed files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $seedPath;
|
||||
|
||||
/**
|
||||
* An instance of the main Database configuration
|
||||
*
|
||||
* @var BaseConfig
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Database Connection instance
|
||||
*
|
||||
* @var BaseConnection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Database Forge instance.
|
||||
*
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
/**
|
||||
* If true, will not display CLI messages.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $silent = false;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Seeder constructor.
|
||||
*
|
||||
* @param BaseConfig $config
|
||||
* @param BaseConnection $db
|
||||
*/
|
||||
public function __construct(BaseConfig $config, BaseConnection $db = null)
|
||||
{
|
||||
$this->seedPath = $config->filesPath ?? APPPATH . 'Database/';
|
||||
|
||||
if (empty($this->seedPath))
|
||||
{
|
||||
throw new \InvalidArgumentException('Invalid filesPath set in the Config\Database.');
|
||||
}
|
||||
|
||||
$this->seedPath = rtrim($this->seedPath, '/') . '/Seeds/';
|
||||
|
||||
if (! is_dir($this->seedPath))
|
||||
{
|
||||
throw new \InvalidArgumentException('Unable to locate the seeds directory. Please check Config\Database::filesPath');
|
||||
}
|
||||
|
||||
$this->config = & $config;
|
||||
|
||||
if (is_null($db))
|
||||
{
|
||||
$db = \Config\Database::connect($this->DBGroup);
|
||||
}
|
||||
|
||||
$this->db = & $db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the specified seeder and runs it.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function call(string $class)
|
||||
{
|
||||
if (empty($class))
|
||||
{
|
||||
throw new \InvalidArgumentException('No Seeder was specified.');
|
||||
}
|
||||
|
||||
$path = str_replace('.php', '', $class) . '.php';
|
||||
|
||||
// If we have namespaced class, simply try to load it.
|
||||
if (strpos($class, '\\') !== false)
|
||||
{
|
||||
$seeder = new $class($this->config);
|
||||
}
|
||||
// Otherwise, try to load the class manually.
|
||||
else
|
||||
{
|
||||
$path = $this->seedPath . $path;
|
||||
|
||||
if (! is_file($path))
|
||||
{
|
||||
throw new \InvalidArgumentException('The specified Seeder is not a valid file: ' . $path);
|
||||
}
|
||||
|
||||
// Assume the class has the correct namespace
|
||||
$class = APP_NAMESPACE . '\Database\Seeds\\' . $class;
|
||||
|
||||
if (! class_exists($class, false))
|
||||
{
|
||||
require_once $path;
|
||||
}
|
||||
|
||||
$seeder = new $class($this->config);
|
||||
}
|
||||
|
||||
$seeder->run();
|
||||
|
||||
unset($seeder);
|
||||
|
||||
if (is_cli() && ! $this->silent)
|
||||
{
|
||||
CLI::write("Seeded: {$class}", 'green');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the location of the directory that seed files can be located in.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return Seeder
|
||||
*/
|
||||
public function setPath(string $path)
|
||||
{
|
||||
$this->seedPath = rtrim($path, '/') . '/';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the silent treatment.
|
||||
*
|
||||
* @param boolean $silent
|
||||
*
|
||||
* @return Seeder
|
||||
*/
|
||||
public function setSilent(bool $silent)
|
||||
{
|
||||
$this->silent = $silent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Run the database seeds. This is where the magic happens.
|
||||
*
|
||||
* Child classes must implement this method and take care
|
||||
* of inserting their data here.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
Reference in New Issue
Block a user