Create RelNode of a select query with concat - apache-calcite

I went through the documentation of Apache Calcite. Is the relNode correct for the following query in BigQuery?
SELECT CONCAT('a or b',' ', '\n', first_name)
FROM foo.schema.employee
WHERE first_name = 'name';
relNode = builder
.scan("schema.employee")
.filter(builder.call(SqlStdOperatorTable.EQUALS,
builder.field("first_name"),
builder.literal("name"))
.project(builder.call(SqlStdOperatorTable.CONCAT,
builder.literal("a or b"),
builder.literal(" "),
builder.literal("\\n"),
builder.field(first_name)))
.build()

That looks correct at a glance. I would suggest you confirm by looking at the query results and also by converting your RelNode to SQL.

Related

RelNode of a query in which FROM clause itself has a query

I want to achieve result from a table where I ORDER BY column id and I don't want id to be present in the result. I can achieve this using the following query.
SELECT COALESCE (col1, '**')
FROM (select col1, id FROM myDataSet.myTable WHERE col4 = 'some filter' ORDER BY id);
Now, I want to create a RelNode of the above query. As far as I know, in calcite, to perform table scan, there are only two methods scan(String tableName) and scan(Iterable<String> tableNames). Is there a way to scan(RelNode ) ? How to do this ?
The query
select col1, col2, col2 FROM myDataSet.myTable WHERE col4 = 'some filter' ORDER BY id
should also give you the desired result.
If you want to represent the query you have written more directly, you would start by constructing a RelNode for the query in the from clause, starting with a scan of myDataSet.myTable, adding the filter, and the order. Then you can project the specific set of columns you want.
Just simply create a RelNode of inner subquery and create another projection on top of it. Like so.
builder.scan('myTable')
.filter(builder.call(SqlStdOperator.EQUALS, builder.field(col4), builder.literal('some filter') )))
.project(builder.field('col1'), builder.field('id'))
.sort(builder.field('id'))
.project(builder.call(SqlStdOperator.COALESCE(builder.field('col1'), builder.literal('**'))))
.build()

Using RelBuilder how to create SELECT and WHEREclause?

I have the following code where I am trying to create a relational algebra:
final RelNode relNode = relBuilder.scan(index).build();
I am looking for a sample code where I can create a SELECE and WHERE clause in the above code.
Have you read the documentation? There are several examples there.
final RelNode node = builder
.scan("EMP")
.aggregate(builder.groupKey("DEPTNO"),
builder.count(false, "C"),
builder.sum(false, "S", builder.field("SAL")))
.filter(
builder.call(SqlStdOperatorTable.GREATER_THAN,
builder.field("C"),
builder.literal(10)))
.build();
System.out.println(RelOptUtil.toString(node));
is equivalent to SQL
SELECT deptno, count(*) AS c, sum(sal) AS s
FROM emp
GROUP BY deptno
HAVING count(*) > 10

How to enforce Django to use "JOIN VALUES"

I'm having a performance problem where I need to replace section of my query statement. Right now I have a the following:
select count(*) FROM "mytable" WHERE "field" IN ('v1', 'v2', ..., 'vN');
this can be translated to Django ORM:
Mytable.objects.all().filter(field__in=[myvalues]).count()
I need to do the following though:
select count(*) FROM "mytable" JOIN (values ('v1', 'v2', ..., 'vN')) as lookup(value) on lookup.value = "mytable".field;
Is there a way to add this to the ORM? I need to do with ORM because I already have other filters. Worst case scenario I thought of getting the query string and adding there manually...
I'm using Postgresql 9.6
I found a way after reading over and over the documentation. I even found a patch that was not merged a while ago.
It doesn't really do the join, but it works much faster than using __in straightforward.
What I'm doing is executing a RawSQL() that was introduced in Django 2.0 and with that result I do the __in again.
So here is a code example:
query = """select myfield from mytable join (values
('v1'), ('v2'), ..., ('vN')
) as lookup(value) on lookup.value = mytable.myfield"""
r = RawSQL(query, [])
mymodel.filter(myfield__in=r)
Now it takes miliseconds instead of minutes!

How to get SQL from Django QuerySet with filter parameters properly quoted?

MyModel.objects.filter(name="my name", date__lte=datetime.now()).query
Outputs:
SELECT "mymodel"."id", "mymodel"."name", "mymodel"."date"
FROM "mymodel"
WHERE ("mymodel"."name" = my name AND "mymodel"."date" <= 2016-02-24 20:24:00.456974+00:00)
Which is not a valid SQL (filter parameters are unquoted).
How can I get the exact SQL that will be executed, which is:
SELECT "mymodel"."id", "mymodel"."name", "mymodel"."date"
FROM "mymodel"
WHERE ("mymodel"."name" = 'my name' AND "mymodel"."date" <= '2016-02-24 20:24:00.456974+00:00'::timestamptz)
This isn't fully answering it, but for my purposes I wanted to be able to rerun the query as raw sql. I was able to get sql and params using:
queryset = MyModel.objects.filter(name="my name", date__lte=datetime.now())
sql, sql_params = queryset.query.get_compiler(using=queryset.db).as_sql()
Then I could use these values to rerun the query as raw:
MyModel.objects.raw(sql, sql_params)

Escaping queries in Django

I have the following method:
def select_query(self):
sql = "SELECT * FROM {t} WHERE 1".format(t=self._meta.db_table)
for column_name in self.distinguishing_column_names():
sql = sql + " AND {c} = {v}".format(c=column_name, v=getattr(self, column_name))
return sql
This will give me a query like this:
SELECT * FROM customer WHERE 1 AND name = JOHN SMITH AND customer_number = 11423 AND social_security_number = 1234567890 AND phone = 2323523353
Obviously, that's not going to work. Is there a way to get Django to quote this for me?
Note: I'm not asking for a prepared statement. That's something different.
Do you need to return a query this way? The proper way would be to call cursor with the query and the params as argument:
Does Python support MySQL prepared statements?
The correct way to format a query seems to be:
query = query % db.literal(args)
Where db is a mysql.Connection (or presumably any connection)
Apparently the answer is "no."