db icon indicating copy to clipboard operation
db copied to clipboard

Support Query result typing

Open smirnov-e opened this issue 10 months ago • 13 comments

Currently

  • data returned by ActiveQuery is typed if column name matches table column name.
  • data returned by Query is mostly untyped.
CREATE TABLE test (
     id INTEGER GENERATED ALWAYS AS IDENTITY,
     int INTEGER,
     float FLOAT,
     int_array INT[],
     text_array TEXT[]
 );

CREATE TYPE currency_money AS
(
  value numeric(19,4),
  currency_code char(3)
);
 
 INSERT INTO test 
 ("int", "float", int_array, text_array)
VALUES (1, 2.33, ARRAY[1, 2, 3], ARRAY['test']);

Code like this

$db->createCommand('SELECT * FROM test')->queryAll();
// or like this

$db->createCommand('SELECT 1 AS id, 
1 AS int, 
2.3 AS float, 
ARRAY[1,2] AS int_array, 
ARRAY['test'] AS text_array,
ARRAY['(0,USD)'::currency_money] as money_array
')->queryAll();

will return

[
    [
        'id' => 1,
        'int' => 1,
        'float' => '2.33', // type lost
        'int_array' => '{1,2,3}', // type lost
        'text_array' => '{test}', // type lost
        'money_array' => '{"(0.0000,USD)"}', // type lost
    ],
]

So currently in Yii3 and Yii2 to get typed data you have to extensively map values and manually control returned types.

// like this
$price = (new Query($db))->select(['price'])->from('product')->scalar();
$pricePhpType = $tableSchema->getColumn('price')->phpTypecast($price);
// or like this
array_map($rows, function(array $row) {
     $row['int_array'] = (new ArrayParser())->parse($row['int_array']));
     return $row;
})

Other way is to use PDOStatement::getColumnMeta which returns rather fine type information:

ARRAY(
    [pgsql:oid] => 16413
    [pgsql:table_oid] => 0
    [native_type] => _currency_money
    [pdo_type] => 2
    [name] => money_array
    [len] => -1
    [precision] => -1
)
[native_type] => int4 // int 
[native_type] => numeric // int 
[native_type] => _int4 // array of INT.  "_<typename>" means array of <typename>
[native_type] => _text // array of strings
[native_type] => _currency_money // array of currency_money (custom type)

Moreover, there is [pgsql:oid] => <some int> that refers to type information stored in pg_type table (SELECT * FROM pg_type WHERE oid = <some int>), so in theory it's possible to correctly typecast any possible type.

Main catch seems to be that it should be optional.

P.S. I can try to make pull request.

smirnov-e avatar Aug 17 '23 21:08 smirnov-e