First Local Commit - After Clean up.

Signed-off-by: Rick Hays <rhays@haysgang.com>
This commit is contained in:
2019-12-02 14:54:38 -06:00
commit 10412ab7f6
486 changed files with 123242 additions and 0 deletions

View 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}:";
}
//--------------------------------------------------------------------
}

View 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');
}
// --------------------------------------------------------------------
}

View 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;
}
//--------------------------------------------------------------------
}

View 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;
}
//--------------------------------------------------------------------
}

View 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);
}
//--------------------------------------------------------------------
}

View 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.');
}
//--------------------------------------------------------------------
}