Skip to content

Commit

Permalink
Merge branch 'master' into drop-table-params
Browse files Browse the repository at this point in the history
  • Loading branch information
vjik authored Oct 19, 2024
2 parents d6accea + 42181e1 commit efb2368
Show file tree
Hide file tree
Showing 55 changed files with 2,099 additions and 439 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/active-record.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:

env:
COMPOSER_ROOT_VERSION: dev-master
EXTENSIONS: pdo, pdo_mysql, pdo_oci, pdo_pgsql, pdo_sqlite, pdo_sqlsrv-5.10.1
EXTENSIONS: pdo, pdo_mysql, pdo_oci, pdo_pgsql, pdo_sqlite, pdo_sqlsrv-5.12

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -77,6 +77,11 @@ jobs:
options: --name=mssql --health-cmd="/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'" --health-interval=10s --health-timeout=5s --health-retries=3

steps:
- name: Install ODBC driver.
run: |
sudo curl https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18
- name: Checkout.
uses: actions/checkout@v3

Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/db-mssql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ jobs:
env:
COMPOSER_ROOT_VERSION: dev-master
CURRENT_PACKAGE: db-mssql
EXTENSIONS: pdo, pdo_sqlsrv-5.10.1
EXTENSIONS: pdo, pdo_sqlsrv-5.12

runs-on: ubuntu-latest
runs-on: ${{ matrix.mssql.os || 'ubuntu-latest' }}

strategy:
matrix:
Expand All @@ -43,7 +43,9 @@ jobs:

include:
- php: 8.3
mssql: { server: 2017-latest }
mssql:
server: 2017-latest
os: ubuntu-20.04
- php: 8.3
mssql:
server: 2019-latest
Expand All @@ -62,6 +64,11 @@ jobs:
options: --name=mssql --health-cmd="/opt/mssql-tools${{ matrix.mssql.odbc-version }}/bin/sqlcmd ${{ matrix.mssql.flag }} -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'" --health-interval=10s --health-timeout=5s --health-retries=3

steps:
- name: Install ODBC driver.
run: |
sudo curl https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18
- name: Checkout.
uses: actions/checkout@v3

Expand Down
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- Enh #806: Build `Expression` instances inside `Expression::$params` when build a query using `QueryBuilder` (@Tigrov)
- Enh #766: Allow `ColumnInterface` as column type. (@Tigrov)
- Bug #828: Fix `float` type when use `AbstractCommand::getRawSql()` method (@Tigrov)
- Enh #752: Implement `ColumnSchemaInterface` classes according to the data type of database table columns
- New #752: Implement `ColumnSchemaInterface` classes according to the data type of database table columns
for type casting performance (@Tigrov)
- Enh #829: Rename `batchInsert()` to `insertBatch()` in `DMLQueryBuilderInterface` and `CommandInterface`
and change parameters from `$table, $columns, $rows` to `$table, $rows, $columns = []` (@Tigrov)
Expand All @@ -29,18 +29,24 @@
- Chg #846: Remove `SchemaInterface::isReadQuery()` and `AbstractSchema::isReadQuery()` methods (@Tigrov)
- Chg #847: Remove `SchemaInterface::getRawTableName()` and `AbstractSchema::getRawTableName()` methods (@Tigrov)
- Enh #852: Add method chaining for column classes (@Tigrov)
- Enh #855: Add array and JSON overlaps conditions (@Tigrov)
- Enh #860: Add `bit` abstract type (@Tigrov)
- New #855: Add array and JSON overlaps conditions (@Tigrov)
- New #860: Add `bit` abstract type (@Tigrov)
- Enh #862: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov)
- Enh #865: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov, @vjik)
- Enh #798: Allow `QueryInterface::one()` and `QueryInterface::all()` to return objects (@darkdef, @Tigrov)
- Enh #872: Use `#[\SensitiveParameter]` attribute to mark sensitive parameters (@heap-s)
- Enh #864: Realize column factory (@Tigrov)
- New #864: Realize column factory (@Tigrov)
- Enh #875: Ignore "Packets out of order..." warnings in `AbstractPdoCommand::internalExecute()` method (@Tigrov)
- Enh #877: Separate column type constants (@Tigrov)
- Enh #878: Realize `ColumnBuilder` class (@Tigrov)
- New #773: Add parameters `$ifExists` and `$cascade` to `CommandInterface::dropTable()` and
`DDLQueryBuilderInterface::dropTable()` methods (@vjik)
- Enh #881: Refactor `ColumnSchemaInterface` and `AbstractColumnSchema` (@Tigrov)
- New #882: Move `ArrayColumnSchema` and `StructuredColumnSchema` classes from `db-pgsql` package (@Tigrov)
- New #883: Add `ColumnDefinitionBuilder` class and `QueryBuilderInterface::buildColumnDefinition()` method (@Tigrov)
- Enh #885: Refactor `AbstractDsn` class (@Tigrov)
- Chg #889: Update `AbstractDMLQueryBuilder::insertBatch()` method (@Tigrov)
- Enh #890: Add properties of `AbstractColumnSchema` class to constructor (@Tigrov)

## 1.3.0 March 21, 2024

Expand Down
17 changes: 14 additions & 3 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,16 @@ and the following changes were made:
- `getName()` method can return `string` or `null`;
- `getPhpType()` method must return `string` PHP type of the column which used for generating related model properties;
- `name(string|null $name)` method is added;
- `load(array $info)` method is added;
- constructor of `AbstractColumnSchema` class is changed to `__construct(string $type, string|null $phpType = null)`;
- `check(string|null $check)` method is added;
- `getCheck()` method is added;
- `reference(ForeignKeyConstraint|null $reference)` method is added;
- `getReference()` method is added;
- `notNull(bool $notNull = true)` method is added;
- `isNotNull()` method is added;
- `unique(bool $unique = true)` method is added;
- `isUnique()` method is added;
- all `AbstractColumnSchema` class properties except `$type` moved to constructor;
- added `DEFAULT_TYPE` constant to `AbstractColumnSchema` class;
- added method chaining.

### New classes with constants
Expand All @@ -89,12 +97,15 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace
- `DoubleColumnSchema` for columns with fractional number type (float, double, decimal, money);
- `StringColumnSchema` for columns with string or datetime type (char, string, text, datetime, timestamp, date, time);
- `BinaryColumnSchema` for columns with binary type;
- `ArrayColumnSchema` for columns with array type;
- `StructuredColumnSchema` for columns with structured type (composite type in PostgreSQL);
- `JsonColumnSchema` for columns with json type.

### New methods

- `QuoterInterface::getRawTableName()` - returns the raw table name without quotes;
- `ConnectionInterface::getColumnFactory()` - returns the column factory object for concrete DBMS.
- `SchemaInterface::getColumnFactory()` - returns the column factory object for concrete DBMS;
- `QueryBuilderInterface::buildColumnDefinition()` - builds column definition for `CREATE TABLE` statement.

### Remove methods

Expand Down
38 changes: 19 additions & 19 deletions src/Connection/AbstractDsn.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

use Stringable;

use function implode;

/**
* It's typically used to parse a DSN string, which is a string that has all the necessary information to connect
* to a database, such as the database driver, hostname, database name, port, and options.
Expand All @@ -17,37 +15,37 @@
abstract class AbstractDsn implements DsnInterface, Stringable
{
/**
* @psalm-param string[] $options
* @param string $driver The database driver name.
* @param string $host The database host name or IP address.
* @param string|null $databaseName The database name to connect to.
* @param string|null $port The database port. Null if isn't set.
* @param string[] $options The database connection options. Default value to an empty array.
*
* @psalm-param array<string,string> $options
*/
public function __construct(
private string $driver,
private string $host,
private string|null $databaseName = null,
private string|null $port = null,
private array $options = []
private readonly string $driver,
private readonly string $host = '127.0.0.1',
private readonly string|null $databaseName = null,
private readonly string|null $port = null,
private readonly array $options = []
) {
}

public function asString(): string
{
$dsn = "$this->driver:" . "host=$this->host";
$dsn = "$this->driver:host=$this->host";

if ($this->databaseName !== null && $this->databaseName !== '') {
$dsn .= ';' . "dbname=$this->databaseName";
$dsn .= ";dbname=$this->databaseName";
}

if ($this->port !== null) {
$dsn .= ';' . "port=$this->port";
$dsn .= ";port=$this->port";
}

$parts = [];

foreach ($this->options as $key => $value) {
$parts[] = "$key=$value";
}

if (!empty($parts)) {
$dsn .= ';' . implode(';', $parts);
$dsn .= ";$key=$value";
}

return $dsn;
Expand Down Expand Up @@ -86,7 +84,9 @@ public function getHost(): string
}

/**
* @return array The database connection options. Default value to an empty array.
* @return string[] The database connection options. Default value to an empty array.
*
* @psalm-return array<string,string>
*/
public function getOptions(): array
{
Expand Down
6 changes: 0 additions & 6 deletions src/Connection/ConnectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use Yiisoft\Db\Query\BatchQueryResultInterface;
use Yiisoft\Db\Query\QueryInterface;
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;
use Yiisoft\Db\Schema\QuoterInterface;
use Yiisoft\Db\Schema\SchemaInterface;
use Yiisoft\Db\Schema\TableSchemaInterface;
Expand Down Expand Up @@ -86,11 +85,6 @@ public function createTransaction(): TransactionInterface;
*/
public function close(): void;

/**
* Returns the column factory for creating column instances.
*/
public function getColumnFactory(): ColumnFactoryInterface;

/**
* Returns the name of the DB driver for the current `dsn`.
*
Expand Down
6 changes: 0 additions & 6 deletions src/Debug/ConnectionInterfaceProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Yiisoft\Db\Query\BatchQueryResultInterface;
use Yiisoft\Db\Query\QueryInterface;
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;
use Yiisoft\Db\Schema\QuoterInterface;
use Yiisoft\Db\Schema\SchemaInterface;
use Yiisoft\Db\Schema\TableSchemaInterface;
Expand Down Expand Up @@ -63,11 +62,6 @@ public function close(): void
$this->connection->close();
}

public function getColumnFactory(): ColumnFactoryInterface
{
return $this->connection->getColumnFactory();
}

public function getLastInsertID(string $sequenceName = null): string
{
return $this->connection->getLastInsertID($sequenceName);
Expand Down
2 changes: 1 addition & 1 deletion src/Expression/ArrayExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* @template-implements ArrayAccess<int, mixed>
* @template-implements IteratorAggregate<int>
*/
class ArrayExpression implements ExpressionInterface, ArrayAccess, Countable, IteratorAggregate
final class ArrayExpression implements ExpressionInterface, ArrayAccess, Countable, IteratorAggregate
{
public function __construct(private mixed $value = [], private string|null $type = null, private int $dimension = 1)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Expression/Expression.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*
* @psalm-import-type ParamsType from ConnectionInterface
*/
class Expression implements ExpressionInterface, Stringable
final class Expression implements ExpressionInterface, Stringable
{
/**
* @psalm-param ParamsType $params
Expand Down
2 changes: 1 addition & 1 deletion src/Expression/JsonExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* new JsonExpression(['a' => 1, 'b' => 2]); // will be encoded to '{"a": 1, "b": 2}'
* ```
*/
class JsonExpression implements ExpressionInterface, JsonSerializable
final class JsonExpression implements ExpressionInterface, JsonSerializable
{
public function __construct(protected mixed $value, private string|null $type = null)
{
Expand Down
126 changes: 126 additions & 0 deletions src/Expression/StructuredExpression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Expression;

use Traversable;
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;

use function array_key_exists;
use function array_keys;
use function get_object_vars;
use function is_object;
use function iterator_to_array;

/**
* Represents a structured type SQL expression.
*
* @see https://en.wikipedia.org/wiki/Structured_type
*
* For example:
*
* ```php
* new StructuredExpression(['price' => 10, 'currency_code' => 'USD']);
* ```
*
* Will be encoded to `ROW(10, USD)` in PostgreSQL.
*/
final class StructuredExpression implements ExpressionInterface
{
/**
* @param array|object $value The content of the structured type. It can be represented as
* - an associative `array` of column names and values;
* - an indexed `array` of column values in the order of structured type columns;
* - an `iterable` object that can be converted to an `array` using `iterator_to_array()`;
* - an `object` that can be converted to an `array` using `get_object_vars()`;
* - an `ExpressionInterface` object that represents a SQL expression.
* @param string|null $type The structured database type name. Defaults to `null` which means the type is not
* explicitly specified. Note that in the case where a type is not specified explicitly and DBMS cannot guess it
* from the context, SQL error will be raised.
* @param ColumnSchemaInterface[] $columns The structured type columns that are used for value normalization and type
* casting.
*
* @psalm-param array<string, ColumnSchemaInterface> $columns
*/
public function __construct(
private array|object $value,
private string|null $type = null,
private array $columns = [],
) {
}

/**
* The structured type name.
*
* Defaults to `null` which means the type is not explicitly specified.
*
* Note that in the case where a type is not specified explicitly and DBMS cannot guess it from the context,
* SQL error will be raised.
*/
public function getType(): string|null
{
return $this->type;
}

/**
* The structured type columns that are used for value normalization and type casting.
*
* @return ColumnSchemaInterface[]
*/
public function getColumns(): array
{
return $this->columns;
}

/**
* The content of the structured type. It can be represented as
* - an associative `array` of column names and values;
* - an indexed `array` of column values in the order of structured type columns;
* - an `iterable` object that can be converted to an `array` using `iterator_to_array()`;
* - an `object` that can be converted to an `array` using `get_object_vars()`;
* - an `ExpressionInterface` object that represents a SQL expression.
*/
public function getValue(): array|object
{
return $this->value;
}

/**
* Returns the normalized value of the structured type, where:
* - values sorted according to the order of structured type columns;
* - indexed keys are replaced with column names;
* - missing elements are filled in with default values;
* - excessive elements are removed.
*
* If the structured type columns are not specified or the value is an `ExpressionInterface` object,
* it will be returned as is.
*/
public function getNormalizedValue(): array|object
{
$value = $this->value;

if (empty($this->columns) || $value instanceof ExpressionInterface) {
return $value;
}

if (is_object($value)) {
$value = $value instanceof Traversable
? iterator_to_array($value)
: get_object_vars($value);
}

$normalized = [];
$columnsNames = array_keys($this->columns);

foreach ($columnsNames as $i => $columnsName) {
$normalized[$columnsName] = match (true) {
array_key_exists($columnsName, $value) => $value[$columnsName],
array_key_exists($i, $value) => $value[$i],
default => $this->columns[$columnsName]->getDefaultValue(),
};
}

return $normalized;
}
}
Loading

0 comments on commit efb2368

Please sign in to comment.