How to insert empty array into jsonb column (pgsql) by Yii2? - postgresql

Created a migration with a new field of jsonb type, not null and default value = []. (example of stored data: ["235", "214"]) and add a rule to model [['unique_users'], 'safe']
public function up()
{
$connection = Yii::$app->getDb();
$sql = 'ALTER TABLE offer ADD unique_users jsonb not null default \'[]\'';
$command = $connection->createCommand($sql, []);
$command->queryAll();
}
Result: Added a unique_users field with a default value [] to each row. jsonb_typeof(unique_users) returns an array type.
Created needed query for test
select jsonb_array_length(unique_users) from test where unique_users #> '"19"'::jsonb
Result from PgAdmin:
It seemed that everything was ready. But after saving a new record with Yii2, I received a query error:
ERROR: you can not get the length of a scalar
And I saw that another value was recorded in the field - ""
I was tryed to add the validation rule to Model: ['unique_users', 'default', 'value' => '[]'],.
Result:
...with the same problem of query - value is not an array. jsonb_typeof(unique_users) returns an string type.
How to insert empty array into jsonb column?

I think you're accidentally sending an empty string as the value for your unique_users field. If the value would be completely empty it should take the default DB value for the column. Please make sure the unique_users field is completely empty (null) when saving.
You can however also do this with a default value rule. This should do the trick:
['unique_users', 'default', 'value' => json_encode([])],

['unique_users', 'default', 'value' => []],

Related

Postgresql - querying jsonb throws a syntax error

I have a table that has a column data of jsonb type.
create table event
(
id bigserial
primary key,
created_at timestamp with time zone default now() not null,
type text not null,
created_by text,
data jsonb,
event_time timestamp with time zone default now() not null
);
In that field I am saving a json object that looks like this:
{
"comment": "Changed by recipient",
"source": "Recipient page"
}
I would like to query values in that table by the value of the comment property of the data json object. Something like this in based by examples [here][1]:
select * from event
where type = 'pickup-data-changed'
and data -> 'comment' = 'Changed by recipient'
If I query like that I get an invalid token error:
[22P02] ERROR: invalid input syntax for type json Detail: Token "Changed" is invalid. Position: 104
What am I doing wrong here?
If I do it as a double arrow like suggested in the comments:
select * from event
where type = 'pickup-data-changed'
and data ->-> 'comment' = 'Changed by recipient'
I get an error:
[42883] ERROR: operator does not exist: jsonb ->-> unknown Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
How can I make this query work?
[1]: https://kb.objectrocket.com/postgresql/how-to-query-a-postgres-jsonb-column-1433
I get an invalid token error. What am I doing wrong here?
data -> 'comment' returns a value of type jsonb, so the right hand side of the comparison 'Changed by recipient' is parsed as JSON as well - and it's invalid JSON. To create a JSON string value to compare against, you'd need to write
… data -> 'comment' = '"Changed by recipient"'
If I do it as a double arrow like suggested in the comments, data ->-> 'comment'
The comments suggested
… data ->> 'comment' = 'Changed by recipient'
not ->->.
alternatives:
select * from event
where type = 'pickup-data-changed'
and data -> 'comment' = '"Changed by recipient"'::jsonb;
or
select * from event
where type = 'pickup-data-changed'
and data['comment'] = '"Changed by recipient"'::jsonb;

Error while inserting array and JSON into PostgreSQL using Codeigniter 3.1.10

I am working on a project which uses Codeigniter 3.1.10 framework and PostgreSQL as database. and I am passing a form value from controller to model like the following:
$data = array(
'field1' => $value1,
'field2' => $value2,
'field3' => $value3
)
$result = $this->modal->function_insert($data);
where value 1 is string, value2 is an array and value3 is JSON. I am trying to insert data to PostgreSQL. In table, data types are correctly defined which are respectively text, text[] and jsonb.
While in modal my code goes like this:
public function function_insert($data)
{
$result = $this->db->insert('table_name', $data);
}
I am getting the following error from codeigniter:
Severity: Notice
Message: Array to string conversion
Filename: database/DB_driver.php
Line Number: 1471
Backtrace:
And the query shows like this:
INSERT INTO "table_name" ("field1", "field2", "field3") VALUES ('1', Array, Array)
Is there any issue in my code, data that I pass to model or something else.
It is not possible to direct insert of PHP Arrays into the database. You have to do some tricks like in the below examples.
1. For inserting PHP Array into Postgres Column value of type Array
$phpArray = [1,3,3];
$pgColValue = '{'.implode($phpArray,',').'}';
echo $pgColValue;
//outputs
{1,3,3} // Treated as postgres array
2. For inserting JSON. Use PHP built in function name json_encode() for conveting PHP array to JSON string.
$phpArray = [1,3,3];
$pgColValue = json_encode($phpArray);
echo $pgColValue;
//outputs
[1,3,3]

How to return a plain value from a Knex / Postgresql query?

I'm trying to return a simple, scalar string value from a Postgres DB using Knex. So far, everything I do returns a JSON object with a key (the column name) and the value, so I have to reach into the object to get the value. If I return multiple rows, then I get multiple JSON objects, each one repeating the key.
I could be returning multiple columns, in which case each row would at least need to be an array. I'm not looking for a special case where specifying a single column returns the value without the array -- I'm OK reaching into the array. I want to avoid the JSON object with the repetitive listing of column names as keys.
I've scoured the Knex docs but don't see how to control the output.
My table is a simple mapping table with two string columns:
CREATE TABLE public._suite
(
piv_id character(18) NOT NULL,
sf_id character(18) NOT NULL,
CONSTRAINT _suite_pkey PRIMARY KEY (piv_id)
)
When I build a query using Knex methods like
let myId = 'foo', table = '_suite';
return db(table).where('piv_id', myId).first(['sf_id'])
.then( function(id) { return(id); });
I get {"sf_id":"a4T8A0000009PsfUAE"} ; what I want is just "a4T8A0000009PsfUAE"
If I use a raw query, like
return db.raw(`select sf_id from ${table} where piv_id = '${myId}'`);
I get a much larger JSON object describing the result:
{"command":"SELECT","rowCount":1,"oid":null,"rows":[{"sf_id":"a4T8A0000009Q9HUAU"}],"fields":[{"name":"sf_id","tableID":33799,"columnID":2,"dataTypeID":1042,"dataTypeSize":-1,"dataTypeModifier":22,"format":"text"}],"_parsers":[null],"RowCtor":null,"rowAsArray":false}
What do I have to do to just get the value itself? (Again, I'm OK if it's in an array -- I just don't want the column names.)
Take a look at the pluck method.
db(table).where('piv_id', myId).pluck('sf_id'); // => will return you ["a4T8A0000009PsfUAE"]

How to add not null constraint in seqeulize if records with null already exist?

I had a table.
I added a new column.
Even though I had set default value in sequelize model, those columns still ended up empty.
So I get error Unhandled rejection SequelizeDatabaseError: column "col_name" contains null values
How do you populate new column with default values upon creation so not null constraint is not broken.
You can update the values first:
update t
set col_name = ?
where col_name is null;
Then add the not null constraint.
Even though I had set default value in sequelize model
I suspect there is a discrepancy between the migration and model. To insert the column with a migration containing a default value use defaultValue in your migration.
The following is a working example:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('tests', 'new_column', {
defaultValue: 'test',
type: Sequelize.STRING
})
}
}
Before running sequelize db:migrate
After sequelize db:migrate:
The documentation for the options object in addColumn is hard to find, it's listed for a different method

Insert with defaults using subquery in knex

I have this query;
knex('metrics').insert(function() {
this.select('metric as name')
.from('stage.metrics as s')
.whereNotExists(function() {
this.select('*')
.from('metrics')
.where('metrics.name', knex.raw('s.metric'))
})
})
The table metrics has two columns; an id, which is incrementing, and name. I expected this to insert into the name column because the subquery has one column, labeled name, and default id. however, instead it complains that I am providing a column of type character varying for my integer column id. How do I make it explicit that I want the id to take the default value?
This can do the trick
knex('metrics').insert(function() {
this
.select([
knex.raw('null::bigint as id'), // or any other type you need (to force using default value you need to pass explicitly null value to insert query)
'metric as name'
])
.from('stage.metrics as s')
.whereNotExists(function() {
this.select('*')
.from('metrics')
.where('metrics.name', knex.raw('s.metric'))
})
})
I know, looks a bit hacky. Would be great to see something in knex API like (example below is a proposal and not a working example)
knex('table_name')
.insert(
['name', 'surname'],
function () {
this.select(['name', 'surname']).from('other_table'))
}
)
Which produces
insert into table_name (name, surname) select name, surname from other_table;
I'm not sure about this interface, but you got the point. Like explicitly write fields you want to insert.