Releases: MechanicalRabbit/FunSQL.jl
v0.11.0
This release introduces some backward-incompatible changes. Before upgrading,
please review these notes.
-
The
Iterate()
node, which is used for assembling recursive queries, has
been simplified. In the previous version, the argument ofIterate()
must
explicitly refer to the output of the previous iteration:Base() |> Iterate(From(:iteration) |> IterationStep() |> As(:iteration))
In v0.11.0, this query is written as:
Base() |> Iterate(IterationStep())
Alternatively, the output of the previous iteration can be fetched with the
From(^)
node:Base() |> Iterate(From(^) |> IterationStep())
-
Function nodes with names
Fun."&&"
,Fun."||"
, andFun."!"
are now
serialized as logical operatorsAND
,OR
, andNOT
. Broadcasting
notationp .&& q
,p .|| q
,.!p
is also supported.FunSQL interpretation of
||
conflicts with SQL dialects that use||
to represent string concatenation. Other dialects use functionconcat
.
In FunSQL, always useFun.concat
, which will pick correct serialization
depending on the target dialect. -
Rendering of
Fun
nodes can now be customized by overriding the method
serialize!()
, which is dispatched on the node name. The following names
have customized serialization:and
,between
,case
,cast
,concat
,
current_date
,current_timestamp
,exists
,extract
,in
,
is_not_null
,is_null
,like
,not
,not_between
,not_exists
,
not_in
,not_like
,or
. -
Introduce template notation for representing irregular SQL syntax.
For example,Fun."SUBSTRING(? FROM ? FOR ?)"(Get.zip, 1, 3)
generates
SUBSTRING("location_1"."zip" FROM 1 to 3)
.In general, the name of a
Fun
node is interpreted as a function name,
an operator name, or a template string:-
If the name has a custom
serialize!()
method, it is used for rendering
the node. Example:Fun.in(Get.zip, "60614", "60615")
. -
If the name contains a placeholder character
?
, it is interpreted as
a template. Placeholders are replaced with the arguments of theFun
node. Use??
to represent a literal?
character. Wrap the template
in parentheses if this is necessary to make the syntax unambiguous. -
If the name contains only symbol characters, or if the name starts or
ends with a space, it is interpreted as an operator name. Examples:
Fun."-"(2020, Get.year_of_birth)
,Fun." ILIKE "(Get.city, "Ch%")
,
Fun." COLLATE \"C\""(Get.state)
,Fun."CURRENT_TIME "()
. -
Otherwise, the name is interpreted as a function name. The function
arguments are separated by a comma and wrapped in parentheses. Examples:
Fun.now()
,Fun.coalesce(Get.city, "N/A")
.
-
-
Agg
nodes also supportserialize!()
and template notation. Custom
serialization is provided forAgg.count()
andAgg.count_distinct(arg)
. -
Remove
distinct
flag fromAgg
nodes. To representCOUNT(DISTINCT …)
,
useAgg.count_distinct(…)
. Otherwise, use template notation, e.g.,
Agg."string_agg(DISTINCT ?, ? ORDER BY ?)"(Get.state, ",", Get.state)
. -
Function names are no longer automatically converted to upper case.
-
Remove ability to customize translation of
Fun
nodes to clause tree
by overriding thetranslate()
method. -
Remove clauses
OP
,KW
,CASE
, which were previously used for expressing
operators and irregular syntax. -
Add support for table-valued functions. Such functions return tabular data
and must appear in aFROM
clause. Example:From(Fun.regexp_matches("2,3,5,7,11", "(\\d+)", "g"), columns = [:captures]) |> Select(Fun."CAST(?[1] AS INTEGER)"(Get.captures))
SELECT CAST("regexp_matches_1"."captures"[1] AS INTEGER) AS "_" FROM regexp_matches('2,3,5,7,11', '(\d+)', 'g') AS "regexp_matches_1" ("captures")
-
To prevent duplicating SQL expressions,
Define()
andGroup()
may create
an additional subquery (#11). Take, for example:From(:person) |> Define(:age => 2020 .- Get.year_of_birth) |> Where(Get.age .>= 16) |> Select(Get.person_id, Get.age)
In the previous version, the definition of
age
is replicated in both
SELECT
andWHERE
clauses:SELECT "person_1"."person_id", (2020 - "person_1"."year_of_birth") AS "age" FROM "person" AS "person_1" WHERE ((2020 - "person_1"."year_of_birth") >= 16)
In v0.11.0,
age
is evaluated once in a nested subquery:SELECT "person_2"."person_id", "person_2"."age" FROM ( SELECT "person_1"."person_id", (2020 - "person_1"."year_of_birth") AS "age" FROM "person" AS "person_1" ) AS "person_2" WHERE ("person_2"."age" >= 16)
-
Similarly, aggregate expressions used in
Bind()
are evaluated in a separate
subquery (#12). -
Fix a bug where FunSQL fails to generate a correct
SELECT DISTINCT
query
when key columns ofGroup()
are not used by the following nodes. For
example, the following query pipeline would fail to render SQL:From(:person) |> Group(Get.year_of_birth) |> Group() |> Select(Agg.count())
Now it correctly renders:
SELECT count(*) AS "count" FROM ( SELECT DISTINCT "person_1"."year_of_birth" FROM "person" AS "person_1" ) AS "person_2"
-
Article Two Kinds of SQL Query Builders is added to documentation.
-
Where possible, pretty-printing replaces
Lit
nodes with their values.
v0.10.2
v0.10.1
v0.10.0
- Add
SQLCatalog
type that encapsulates information about database
tables, the target SQL dialect, and a cache of rendered queries. - Add function
reflect
to retrieve information about available
database tables. From(::Symbol)
can refer to a table in the database catalog.- Add a dependency on
DBInterface
package. - Add type
SQLConnection <: DBInterface.Connection
that combines
a raw database connection and the database catalog. Also add
SQLStatement <: DBInterface.Statement
. - Implement
DBInterface.connect
to create a database connection
and callreflect
to retrieve the database catalog. - Implement
DBInterface.prepare
andDBInterface.execute
to
render a query node to SQL and immediately compile or execute it. - Allow
render
to take aSQLConnection
or aSQLCatalog
argument. - Rename
SQLStatement
toSQLString
, drop thedialect
field. - Update the guide and the examples to use the DBInterface API
and improve many docstrings.
v0.9.2
v0.9.1
Join
: addoptional
flag to omit theJOIN
clause in the case when
the data from the right branch is not used by the query.With
: addmaterialized
flag to make CTEs withMATERIALIZED
and
NOT MATERIALIZED
annotations.- Add
WithExternal
node that can be used to prepare the definition for
aCREATE TABLE AS
or aSELECT INTO
statement. - Rearranging clause types: drop
CTE
clause; addcolumns
toAS
clause;
addNOTE
clause.
v0.9.0
v0.8.2
v0.8.1
v0.8.0
- Refactored the SQL translator to make it faster and easier to maintain.
- Improved error messages.
- Include columns added by
Define
to the output. - Report an error when
Agg
is used withoutGroup
. - Deduplicate identical aggregates in a
Group
subquery. - Support for
WITH
clause. - Updated the Tutorial/Usage Guide.