Calcite function with default argument value - apache-calcite

I am using Apache Calcite to add some built-in functions. Now, I want to implement the GROUP_CONCAT function like MySQL to concat one column with one separator.
SELECT GROUP_CONCAT(n_name, '|') FROM nation GROUP BY n_lang;
The function class as follows:
public class SqlGroupConcatFunction extends SqlAggFunction {
public SqlGroupConcatFunction() {
super(
"GROUP_CONCAT",
null,
SqlKind.GROUP_CONCAT,
ReturnTypes.VARCHAR_2000,
InferTypes.FIRST_KNOWN,
OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING),
SqlFunctionCategory.STRING,
false,
false);
}
}
Now, I want this function can accept one parameter(without separator) or two parameters. When accept only one parameter, set the second parameter with default value.
I do not find any methods to set the default argument value in Calcite. Does have method to implement this feature?

In CALCITE-941 we added support for parameters are optional and have names. You can add the #Parameter annotation to an argument of user-defined function to specify its name and whether it is optional.
However, your use case is an aggregate function, and the #Parameter annotation does not work for these.
So, you will have to specify a SqlOperandTypeChecker argument to your constructor that allows 1 or 2 parameters. Try replacing
OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING),
with
OperandTypes.or(
OperandTypes.family(SqlTypeFamily.ANY),
OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)),
That will satisfy the validator. At some later stage, probably at sql-to-rel conversion time, you can intercept the 1-argument calls and make them into 2-argument calls.

Related

How to create an association to a parameterized CDS view?

I'm struggling to find the syntax to create an association between an extension of a parameterized CDS view and a parameterized CDS view. Their input parameters have the same names.
I've tried this:
extend view I_AAA with ZZ_AAA
association [0..1] to ZZ_BBB(P_param1 : $parameters.P_param1) as _ZZ_BBB
This gives the error: "unexpected keyword '(' (ON was expected)".
Or this:
extend view I_AAA with ZZ_AAA
association [0..1] to ZZ_BBB as _ZZ_BBB on $parameters.P_param1 = _ZZ_BBB.P_param1
This gives the error: "The entity ZZ_BBB requires parameter P_X".
The documentation states:
If the data source target of a specified CDS association is a CDS
entity with input parameters, parameters must be used after the name
_assoc to pass actual parameters to them. No parameters can be specified for a CDS association published as an element of a SELECT
list.
Putting parameters after _assoc is what I tried in the first example.
I've found a workaround: parameters have to be specified for each data element in the selection list using the following syntax:
association [0..1] to ZZ_BBB as _ZZ_BBB on $projection.operand1 = _ZZ_BBB.operand1
{
_ZZ_BBB(P_Param1:$parameters.P_Param1, P_Param2:$parameters.P_Param2).Element1 as SomeElement
...
I still would like to know if it is possible to specify a general parameter for the association that would affect all data elements. I'm going to accept this answer in the meantime.

Changing parameter values inside a loup

I have created a parameter to report values after each instance solve in an iterative program.I wan't my parameter to be indexed by the set that defines the number of iteration and have two other free indexes like so:
model.report=Param(model.iter,[],[])
I then wan't to create a function,that will be called into a while loop and that will create my model,solve an instance and give some values to some variables, whose names will be used as indexes in my report parameter, like so:
report(model.iter,'cost',model.i)=model.cost[i]
Where model.cost is my cost variable indexed by set i.
Is it possible to do that?
You are better off simply making model.report a Python dict. Then you can assign it entries with whatever keys you like:
model.report = dict()
...
for j in range(10):
# assumes model.i is a simple built-in type and not a Param
# (otherwise you would need to use model.i.value)
model.report[j,'cost',model.i] = model.cost[j]
There is no reason to use a Param in this context unless you are using those values in some kind of expression and you want to be able to update the expression at a later time by changing the value of the Param (i.e., you would use mutable=True when you declare the Param).

OrientDB: How to obtain an inline behavior from aggregate functions when doing UPDATE

OrientDB has a very particular way of handling SQL aggregate functions. It treats them as aggregate when only one parameter is given, and as inline when more than one parameter are given (in contrast to relying on the given data type, and then allowing aggregates of aggregates).
So, let's say we want to draw a guest list for an event.
This won't work:
SELECT title, SET(in("Attending").name) FROM event
And instead one has to run:
SELECT title, SET(in("Attending").name, in("Attending").name) FROM event
or
SELECT title, SET(in("Attending").name) FROM event GROUP BY title
These are not great solutions, but depending on the case, one of the two always works.
This is not the case when running updates, though...
What if we need to use inline an aggregate function that doesn't take multiple lists as parameters within a query that doesn't support group by?
UPDATE event SET guest_count = sum(in("Attending").family_size)
As logical as this query may seem, it doesn't work. It returns a cumulative sum on each line. None of the following works either:
UPDATE event SET guest_count = sum(in("Attending").family_size) GROUP BY title
UPDATE event SET guest_count = sum(in("Attending").family_size, in("Attending").family_size)
UPDATE event SET guest_count = sum(in("Attending").family_size, NULL)
UPDATE event SET guest_count = sum(in("Attending").family_size, 0)
UPDATE event SET guest_count = sum(in("Attending").family_size, [0])
Any solution?
In case you need to use a function as inline when you've only one parameter, then add a second one like "null":
SELECT first( out('friends').name, null ) as firstFriend FROM Profiles
See: http://orientdb.com/docs/2.0/orientdb.wiki/SQL-Functions.html#inline-mode

SOCI, pgsql function returning table record - type_conversion not working

I have a pgsql function declared as:
CREATE FUNCTION auth.read_session(session_id varchar) RETURNS auth.sessions
It returns one record from the table auth.sessions.
I have a SOCI type_conversion that works perfectly fine when
I run select * from auth.sessions where id = :id.
It works when a matching record is found and when the result is NULL.
However, when I change the statement to:
select * from auth.read_session('invalid');
I get exception:
Null value not allowed for this type while executing "select * from
auth.read_session('invalid')".
I tried with listing columns, passing soci::indicator, etc.
I cannot get it to work.
The exception comes from base type_conversion<>.
In type-conversion-traits.h there is a comment stating that:
// default traits class type_conversion, acts as pass through for
row::get() // when no actual conversion is needed.
Why is no conversion needed? Yes my function returns the record of table type "auth.sessions".
Should it return RECORD instead so that the conversion gets launched?
Apparently the only way this can be done is having the function return SETOF records. I believe the conversion is not needed as in my case the whole result was NULL and it cannot be somehow cast to my own type.
Returning a single record of table type works as long as there is any result.
It would work if a function was designed to always return a record, even if an empty one, but still a "record".
PadThink you're right about the return. To call this function the way you're doing you need to return a QUERY, TABLE, or SETOF records.
http://www.postgresql.org/docs/9.2/static/xfunc-sql.html

Django’s filter() method joins parameters using an AND statement. Is there an alternative that uses OR?

I’m trying to write a Django query that returns objects that match either of two parameters.
If I do this:
MyModel.objects.filter(parameter1=True, parameter2=True)
Then I only get objects that match both parameters.
What query can I use to select objects that match either parameter?
It's very simple. You just need to use special Q object.
As it is described here: https://docs.djangoproject.com/en/1.3/topics/db/queries/#complex-lookups-with-q-objects