How to write where Between query in Yii2 with integer as a second parameter - postgresql

I have this query in PostgreSQL that I want to implement in my model in Yii2:
SELECT *
FROM some_table
WHERE 1492257600 BETWEEN start AND end
start and end are attributes of some_table.
In my model, I tried the following but, no matter how I write it, it keeps throwing the same error.
$results = static::find()->where(['between', 1492257600, 'start', 'end'])
->all();
This is the error I'm getting:
Undefined column: 7 ERROR: column \"1492257600\" does not exist
The SQL being executed was: SELECT * FROM \"some_table\" WHERE \"1492257600\" BETWEEN 'start' AND 'end'"
When the query is built, these symbols " " are added to the integer, so PostgreSQL thinks it is a column of the table.
Can anyone tell me how to write the where between correctly?

There is dedicated BetweenColumnsCondition expression for such cases:
use yii\db\conditions\BetweenColumnsCondition;
$results = static::find()
->where(new BetweenColumnsCondition(1492257600, 'BETWEEN', 'start', 'end'))
->all();
It automatically quotes column names and escapes value, so it should be more convenient than simple yii\db\Expression.
Also note that WHERE 1492257600 BETWEEN start AND end may be slower than WHERE 1492257600 >= start AND 1492257600 <= end, I suggest to do some performance test if your table may grow big and usage indexes is crucial.

I don't have experience with postgresql but this will work same as between,
$results = self::find()
->where(['>=', 'start', 1492257600])
->andWhere(['<=', 'end', 1492257600])
->all();

Refer to this: Yii2 Doc
When an Expression object is embedded within a SQL statement or fragment, it will be replaced with the $expression property value without any DB escaping or quoting.
so You'll have something like this:
$expression = new \yii\db\Expression('1492257600 BETWEEN start AND end');
$results = self::find()->where($expression)
->all();

Related

Postgres: Python: TypeError: SQL.__init__() takes 2 positional arguments but 3 were given

Hello I am getting error from my code, can someone help me please?
def query_builder(self, field_name, table_name, pkey, id):
queryx=sql.SQL("select {field} from {table} where {pkey} = %s",(id)).format(
field=sql.Identifier(field_name),
table=sql.Identifier(table_name),
pkey=sql.Identifier(pkey))
self.cur.execute(queryx.as_string(self.conn))
I'm going to assume you are using psycopg2.
If so the issues are, first:
"select {field} from {table} where {pkey} = %s",(id) ..."
Do not include the argument (id) in the string. Also this is not proper form for a single value in a tuple. Python requires it be (id,), note the comma.
Second:
self.cur.execute(queryx.as_string(self.conn))
Should be:
self.cur.execute(queryx, (id,))
The execute is where you supply the argument. Also the composable sql.SQL(...) can be passed directly to execute without being run through as_string. See here sql for more examples.
UPDATE
To use "*" there are two ways:
cur.execute(sql.SQL("select * from {table} where {pkey} = %s).format(table.sql.Identifier(table_name), pkey=sql.Identifier(pkey))
--OR
cur.execute(sql.SQL("select {field} from {table} where {pkey} = %s).format(field=sql.SQL("*"), table=sql.Identifier(table_name), pkey=sql.Identifier(pkey))
Warning, the second does allow for SQL injection as sql.SQL() does not escape values.
As to multiple fields the sql section of the docs has multiple examples. For instance:
If part of your query is a variable sequence of arguments, such as a comma-separated list of field names, you can use the SQL.join() method to pass them to the query:
query = sql.SQL("select {fields} from {table}").format(
fields=sql.SQL(',').join([
sql.Identifier('field1'),
sql.Identifier('field2'),
sql.Identifier('field3'),
]),
table=sql.Identifier('some_table'))

Why is my UPDATE query with jsonb popping an error?

I'm trying to update a row in my PostgreSQL database and it's saying it's not finding the x column. the thing is the column pg is trying to find is actually a parameter for the new value in the jsonb_set function, so I'm at my wits end.
It's hard to explain, so I included the query and the error it throws.
Tried adding quotes, double-quotes, brackets, inside and out... didn't work.
UPDATE public.sometable
SET somecolumn = jsonb_set(somecolumn, '{firstKey, secondKey}', someInputString), update_date=NOW(), update_username="someone#somewhere.com"
WHERE id=1
RETURNING *
I'm expecting the value of the row I'm updating to be returned, instead I get:
ERROR: column "someInputString" does not exist
LINE 1: ...n = jsonb_set(somecolumn , '{firstKey, secondKey}', someInputString)...
You have to deliver a valid json value as the third argument of the function:
UPDATE public.sometable
SET
somecolumn = jsonb_set(somecolumn, '{firstKey, secondKey}', '"someInputString"'),
update_date = now(),
update_username = 'someone#somewhere.com'
WHERE id = 1
RETURNING *
Note, I guess update_username is a text, so you should use single quotes for a simple text.
Db<>fiddle.

Psycopg2 functions in parameters

I'm wonder how I can pass a Postgres function to a field in an insert statement in Psycopg2. Looked everywhere in the doc but can't find the answer.
The code:
From the DBconn class:
def insert(self, table, data):
fields = data.keys()
sql = "INSERT INTO " + table + "("
sql += ",".join(fields) + ") VALUES (" + ",".join(["%s"]*len(fields)) + ");"
self.cur.execute(sql, data.values())
And when calling this function:
db.insert("tblpeople", {"person_name":"john", "org_id":"currval('tblorganisations_org_id_seq')"})
So the question is how can I tell Psycopg2 that the org_id value is a Postgres function?
Tnx!
Your code is producing the statement:
INSERT INTO tblpeople (org_id,person_name) VALUES (%s,%s);
and you are passing 2 arguments. That won't work. You want to produce this code:
INSERT INTO tblpeople (org_id,person_name)
VALUES (currval('tblorganisations_org_id_seq'),%s);
and then pass a single argument. I don't see any way of doing that with your current data structure. If you add another piece of data to your dictionary, like:
{
"text":{"person_name":"john"},
"function":{"org_id":"currval('tblorganisations_org_id_seq')"}
}
and change your constructor to produce the proper query based upon the 'function' key, or, you could 'hard code' your query builder to recognize the 'currval(' being at the beginning of the value. Anyway, the basic problem is you are passing the data as a string argument which won't be handled correctly.
-g

DBIx::Class - add a read-only synthesized column (defined by SQL) to a result class?

Is it possible to add a 'synthesized' column to a DBIx::Class result class? The value of the synthesized column would be defined by a SQL expression of the current row. For example, if a row had the columns first and last, I could synthesize a new read-only column whose definition is \"me.first || ' ' || me.last" (this is Oracle SQL syntax).
Close to what I want is listed under "COMPUTED COLUMNS" in the DBIx::Class documentation. However, in that case it seems that the column is already defined on the database side. My synthesized column is not in the table schema.
Failing that, is it possible to add my SQL expression to the generated SELECT statement when I search the resultset?
(The above example SQL is misleading - I need to execute a SQL expression that involves database functions so I can't just implement it in perl.)
Perhaps I'm missing something, but I don't see why you can't just add a method to your result class like this:
sub name {
my $self = shift;
return $self->first . ' ' . $self->last;
}
If the calculation must be done on the database side (after your earlier comment), then use the temporary column idea that i suggested, together with some direct SQL. Assuming that you don't wish to search on the field, then something like the following should work:
my $rs = My::Schema->resultset('people');
my #columns_as = $rs->result_source->columns;
my #columns = map { "me.$_" } #columns_as;
my #people = $rs->search(
{ ... where conditions here ... },
{
select => [ #columns, \"me.first || ' ' || me.last" ],  # scalar ref for direct SQL
as => [ #columns_as, 'full_name' ],
order_by => 'full_name',
... etc ...
}
);
# then
print $_->full_name."\n" foreach #people; # now set on the object...
It should theoretically be possible to just specify your additional select and as columns using +select and +as, but I was unable to get these to work properly (this was a year or so ago). Can't recall exactly why now...

Postgres INSERT INTO query bug?

What's wrong with the following Postgres query?
INSERT INTO kayak.airports(name, x, y, city) VALUES( $name, $x, $y, $city)
WHERE airport_id='$airport_id
EDIT (thanks Donnie for helping me make progress) :
I tried:
$query="UPDATE kayak.airports SET name=$name, x = $x, y = $y, city = $city
WHERE airports.airport_id='$airport_id'";
It said "column 'Brisbane' doesn't exist" (Brisbane is the first city to be inserted. ) I took out everything between SET and WHERE except for "x=$x" and those were successfully inserted. Ditto for "y=$y". When only leaving in name=$name it says
"Query failed: ERROR: syntax error at or near "International" LINE 1: UPDATE kayak .airports SET name=Brisbane International WHERE... ^"
Your query string is not quoted. Do not use PHP variable interpolation for building SQL queries, because this will leave your script or application vulnerable to an SQL injection attack.
Instead, use parameterized queries. Thus, your query above becomes:
$query = 'UPDATE kayak.airports SET name = $1, x = $2, y = $3, city = $4'.
'WHERE airports.airport_id = $5';
Then, you will use the parameterized query calling function pg_query_paramsto pass the required parameters:
$result = pg_query_params($query, $parameters)
Where $parameters is an array of parameters.
Also note that the $query string is single-quoted, because the $n placeholders are not there for interpolation. This prevents any mistakes (such as typing a real variable name by bumping a letter first) and eliminates any possibility of SQL injection.
You're attempting to insert literal values. A where clause makes no sense.
For insert, you can only use where in an insert ... select to limit what the select is returning.
Perhaps you actually want to update an existing record?
For me, if I get an error that a column doesn't exist, it's usually a tipoff that I've quoted something incorrectly (or not at all).
This is borne out by the error message from your attempt to update only the name field:
ERROR: syntax error at or near "International" LINE 1:
(The carat should point right to the problem area in the query.)
The value you are passing to the name field in your UPDATE statement needs to be quoted, just like the value you're passing to airport_id. (I'm going to take a wild guess that x and y are integers, which wouldn't require quoting, which is why you don't get an error when you try to update just those field.) (I'm going to take another wild guess that the value you pass to city will need to be quoted too, but you will probably figure that out shortly. :) )
The end result expanded UPDATE should look something like this:
UPDATE kayak.airports
SET name='Brisbane International', x = 123, y = 456, city = 'Brisbane'
WHERE ...