Files
gnatcoll-db/sql/gnatcoll-sql.adb
Fedor Rybin 5451547525 Make GNATCOLL.SQL Ada 2020 compatible
Qualify the aggregate to fix compilation error in 2020 mode.

for U121-029

Change-Id: I57c0696eb01ca2ab32106c6760099a402e41910d
(cherry picked from commit 33b963c5e4)
2021-05-20 10:25:55 +02:00

2884 lines
78 KiB
Ada

------------------------------------------------------------------------------
-- G N A T C O L L --
-- --
-- Copyright (C) 2005-2021, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
with Ada.Calendar; use Ada.Calendar;
with Ada.Containers; use Ada.Containers;
with Ada.Unchecked_Deallocation;
with GNAT.Strings; use GNAT.Strings;
package body GNATCOLL.SQL is
use Table_List, Field_List, Criteria_Lists, Table_Sets;
use When_Lists, Query_Pointers;
use type Boolean_Fields.Field;
Comparison_Like : aliased constant String := " LIKE ";
Comparison_ILike : aliased constant String := " ILIKE ";
Comparison_Not_Like : aliased constant String := " NOT LIKE ";
Comparison_Not_ILike : aliased constant String := " NOT ILIKE ";
Comparison_Overlaps : aliased constant String := " OVERLAPS ";
Comparison_Any : aliased constant String := " = ANY (";
Comparison_Parenthesis : aliased constant String := ")";
procedure Unchecked_Free is new Ada.Unchecked_Deallocation
(SQL_Table'Class, SQL_Table_Access);
function Combine
(Left, Right : SQL_Criteria; Op : Criteria_Combine) return SQL_Criteria;
-- Combine the two criterias with a specific operator.
procedure Append_Tables
(From : SQL_Field_List; To : in out Table_Sets.Set);
-- Append all tables referenced in From to To.
function To_String (Names : Table_Names) return String;
function To_String (Self : Table_Sets.Set) return Unbounded_String;
-- Various implementations for To_String, for different types
package Any_Fields is new Data_Fields (SQL_Field);
type SQL_Field_Any is new Any_Fields.Field with null record;
-------------------
-- As field data --
-------------------
-- Used when a field is renamed via "anything AS name"
type As_Field_Internal is new SQL_Field_Internal with record
As : GNAT.Strings.String_Access;
Renamed : SQL_Field_Pointer;
end record;
overriding procedure Free (Self : in out As_Field_Internal);
overriding function To_String
(Self : As_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String;
overriding procedure Append_Tables
(Self : As_Field_Internal; To : in out Table_Sets.Set);
overriding procedure Append_If_Not_Aggregate
(Self : access As_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean);
--------------------------
-- Multiple args fields --
--------------------------
-- Several fields grouped into one via functions, operators or other. Such
-- fields are not typed ("field1 operator field2 operator field3 ...")
type Multiple_Args_Field_Internal is new SQL_Field_Internal with record
Func_Name : GNAT.Strings.String_Access; -- can be null
Separator : GNAT.Strings.String_Access;
Suffix : GNAT.Strings.String_Access; -- can be null
List : Field_List.Vector;
end record;
overriding function To_String
(Self : Multiple_Args_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String;
overriding procedure Append_Tables
(Self : Multiple_Args_Field_Internal; To : in out Table_Sets.Set);
overriding procedure Append_If_Not_Aggregate
(Self : access Multiple_Args_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean);
overriding procedure Free (Self : in out Multiple_Args_Field_Internal);
----------------------
-- Aggregate fields --
----------------------
-- Representing an sql aggregate function.
type Aggregate_Field_Internal is new SQL_Field_Internal with record
Func : GNAT.Strings.String_Access;
-- Func might be null if we only want to represent as criteria as
-- a field
Params : SQL_Field_List;
Criteria : SQL_Criteria;
Order_By : SQL_Field_List;
end record;
overriding procedure Free (Self : in out Aggregate_Field_Internal);
overriding function To_String
(Self : Aggregate_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String;
overriding procedure Append_Tables
(Self : Aggregate_Field_Internal; To : in out Table_Sets.Set);
overriding procedure Append_If_Not_Aggregate
(Self : access Aggregate_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean);
-----------------
-- Query fields --
-----------------
-- a SQL query represented as a field
type Query_Field_Internal is new SQL_Field_Internal with record
Query : SQL_Query;
end record;
overriding function To_String
(Self : Query_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String;
overriding procedure Append_Tables
(Self : Query_Field_Internal; To : in out Table_Sets.Set) is null;
overriding procedure Append_If_Not_Aggregate
(Self : access Query_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean) is null;
-----------------
-- Sort fields --
-----------------
-- Fields used in the "ORDER BY" clauses
type Sorted_Field_Internal is new SQL_Field_Internal with record
Ascending : Boolean;
Sorted : SQL_Field_Pointer;
end record;
overriding function To_String
(Self : Sorted_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String;
overriding procedure Append_Tables
(Self : Sorted_Field_Internal; To : in out Table_Sets.Set);
overriding procedure Append_If_Not_Aggregate
(Self : access Sorted_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean);
-------------------------
-- Field_List_Function --
-------------------------
function Field_List_Function
(Fields : SQL_Field_List) return SQL_Field'Class
is
Data : Multiple_Args_Field_Internal;
F : SQL_Field_Any
(Table => null, Instance => null, Instance_Index => -1, Name => null);
C : Field_List.Cursor := First (Fields);
begin
if Func_Name /= "" then
Data.Func_Name := new String'(Func_Name);
end if;
Data.Separator := new String'(Separator);
if Suffix /= "" then
Data.Suffix := new String'(Suffix);
end if;
while Has_Element (C) loop
declare
Field : constant SQL_Field'Class := Element (C);
C2 : Field_List.Cursor;
R : Field_Pointers.Ref;
begin
if Field in SQL_Field_Any'Class then
R := SQL_Field_Any (Field).Data;
if R.Get.Element.all in Multiple_Args_Field_Internal'Class then
declare
D : Multiple_Args_Field_Internal'Class renames
Multiple_Args_Field_Internal'Class (R.Get.Element.all);
begin
if D.Separator.all = Separator then
-- Avoid nested concatenations, put them all at the
-- same level. This simplifies the query. Due to this,
-- we are also sure the concatenation itself doesn't
-- have sub-expressions
C2 := First (D.List);
while Has_Element (C2) loop
Append (Data.List, Element (C2));
Next (C2);
end loop;
else
Append (Data.List, Field);
end if;
end;
else
Append (Data.List, Field);
end if;
else
Append (Data.List, Field);
end if;
end;
Next (C);
end loop;
F.Data.Set (Data);
return F;
end Field_List_Function;
---------------
-- To_String --
---------------
function To_String
(Self : SQL_Left_Join_Table; Format : Formatter'Class) return String
is
Result : Unbounded_String;
C : Table_List.Cursor := Table_List.No_Element;
begin
if not Self.Data.Is_Null then
C := First (Self.Data.Get.Tables.Data.Get);
end if;
Append (Result, "(");
Append (Result, To_String (Element (C), Format));
if Self.Data.Get.Is_Left_Join then
Append (Result, " LEFT JOIN ");
else
Append (Result, " JOIN ");
end if;
Next (C);
Append (Result, To_String (Element (C), Format));
if Self.Data.Get.On /= No_Criteria then
Append (Result, " ON ");
Append
(Result,
GNATCOLL.SQL_Impl.To_String
(Self.Data.Get.On, Format, Long => True));
end if;
Append (Result, ")");
if Self.Instance /= null then
Append (Result, " " & Self.Instance.all);
end if;
return To_String (Result);
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : Subquery_Table; Format : Formatter'Class) return String is
begin
if Self.Instance /= null then
return "(" & To_String (To_String (Self.Query, Format)) & ") "
& Self.Instance.all;
else
return "(" & To_String (To_String (Self.Query, Format)) & ")";
end if;
end To_String;
---------------
-- To_String --
---------------
function To_String (Self : SQL_Table'Class) return String is
begin
return To_String (Table_Names'
(Name => Self.Table_Name,
Instance => Self.Instance,
Instance_Index => Self.Instance_Index));
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : SQL_Table; Format : Formatter'Class) return String
is
pragma Unreferenced (Format);
begin
return To_String (Self);
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : SQL_Table_List; Format : Formatter'Class) return String
is
C : Table_List.Cursor := Table_List.No_Element;
Result : Unbounded_String;
begin
if not Self.Data.Is_Null then
C := First (Self.Data.Get);
end if;
if Has_Element (C) then
Append (Result, To_String (Element (C), Format));
Next (C);
end if;
while Has_Element (C) loop
Append (Result, ", ");
Append (Result, To_String (Element (C), Format));
Next (C);
end loop;
return To_String (Result);
end To_String;
---------------
-- To_String --
---------------
function To_String (Names : Table_Names) return String is
Instance : constant String := Instance_Name (Names);
begin
if Instance /= Names.Name.all then
return Names.Name.all & " " & Instance;
else
return Names.Name.all;
end if;
end To_String;
---------------
-- To_String --
---------------
function To_String (Self : Table_Sets.Set) return Unbounded_String is
Result : Unbounded_String;
C : Table_Sets.Cursor := First (Self);
begin
if Has_Element (C) then
Append (Result, To_String (Element (C)));
Next (C);
end if;
while Has_Element (C) loop
Append (Result, ", ");
Append (Result, To_String (Element (C)));
Next (C);
end loop;
return Result;
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : As_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String
is
Has_Blank : Boolean := False;
begin
for J in Self.As'Range loop
if Self.As (J) = ' ' then
Has_Blank := True;
exit;
end if;
end loop;
if Has_Blank
and then (Self.As (Self.As'First) /= '"'
or else Self.As (Self.As'Last) /= '"')
then
return To_String (Self.Renamed, Format, Long)
& " AS """ & Self.As.all & """";
else
return To_String (Self.Renamed, Format, Long)
& " AS " & Self.As.all;
end if;
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : Sorted_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String is
begin
if Self.Ascending then
return To_String (Self.Sorted, Format, Long => Long) & " ASC";
else
return To_String (Self.Sorted, Format, Long => Long) & " DESC";
end if;
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : Multiple_Args_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String
is
C : Field_List.Cursor := First (Self.List);
Result : Unbounded_String;
begin
if Self.Func_Name /= null then
Append (Result, Self.Func_Name.all);
end if;
if Has_Element (C) then
Append (Result, To_String (Element (C), Format, Long));
Next (C);
end if;
while Has_Element (C) loop
Append (Result, Self.Separator.all);
Append (Result, To_String (Element (C), Format, Long));
Next (C);
end loop;
if Self.Suffix /= null then
Append (Result, Self.Suffix.all);
end if;
return To_String (Result);
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : Case_Stmt_Internal;
Format : Formatter'Class;
Long : Boolean) return String
is
C : When_Lists.Cursor := First (Self.Criteria.List);
Result : Unbounded_String;
begin
Append (Result, "CASE");
while Has_Element (C) loop
Append (Result, " WHEN "
& GNATCOLL.SQL_Impl.To_String (Element (C).Criteria, Format)
& " THEN "
& To_String (Element (C).Field, Format, Long));
Next (C);
end loop;
if Self.Else_Clause /= No_Field_Pointer then
Append
(Result,
" ELSE " & To_String (Self.Else_Clause, Format, Long));
end if;
Append (Result, " END");
return To_String (Result);
end To_String;
---------
-- "&" --
---------
function "&" (Left, Right : SQL_Table_List) return SQL_Table_List is
begin
if Left.Data.Is_Null then
return Right;
end if;
if Right.Data.Is_Null then
return Left;
end if;
Copy_Operands : declare
Result : SQL_Table_List;
begin
Result.Data.Set (Table_List."&" (Left.Data.Get, Right.Data.Get));
return Result;
end Copy_Operands;
end "&";
---------
-- "&" --
---------
function "&"
(Left : SQL_Table_List;
Right : SQL_Single_Table'Class) return SQL_Table_List
is
begin
return Left & (+Right);
end "&";
---------
-- "&" --
---------
function "&" (Left, Right : SQL_Single_Table'Class) return SQL_Table_List is
Result : constant SQL_Table_List := +Left;
begin
Result.Data.Get.Append (Right);
return Result;
end "&";
---------
-- "+" --
---------
function "+" (Left : SQL_Single_Table'Class) return SQL_Table_List is
Result : SQL_Table_List;
begin
Result.Data.Set (Table_List.To_Vector (Left, 1));
return Result;
end "+";
--------
-- As --
--------
function As
(Field : SQL_Field'Class; Name : String) return SQL_Field'Class
is
Data : As_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.As := new String'(Name);
Data.Renamed := +Field;
F.Set (Data);
return SQL_Field_Any'
(Table => null,
Instance => null,
Name => null,
Instance_Index => -1,
Data => F);
end As;
----------
-- Desc --
----------
function Desc (Field : SQL_Field'Class) return SQL_Field'Class is
Data : Sorted_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Ascending := False;
Data.Sorted := +Field;
F.Set (Data);
return SQL_Field_Any'
(Table => null,
Instance => null,
Name => null,
Instance_Index => -1,
Data => F);
end Desc;
---------
-- Asc --
---------
function Asc (Field : SQL_Field'Class) return SQL_Field'Class is
Data : Sorted_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Ascending := True;
Data.Sorted := +Field;
F.Set (Data);
return SQL_Field_Any'
(Table => null, Instance => null, Name => null,
Instance_Index => -1,
Data => F);
end Asc;
------------------------
-- Expression_Or_Null --
------------------------
function Expression_Or_Null
(Value : String) return Text_Fields.Field'Class
is
begin
if Value = Null_String then
return Text_Fields.From_String (Null_String);
else
return Text_Fields.Expression (Value);
end if;
end Expression_Or_Null;
----------------
-- As_Boolean --
----------------
function As_Boolean
(Criteria : SQL_Criteria) return SQL_Field'Class
is
Data : Aggregate_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Criteria := Criteria;
F.Set (Data);
return SQL_Field_Any'
(Table => null, Instance => null, Name => null,
Instance_Index => -1,
Data => F);
end As_Boolean;
-------------
-- As_Days --
-------------
function As_Days (Count : Natural) return Time_Fields.Field'Class is
begin
return Time_Fields.From_String
("interval '" & Integer'Image (Count) & "days'");
end As_Days;
function As_Days (Count : Natural) return Date_Fields.Field'Class is
begin
return Date_Fields.From_String (Integer'Image (Count));
end As_Days;
------------------
-- At_Time_Zone --
------------------
function At_Time_Zone
(Field : Time_Fields.Field'Class; TZ : String)
return Time_Fields.Field'Class
is
function Internal is new Time_Fields.Apply_Function
(Time_Fields.Field, "", " at time zone '" & TZ & "'");
begin
return Internal (Field);
end At_Time_Zone;
----------
-- Free --
----------
overriding procedure Free (Self : in out Multiple_Args_Field_Internal) is
begin
Free (Self.Suffix);
Free (Self.Func_Name);
Free (Self.Separator);
end Free;
------------
-- Concat --
------------
function Concat (Fields : SQL_Field_List) return SQL_Field'Class is
function Internal is new Field_List_Function ("", " || ", "");
begin
return Internal (Fields);
end Concat;
-----------
-- Tuple --
-----------
function Tuple (Fields : SQL_Field_List) return SQL_Field'Class is
function Internal is new Field_List_Function ("(", ", ", ")");
begin
return Internal (Fields);
end Tuple;
--------------
-- Coalesce --
--------------
function Coalesce (Fields : SQL_Field_List) return SQL_Field'Class is
function Internal is new Field_List_Function ("COALESCE (", ", ", ")");
begin
return Internal (Fields);
end Coalesce;
---------
-- "&" --
---------
function "&" (List1, List2 : When_List) return When_List is
Result : When_List;
procedure Copy_Elements (L : When_List);
-- Copy elements from L into Result
-------------------
-- Copy_Elements --
-------------------
procedure Copy_Elements (L : When_List) is
C : When_Lists.Cursor := L.List.First;
begin
while Has_Element (C) loop
Append (Result.List, Element (C));
Next (C);
end loop;
end Copy_Elements;
-- Start of processing for "&"
begin
Copy_Elements (List1);
Copy_Elements (List2);
return Result;
end "&";
--------------
-- SQL_When --
--------------
function SQL_When
(Criteria : SQL_Criteria; Field : SQL_Field'Class) return When_List
is
Result : When_List;
begin
Append (Result.List, When_List_Item'(Criteria, +Field));
return Result;
end SQL_When;
--------------
-- SQL_Case --
--------------
function SQL_Case
(List : When_List; Else_Clause : SQL_Field'Class := Null_Field_Text)
return SQL_Field'Class
is
Data : Case_Stmt_Internal;
F : Field_Pointers.Ref;
begin
Data.Criteria := List;
if Else_Clause /= SQL_Field'Class (Null_Field_Text) then
Data.Else_Clause := +Else_Clause;
end if;
F.Set (Data);
return SQL_Field_Any'
(Table => null,
Instance => null,
Name => null,
Instance_Index => -1,
Data => F);
end SQL_Case;
-------------
-- To_Char --
-------------
function To_Char
(Field : Time_Fields.Field'Class; Format : String)
return Text_Fields.Field'Class
is
function Internal is new Text_Fields.Apply_Function
(Time_Fields.Field, "TO_CHAR (", ", '" & Format & "')");
begin
return Internal (Field);
end To_Char;
-------------
-- Extract --
-------------
function Extract
(Field : Time_Fields.Field'Class; Attribute : String)
return Time_Fields.Field'Class
is
function Internal is new Time_Fields.Apply_Function
(Time_Fields.Field, "EXTRACT (" & Attribute & " from ");
begin
return Internal (Field);
end Extract;
-------------
-- Extract --
-------------
function Extract
(Field : Date_Fields.Field'Class; Attribute : String)
return Date_Fields.Field'Class
is
function Internal is new Date_Fields.Apply_Function
(Date_Fields.Field, "EXTRACT (" & Attribute & " from ");
begin
return Internal (Field);
end Extract;
--------------
-- Absolute --
--------------
function Absolute
(Field : SQL_Field'Class) return Integer_Fields.Field'Class
is
function Internal is new Integer_Fields.Apply_Function
(SQL_Field, "ABS (");
begin
return Internal (Field);
end Absolute;
-----------
-- Lower --
-----------
function Lower
(Field : SQL_Field'Class) return Text_Fields.Field'Class
is
function Internal is new Text_Fields.Apply_Function
(SQL_Field, "LOWER (");
begin
return Internal (Field);
end Lower;
-----------
-- Upper --
-----------
function Upper
(Field : SQL_Field'Class) return Text_Fields.Field'Class
is
function Internal is new Text_Fields.Apply_Function
(SQL_Field, "UPPER (");
begin
return Internal (Field);
end Upper;
----------
-- Trim --
----------
function Trim (Field : SQL_Field'Class) return Text_Fields.Field'Class is
function Internal is new Text_Fields.Apply_Function
(SQL_Field, "TRIM (");
begin
return Internal (Field);
end Trim;
-------------
-- Initcap --
-------------
function Initcap
(Field : SQL_Field'Class) return Text_Fields.Field'Class
is
function Internal is new Text_Fields.Apply_Function
(SQL_Field, "INITCAP (");
begin
return Internal (Field);
end Initcap;
--------------------
-- Cast_To_String --
--------------------
function Cast_To_String
(Field : SQL_Field'Class) return Text_Fields.Field'Class
is
function Internal is new Text_Fields.Apply_Function
(SQL_Field, "CAST (", "AS TEXT)");
begin
return Internal (Field);
end Cast_To_String;
------------------
-- Cast_To_Date --
------------------
function Cast_To_Date
(Field : SQL_Field'Class) return Date_Fields.Field'Class
is
function Internal is new Date_Fields.Apply_Function
(SQL_Field, "CAST (", "AS DATE)");
begin
return Internal (Field);
end Cast_To_Date;
---------------------
-- Cast_To_Integer --
---------------------
function Cast_To_Integer
(Field : SQL_Field'Class) return Integer_Fields.Field'Class
is
function Internal is new Integer_Fields.Apply_Function
(SQL_Field, "CAST (", "AS INTEGER)");
begin
return Internal (Field);
end Cast_To_Integer;
---------------
-- To_String --
---------------
function To_String
(Self : Aggregate_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String
is
Result : Unbounded_String;
procedure Append_Field_List (L : SQL_Field_List);
-- Append L as a comma separated list to Result
-----------------------
-- Append_Field_List --
-----------------------
procedure Append_Field_List (L : SQL_Field_List) is
C : Field_List.Cursor := First (L);
First : Boolean := True;
begin
while Has_Element (C) loop
if First then
First := False;
else
Append (Result, ", ");
end if;
Append (Result, To_String (Element (C), Format, Long));
Next (C);
end loop;
end Append_Field_List;
-- Start of processing for To_String
begin
if Self.Func /= null then
Result := To_Unbounded_String (Self.Func.all & " (");
end if;
Append_Field_List (Self.Params);
if Self.Criteria /= No_Criteria then
Append (Result, GNATCOLL.SQL_Impl.To_String (Self.Criteria, Format));
end if;
-- Optional ORDER BY clause
if Self.Order_By /= Empty_Field_List then
Append (Result, " ORDER BY ");
Append_Field_List (Self.Order_By);
end if;
if Self.Func /= null then
Append (Result, ")");
end if;
return To_String (Result);
end To_String;
-----------
-- Apply --
-----------
function Apply
(Func : Aggregate_Function;
Criteria : SQL_Criteria;
Order_By : SQL_Field_Or_List'Class := Empty_Field_List)
return SQL_Field'Class
is
Data : Aggregate_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Criteria := Criteria;
Data.Func := new String'(String (Func));
if Order_By in SQL_Field'Class then
Data.Order_By := +SQL_Field'Class (Order_By);
else
Data.Order_By := SQL_Field_List (Order_By);
end if;
F.Set (Data);
return SQL_Field_Any'
(Table => null, Instance => null, Name => null,
Instance_Index => -1,
Data => F);
end Apply;
-----------
-- Apply --
-----------
function Apply
(Func : Aggregate_Function;
Fields : SQL_Field_List;
Order_By : SQL_Field_Or_List'Class := Empty_Field_List)
return SQL_Field'Class
is
Data : Aggregate_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Params := Fields;
Data.Func := new String'(String (Func));
if Order_By in SQL_Field'Class then
Data.Order_By := +SQL_Field'Class (Order_By);
else
Data.Order_By := SQL_Field_List (Order_By);
end if;
F.Set (Data);
return SQL_Field_Any'
(Table => null,
Instance => null,
Name => null,
Instance_Index => -1,
Data => F);
end Apply;
-----------
-- Apply --
-----------
function Apply
(Func : Aggregate_Function;
Field : SQL_Field'Class;
Order_By : SQL_Field_Or_List'Class := Empty_Field_List)
return SQL_Field'Class
is
Data : Aggregate_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Params := +Field;
Data.Func := new String'(String (Func));
if Order_By in SQL_Field'Class then
Data.Order_By := +SQL_Field'Class (Order_By);
else
Data.Order_By := SQL_Field_List (Order_By);
end if;
F.Set (Data);
return SQL_Field_Any'
(Table => null,
Instance => null,
Name => null,
Instance_Index => -1,
Data => F);
end Apply;
-------------
-- To_List --
-------------
function To_List (Fields : SQL_Field_Array) return SQL_Field_List is
S : SQL_Field_List;
begin
for A in Fields'Range loop
Append (S, Fields (A));
end loop;
return S;
end To_List;
-----------
-- "not" --
-----------
function "not" (Self : SQL_Criteria) return SQL_Criteria is
Data : SQL_Criteria_Data (Criteria_Not);
Result : SQL_Criteria;
begin
if Self = No_Criteria then
return No_Criteria;
end if;
Data.Criteria := Self;
Set_Data (Result, Data);
return Result;
end "not";
------------
-- Length --
------------
function Length (Self : SQL_Criteria) return Natural is
use type SQL_Criteria_Data_Access;
Ptr : constant SQL_Criteria_Data_Access := Get_Data (Self);
begin
if Ptr = null then
return 0;
end if;
if Ptr.all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Ptr.all).Op in Criteria_Combine
then
return Natural (SQL_Criteria_Data (Ptr.all).Criterias.Length);
else
return 1;
end if;
end Length;
-----------
-- Is_Or --
-----------
function Is_Or (Self : SQL_Criteria) return Boolean is
use type SQL_Criteria_Data_Access;
Ptr : constant SQL_Criteria_Data_Access := Get_Data (Self);
begin
return Ptr /= null and then Ptr.all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Ptr.all).Op = Criteria_Or;
end Is_Or;
------------
-- Is_And --
------------
function Is_And (Self : SQL_Criteria) return Boolean is
use type SQL_Criteria_Data_Access;
Ptr : constant SQL_Criteria_Data_Access := Get_Data (Self);
begin
return Ptr /= null and then Ptr.all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Ptr.all).Op = Criteria_And;
end Is_And;
-------------
-- Combine --
-------------
function Combine
(List : Criteria_List; Op : Criteria_Combine) return SQL_Criteria
is
Result : SQL_Criteria;
Data : SQL_Criteria_Data (Op);
begin
Data.Criterias := List;
Set_Data (Result, Data);
return Result;
end Combine;
-------------
-- Combine --
-------------
function Combine
(Left, Right : SQL_Criteria; Op : Criteria_Combine) return SQL_Criteria
is
List : Criteria_List;
C : Criteria_Lists.Cursor;
begin
if Left = No_Criteria then
return Right;
elsif Right = No_Criteria then
return Left;
elsif Get_Data (Left).all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Get_Data (Left).all).Op = Op
then
-- ??? We could optimize when Left.Refcount=1, since we are modifying
-- the last instance and thus do not need to copy the list
List := SQL_Criteria_Data (Get_Data (Left).all).Criterias;
if Get_Data (Right).all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Get_Data (Right).all).Op = Op
then
C := First (SQL_Criteria_Data (Get_Data (Right).all).Criterias);
while Has_Element (C) loop
Append (List, Element (C));
Next (C);
end loop;
else
Append (List, Right);
end if;
elsif Get_Data (Right).all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Get_Data (Right).all).Op = Op
then
List := SQL_Criteria_Data (Get_Data (Right).all).Criterias;
Prepend (List, Left);
else
Append (List, Left);
Append (List, Right);
end if;
return Combine (List, Op);
end Combine;
--------------
-- Overlaps --
--------------
function Overlaps (Left, Right : SQL_Field'Class) return SQL_Criteria is
begin
return Compare (Left, Right, Comparison_Overlaps'Access);
end Overlaps;
-----------
-- "and" --
-----------
function "and" (Left, Right : SQL_Criteria) return SQL_Criteria is
begin
return Combine (Left, Right, Criteria_And);
end "and";
----------
-- "or" --
----------
function "or" (Left, Right : SQL_Criteria) return SQL_Criteria is
begin
return Combine (Left, Right, Criteria_Or);
end "or";
-----------
-- "and" --
-----------
function "and"
(Left : SQL_Criteria; Right : Boolean_Fields.Field'Class)
return SQL_Criteria is
begin
return Left and (Right = True);
end "and";
----------
-- "or" --
----------
function "or"
(Left : SQL_Criteria; Right : Boolean_Fields.Field'Class)
return SQL_Criteria is
begin
return Left or (Right = True);
end "or";
-----------
-- "not" --
-----------
function "not" (Left : Boolean_Fields.Field'Class) return SQL_Criteria is
begin
return Left = False;
end "not";
------------
-- SQL_In --
------------
function SQL_In
(Self : SQL_Field'Class; List : SQL_Field_List) return SQL_Criteria
is
Data : SQL_Criteria_Data (Criteria_In);
Result : SQL_Criteria;
begin
Data.Arg := +Self;
Data.List := List;
Set_Data (Result, Data);
return Result;
end SQL_In;
function SQL_In
(Self : SQL_Field'Class; Subquery : SQL_Query) return SQL_Criteria
is
Data : SQL_Criteria_Data (Criteria_In);
Result : SQL_Criteria;
begin
Data.Arg := +Self;
Data.Subquery := Subquery;
Set_Data (Result, Data);
return Result;
end SQL_In;
function SQL_In
(Self : SQL_Field'Class; List : String) return SQL_Criteria
is
Data : SQL_Criteria_Data (Criteria_In);
Result : SQL_Criteria;
begin
Data.Arg := +Self;
Data.In_String := To_Unbounded_String (List);
Set_Data (Result, Data);
return Result;
end SQL_In;
------------
-- Exists --
------------
function Exists (Subquery : SQL_Query) return SQL_Criteria is
Data : SQL_Criteria_Data (Criteria_Exists);
Result : SQL_Criteria;
begin
Data.Subquery2 := Subquery;
Set_Data (Result, Data);
return Result;
end Exists;
----------------
-- SQL_Not_In --
----------------
function SQL_Not_In
(Self : SQL_Field'Class; List : SQL_Field_List) return SQL_Criteria
is
Data : SQL_Criteria_Data (Criteria_Not_In);
Result : SQL_Criteria;
begin
Data.Arg := +Self;
Data.List := List;
Set_Data (Result, Data);
return Result;
end SQL_Not_In;
function SQL_Not_In
(Self : SQL_Field'Class; Subquery : SQL_Query) return SQL_Criteria
is
Data : SQL_Criteria_Data (Criteria_Not_In);
Result : SQL_Criteria;
begin
Data.Arg := +Self;
Data.Subquery := Subquery;
Set_Data (Result, Data);
return Result;
end SQL_Not_In;
function SQL_Not_In
(Self : SQL_Field'Class; List : String) return SQL_Criteria
is
Data : SQL_Criteria_Data (Criteria_Not_In);
Result : SQL_Criteria;
begin
Data.Arg := +Self;
Data.In_String := To_Unbounded_String (List);
Set_Data (Result, Data);
return Result;
end SQL_Not_In;
-----------------
-- SQL_Between --
-----------------
function SQL_Between
(Self, Left, Right : SQL_Field'Class) return SQL_Criteria is
begin
return Result : SQL_Criteria do
Set_Data
(Result,
SQL_Criteria_Data'
(Criteria_Between,
Arg2 => +Self, Left => +Left, Right => +Right));
end return;
end SQL_Between;
---------------------
-- SQL_Not_Between --
---------------------
function SQL_Not_Between
(Self, Left, Right : SQL_Field'Class) return SQL_Criteria is
begin
return Result : SQL_Criteria do
Set_Data
(Result,
SQL_Criteria_Data'
(Criteria_Not_Between,
Arg2 => +Self, Left => +Left, Right => +Right));
end return;
end SQL_Not_Between;
-------------
-- Is_Null --
-------------
function Is_Null (Self : SQL_Field'Class) return SQL_Criteria is
Data : SQL_Criteria_Data (Criteria_Null);
Result : SQL_Criteria;
begin
Data.Arg3 := +Self;
Set_Data (Result, Data);
return Result;
end Is_Null;
-----------------
-- Is_Not_Null --
-----------------
function Is_Not_Null (Self : SQL_Field'Class) return SQL_Criteria is
Data : SQL_Criteria_Data (Criteria_Not_Null);
Result : SQL_Criteria;
begin
Data.Arg3 := +Self;
Set_Data (Result, Data);
return Result;
end Is_Not_Null;
---------
-- Any --
---------
function Any (Self, Str : Text_Fields.Field'Class) return SQL_Criteria is
begin
return Compare
(Self, Str, Comparison_Any'Access, Comparison_Parenthesis'Access);
end Any;
-----------
-- Ilike --
-----------
function Ilike
(Self : Text_Fields.Field'Class; Str : String) return SQL_Criteria is
begin
return Compare (Self, Expression (Str), Comparison_ILike'Access);
end Ilike;
----------
-- Like --
----------
function Like
(Self : Text_Fields.Field'Class; Str : String) return SQL_Criteria is
begin
return Compare (Self, Expression (Str), Comparison_Like'Access);
end Like;
-----------
-- Ilike --
-----------
function Ilike
(Self : Text_Fields.Field'Class; Field : SQL_Field'Class)
return SQL_Criteria is
begin
return Compare (Self, Field, Comparison_ILike'Access);
end Ilike;
----------
-- Like --
----------
function Like
(Self : Text_Fields.Field'Class; Field : Text_Fields.Field'Class)
return SQL_Criteria
is
begin
return Compare (Self, Field, Comparison_Like'Access);
end Like;
---------------
-- Not_Ilike --
---------------
function Not_Ilike
(Self : Text_Fields.Field'Class; Str : String) return SQL_Criteria is
begin
return Compare (Self, Expression (Str), Comparison_Not_ILike'Access);
end Not_Ilike;
--------------
-- Not_Like --
--------------
function Not_Like
(Self : Text_Fields.Field'Class; Str : String) return SQL_Criteria is
begin
return Compare (Self, Expression (Str), Comparison_Not_Like'Access);
end Not_Like;
---------------
-- To_String --
---------------
function To_String
(Self : SQL_Criteria_Data;
Format : Formatter'Class;
Long : Boolean := True) return String
is
Result : Unbounded_String;
List : Unbounded_String;
C : Criteria_Lists.Cursor;
C2 : Field_List.Cursor;
Is_First : Boolean;
Criteria : SQL_Criteria;
begin
case Self.Op is
when Criteria_Combine =>
C := First (Self.Criterias);
while Has_Element (C) loop
if C /= First (Self.Criterias) then
case Self.Op is
when Criteria_And => Append (Result, " AND ");
when Criteria_Or => Append (Result, " OR ");
when others => null;
end case;
end if;
Criteria := Element (C);
if Get_Data (Criteria).all in SQL_Criteria_Data'Class
and then SQL_Criteria_Data (Get_Data (Criteria).all).Op
in Criteria_Combine
then
Append (Result, "(");
Append (Result,
GNATCOLL.SQL_Impl.To_String (Element (C), Format));
Append (Result, ")");
else
Append (Result,
GNATCOLL.SQL_Impl.To_String (Element (C), Format));
end if;
Next (C);
end loop;
when Criteria_In | Criteria_Not_In =>
List := Null_Unbounded_String;
Is_First := True;
C2 := First (Self.List);
while Has_Element (C2) loop
if not Is_First then
Append (List, ",");
end if;
Is_First := False;
Append (List, To_String (Element (C2), Format, Long));
Next (C2);
end loop;
Append (List, To_String (Self.Subquery, Format));
Append (List, Self.In_String);
if List = "" then
-- "A in ()" is same as "False"
-- "A not in ()" is same as "True"
Result := To_Unbounded_String
(Expression (Self.Op = Criteria_Not_In)
.To_String (Format, Long));
else
Result :=
To_Unbounded_String (To_String (Self.Arg, Format, Long));
Append (Result,
(if Self.Op = Criteria_In
then " IN (" else " NOT IN ("));
Append (Result, List);
Append (Result, ")");
end if;
when Criteria_Exists =>
Result := To_Unbounded_String ("EXISTS (");
Append (Result, To_String (Self.Subquery2, Format));
Append (Result, ")");
when Criteria_Between | Criteria_Not_Between =>
Result := To_Unbounded_String
(To_String (Self.Arg2, Format, Long));
if Self.Op = Criteria_Not_Between then
Append (Result, " NOT");
end if;
Append
(Result,
" BETWEEN " & To_String (Self.Left, Format, Long) & " AND "
& To_String (Self.Right, Format, Long));
when Null_Criteria =>
Result := To_Unbounded_String
(To_String (Self.Arg3, Format, Long));
case Self.Op is
when Criteria_Null => Append (Result, " IS NULL");
when Criteria_Not_Null => Append (Result, " IS NOT NULL");
when others => null;
end case;
when Criteria_Not =>
Result := To_Unbounded_String
("NOT (" & To_String (Self.Criteria, Format, Long) & ")");
end case;
return To_String (Result);
end To_String;
----------------
-- SQL_Values --
----------------
function SQL_Values (Val : Field_List_Array) return SQL_Query is
Q : SQL_Query;
Data : constant Query_Pointers.Encapsulated_Access :=
new Query_Values_Contents'
(Query_Contents with Size => Val'Length, Values => Val);
-- We have to declare and assign Data with definite access type first
-- and then put it into Q.Set. If we use operator "new" as a parameter
-- for Q.Set we've got a runtime error "accessibility check failed"
-- inside of Q.Set. Checked at GNATLS Pro 18.0w (20170525-63).
begin
Q.Set (Data);
return Q;
end SQL_Values;
---------------
-- To_String --
---------------
overriding function To_String
(Self : Query_Values_Contents;
Format : Formatter'Class) return Unbounded_String
is
Result : Unbounded_String := To_Unbounded_String ("VALUES ");
begin
for R in Self.Values'Range loop
if R /= Self.Values'First then
Append (Result, ", (");
else
Append (Result, '(');
end if;
Append (Result, To_String (Self.Values (R), Format, Long => True));
Append (Result, ')');
end loop;
return Result;
end To_String;
----------------
-- SQL_Select --
----------------
function SQL_Select
(Fields : SQL_Field_Or_List'Class;
From : SQL_Table_Or_List'Class := Empty_Table_List;
Where : SQL_Criteria := No_Criteria;
Group_By : SQL_Field_Or_List'Class := Empty_Field_List;
Having : SQL_Criteria := No_Criteria;
Order_By : SQL_Field_Or_List'Class := Empty_Field_List;
Limit : Integer := -1;
Offset : Integer := -1;
Distinct : Boolean := False;
Auto_Complete : Boolean := False) return SQL_Query
is
Data : Query_Select_Contents;
Q : SQL_Query;
begin
if Fields in SQL_Field'Class then
Data.Fields := +SQL_Field'Class (Fields);
else
Data.Fields := SQL_Field_List (Fields);
end if;
if From in SQL_Table_List'Class then
Data.Tables := SQL_Table_List (From);
else
Data.Tables := +SQL_Single_Table'Class (From);
end if;
Data.Criteria := Where;
if Group_By in SQL_Field'Class then
Data.Group_By := +SQL_Field'Class (Group_By);
else
Data.Group_By := SQL_Field_List (Group_By);
end if;
Data.Having := Having;
if Order_By in SQL_Field'Class then
Data.Order_By := +SQL_Field'Class (Order_By);
else
Data.Order_By := SQL_Field_List (Order_By);
end if;
Data.Limit := Limit;
Data.Offset := Offset;
Data.Distinct := Distinct;
Q.Set (Data);
if Auto_Complete then
GNATCOLL.SQL.Auto_Complete (Q);
end if;
return Q;
end SQL_Select;
---------------
-- To_String --
---------------
overriding function To_String
(Self : Query_Select_Contents;
Format : Formatter'Class) return Unbounded_String
is
Result : Unbounded_String;
begin
Result := To_Unbounded_String ("SELECT ");
if Self.Distinct then
Append (Result, "DISTINCT ");
end if;
Append (Result, To_String (Self.Fields, Format, Long => True));
if Self.Tables /= Empty_Table_List
or else not Is_Empty (Self.Extra_Tables)
then
Append (Result, " FROM ");
if Self.Tables.Data.Is_Null or else Self.Tables.Data.Get.Is_Empty then
Append (Result, To_String (Self.Extra_Tables));
elsif Is_Empty (Self.Extra_Tables) then
Append (Result, To_String (Self.Tables, Format));
else
Append (Result, To_String (Self.Tables, Format));
Append (Result, ", ");
Append (Result, To_String (Self.Extra_Tables));
end if;
end if;
if Self.Criteria /= No_Criteria then
Append (Result, " WHERE ");
Append (Result, GNATCOLL.SQL_Impl.To_String (Self.Criteria, Format));
end if;
if Self.Group_By /= Empty_Field_List then
Append (Result, " GROUP BY ");
Append (Result, To_String (Self.Group_By, Format, Long => True));
if Self.Having /= No_Criteria then
Append (Result, " HAVING ");
Append (Result, GNATCOLL.SQL_Impl.To_String (Self.Having, Format));
end if;
end if;
if Self.Order_By /= Empty_Field_List then
Append (Result, " ORDER BY ");
Append (Result, To_String (Self.Order_By, Format, Long => True));
end if;
-- Need to output LIMIT before OFFSET for sqlite. This seems to be
-- compatible with other backends.
if Self.Limit >= 0 or else Self.Offset >= 0 then
Append (Result, " LIMIT" & Integer'Image (Self.Limit));
end if;
if Self.Offset >= 0 then
Append (Result, " OFFSET" & Integer'Image (Self.Offset));
end if;
return Result;
end To_String;
---------------
-- SQL_Union --
---------------
function SQL_Union
(Query1, Query2 : SQL_Query;
Order_By : SQL_Field_Or_List'Class := Empty_Field_List;
Limit : Integer := -1;
Offset : Integer := -1;
Distinct : Boolean := False) return SQL_Query
is
Data : Query_Union_Contents;
Q : SQL_Query;
begin
Data.Q1 := Query1;
Data.Q2 := Query2;
if Order_By in SQL_Field'Class then
Data.Order_By := +SQL_Field'Class (Order_By);
else
Data.Order_By := SQL_Field_List (Order_By);
end if;
Data.Limit := Limit;
Data.Offset := Offset;
Data.Distinct := Distinct;
Q.Set (Data);
return Q;
end SQL_Union;
---------------
-- To_String --
---------------
overriding function To_String
(Self : Query_Union_Contents;
Format : Formatter'Class) return Unbounded_String
is
Result : Unbounded_String;
begin
Append (Result, To_String (Self.Q1, Format));
Append (Result, " UNION ");
if not Self.Distinct then
Append (Result, "ALL ");
end if;
Append (Result, To_String (Self.Q2, Format));
Append (Result, " ");
if Self.Order_By /= Empty_Field_List then
Append (Result, " ORDER BY ");
Append (Result, To_String (Self.Order_By, Format, Long => True));
end if;
-- Need to output LIMIT before OFFSET for sqlite. This seems to be
-- compatible with other backends.
if Self.Limit >= 0 or else Self.Offset >= 0 then
Append (Result, " LIMIT" & Integer'Image (Self.Limit));
end if;
if Self.Offset >= 0 then
Append (Result, " OFFSET" & Integer'Image (Self.Offset));
end if;
return Result;
end To_String;
---------------
-- To_String --
---------------
function To_String
(Self : SQL_Query; Format : Formatter'Class) return Unbounded_String is
begin
if Self.Get = null then
return Null_Unbounded_String;
else
return To_String (Self.Get.all, Format);
end if;
end To_String;
----------
-- Free --
----------
procedure Free (Self : in out As_Field_Internal) is
begin
Free (Self.As);
end Free;
----------
-- Free --
----------
procedure Free (Self : in out Aggregate_Field_Internal) is
begin
Free (Self.Func);
end Free;
-------------------
-- Auto_Complete --
-------------------
procedure Auto_Complete
(Self : in out Query_Select_Contents;
Auto_Complete_From : Boolean := True;
Auto_Complete_Group_By : Boolean := True)
is
List2 : Table_Sets.Set;
Group_By : SQL_Field_List;
Has_Aggregate : Boolean := False;
begin
if Auto_Complete_From then
-- For each field, make sure the table is in the list
Append_Tables (Self.Fields, Self.Extra_Tables);
Append_Tables (Self.Group_By, Self.Extra_Tables);
Append_Tables (Self.Order_By, Self.Extra_Tables);
Append_Tables (Self.Criteria, Self.Extra_Tables);
Append_Tables (Self.Having, Self.Extra_Tables);
Append_Tables (Self.Tables, List2);
Difference (Self.Extra_Tables, List2);
end if;
if Auto_Complete_Group_By then
Append_If_Not_Aggregate (Self.Fields, Group_By, Has_Aggregate);
Append_If_Not_Aggregate (Self.Order_By, Group_By, Has_Aggregate);
Append_If_Not_Aggregate (Self.Having, Group_By, Has_Aggregate);
if Has_Aggregate then
Self.Group_By := Group_By;
end if;
end if;
end Auto_Complete;
-------------------
-- Auto_Complete --
-------------------
procedure Auto_Complete
(Self : in out SQL_Query;
Auto_Complete_From : Boolean := True;
Auto_Complete_Group_By : Boolean := True) is
begin
if Self.Get /= null then
Auto_Complete
(Self.Get.all, Auto_Complete_From, Auto_Complete_Group_By);
end if;
end Auto_Complete;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : SQL_Criteria_Data; To : in out Table_Sets.Set)
is
C : Criteria_Lists.Cursor;
begin
case Self.Op is
when Criteria_Combine =>
C := First (Self.Criterias);
while Has_Element (C) loop
Append_Tables (Element (C), To);
Next (C);
end loop;
when Criteria_In | Criteria_Not_In =>
Append_Tables (Self.Arg, To);
when Criteria_Exists =>
null;
when Criteria_Between | Criteria_Not_Between =>
Append_Tables (Self.Arg2, To);
when Null_Criteria =>
Append_Tables (Self.Arg3, To);
when Criteria_Not =>
Append_Tables (Self.Criteria, To);
end case;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : SQL_Left_Join_Table; To : in out Table_Sets.Set)
is
C : Table_List.Cursor;
begin
if not Self.Data.Get.Tables.Data.Is_Null then
C := First (Self.Data.Get.Tables.Data.Get);
while Has_Element (C) loop
Append_Tables (Element (C), To);
Next (C);
end loop;
end if;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables (Self : SQL_Table; To : in out Table_Sets.Set) is
begin
if Self.Table_Name /= null then
Include (To, (Name => Self.Table_Name,
Instance => Self.Instance,
Instance_Index => Self.Instance_Index));
end if;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : SQL_Table_List; To : in out Table_Sets.Set)
is
C : Table_List.Cursor;
begin
if not Self.Data.Is_Null then
C := First (Self.Data.Get);
while Has_Element (C) loop
Append_Tables (Element (C), To);
Next (C);
end loop;
end if;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(From : SQL_Field_List; To : in out Table_Sets.Set)
is
C : Field_List.Cursor := First (From);
begin
while Has_Element (C) loop
Append_Tables (Element (C), To);
Next (C);
end loop;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : As_Field_Internal; To : in out Table_Sets.Set) is
begin
Append_Tables (Self.Renamed, To);
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : Sorted_Field_Internal; To : in out Table_Sets.Set) is
begin
Append_Tables (Self.Sorted, To);
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : Multiple_Args_Field_Internal; To : in out Table_Sets.Set)
is
C : Field_List.Cursor := First (Self.List);
begin
while Has_Element (C) loop
Append_Tables (Element (C), To);
Next (C);
end loop;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : Case_Stmt_Internal; To : in out Table_Sets.Set)
is
C : When_Lists.Cursor := First (Self.Criteria.List);
begin
while Has_Element (C) loop
Append_Tables (Element (C).Field, To);
Next (C);
end loop;
if Self.Else_Clause /= No_Field_Pointer then
Append_Tables (Self.Else_Clause, To);
end if;
end Append_Tables;
-------------------
-- Append_Tables --
-------------------
procedure Append_Tables
(Self : Aggregate_Field_Internal; To : in out Table_Sets.Set)
is
C : Field_List.Cursor := First (Self.Params);
begin
while Has_Element (C) loop
Append_Tables (Element (C), To);
Next (C);
end loop;
Append_Tables (Self.Criteria, To);
end Append_Tables;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : SQL_Field_List;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean)
is
C : Field_List.Cursor := First (Self);
begin
while Has_Element (C) loop
Append_If_Not_Aggregate (Element (C), To, Is_Aggregate);
Next (C);
end loop;
end Append_If_Not_Aggregate;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : access As_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean) is
begin
Append_If_Not_Aggregate (Self.Renamed, To, Is_Aggregate);
end Append_If_Not_Aggregate;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : access Sorted_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean) is
begin
Append_If_Not_Aggregate (Self.Sorted, To, Is_Aggregate);
end Append_If_Not_Aggregate;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : access Multiple_Args_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean)
is
C : Field_List.Cursor := First (Self.List);
begin
while Has_Element (C) loop
Append_If_Not_Aggregate (Element (C), To, Is_Aggregate);
Next (C);
end loop;
end Append_If_Not_Aggregate;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : access Case_Stmt_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean)
is
C : When_Lists.Cursor := First (Self.Criteria.List);
begin
while Has_Element (C) loop
Append_If_Not_Aggregate (Element (C).Criteria, To, Is_Aggregate);
Append_If_Not_Aggregate (Element (C).Field, To, Is_Aggregate);
Next (C);
end loop;
if Self.Else_Clause /= No_Field_Pointer then
Append_If_Not_Aggregate (Self.Else_Clause, To, Is_Aggregate);
end if;
end Append_If_Not_Aggregate;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : SQL_Criteria_Data;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean)
is
C : Criteria_Lists.Cursor;
begin
case Self.Op is
when Criteria_Combine =>
C := First (Self.Criterias);
while Has_Element (C) loop
Append_If_Not_Aggregate (Element (C), To, Is_Aggregate);
Next (C);
end loop;
when Criteria_In | Criteria_Not_In =>
Append_If_Not_Aggregate (Self.Arg, To, Is_Aggregate);
when Criteria_Exists =>
null;
when Criteria_Between | Criteria_Not_Between =>
Append_If_Not_Aggregate (Self.Arg2, To, Is_Aggregate);
when Null_Criteria =>
Append_If_Not_Aggregate (Self.Arg3, To, Is_Aggregate);
when Criteria_Not =>
Append_If_Not_Aggregate (Self.Criteria, To, Is_Aggregate);
end case;
end Append_If_Not_Aggregate;
-----------------------------
-- Append_If_Not_Aggregate --
-----------------------------
procedure Append_If_Not_Aggregate
(Self : access Aggregate_Field_Internal;
To : in out SQL_Field_List'Class;
Is_Aggregate : in out Boolean)
is
pragma Unreferenced (Self, To);
begin
Is_Aggregate := True;
end Append_If_Not_Aggregate;
----------------------
-- SQL_Create_Table --
----------------------
function SQL_Create_Table
(Name : String;
As : SQL_Query;
Temp : Boolean := False;
On_Commit : Temp_Table_Behavior := Preserve_Rows) return SQL_Query
is
Data : Query_Create_Table_As_Contents;
Q : SQL_Query;
begin
Data.Name := To_Unbounded_String (Name);
Data.As := As;
Data.Temp := Temp;
Data.On_Commit := On_Commit;
Q.Set (Data);
return Q;
end SQL_Create_Table;
---------------
-- To_String --
---------------
function To_String
(Self : Query_Create_Table_As_Contents; Format : Formatter'Class)
return Unbounded_String
is
Result : Unbounded_String;
begin
Result := To_Unbounded_String ("CREATE ");
if Self.Temp then
Append (Result, "TEMPORARY ");
end if;
Append (Result, "TABLE ");
Append (Result, Self.Name);
if Self.Temp then
Append (Result, " ON COMMIT ");
case Self.On_Commit is
when Preserve_Rows => Append (Result, "PRESERVE ROWS");
when Delete_Rows => Append (Result, "DELETE ROWS");
when Drop => Append (Result, "DROP");
end case;
end if;
Append (Result, " AS (");
Append (Result, Unbounded_String'(To_String (Self.As, Format)));
Append (Result, ')');
return Result;
end To_String;
----------------
-- SQL_Delete --
----------------
function SQL_Delete
(From : SQL_Table'Class;
Where : SQL_Criteria := No_Criteria) return SQL_Query
is
Data : Query_Delete_Contents;
Q : SQL_Query;
begin
Data.Table := +From;
Data.Where := Where;
Q.Set (Data);
return Q;
end SQL_Delete;
---------------
-- To_String --
---------------
function To_String
(Self : Query_Delete_Contents; Format : Formatter'Class)
return Unbounded_String
is
Result : Unbounded_String;
begin
Result := To_Unbounded_String ("DELETE FROM ");
Append (Result,
To_String (Element (First (Self.Table.Data.Get)), Format));
if Self.Where /= No_Criteria then
Append (Result, " WHERE ");
Append
(Result,
GNATCOLL.SQL_Impl.To_String (Self.Where, Format, Long => False));
end if;
return Result;
end To_String;
-------------------------------
-- SQL_Insert_Default_Values --
-------------------------------
function SQL_Insert_Default_Values
(Table : SQL_Table'Class) return SQL_Query
is
Data : Query_Insert_Contents;
Q : SQL_Query;
begin
Data.Into := (Name => Table.Table_Name,
Instance => Table.Instance,
Instance_Index => Table.Instance_Index);
Data.Default_Values := True;
Q.Set (Data);
return Q;
end SQL_Insert_Default_Values;
----------------
-- SQL_Insert --
----------------
function SQL_Insert
(Fields : SQL_Field_Or_List'Class;
Values : SQL_Query;
Qualifier : String := "") return SQL_Query
is
Data : Query_Insert_Contents;
Q : SQL_Query;
begin
if Fields in SQL_Field'Class then
Data.Fields := +SQL_Field'Class (Fields);
else
Data.Fields := SQL_Field_List (Fields);
end if;
Data.Into := No_Names;
Data.Subquery := Values;
if Qualifier /= "" then
Data.Qualifier := To_Unbounded_String (Qualifier);
end if;
Q.Set (Data);
Auto_Complete (Q);
return Q;
end SQL_Insert;
----------------
-- SQL_Insert --
----------------
function SQL_Insert
(Values : SQL_Assignment;
Where : SQL_Criteria := No_Criteria;
Limit : Integer := -1;
Qualifier : String := "") return SQL_Query
is
Data : Query_Insert_Contents;
Q : SQL_Query;
begin
Data.Into := No_Names;
Data.Values := Values;
Data.Where := Where;
Data.Limit := Limit;
if Qualifier /= "" then
Data.Qualifier := To_Unbounded_String (Qualifier);
end if;
Q.Set (Data);
Auto_Complete (Q);
return Q;
end SQL_Insert;
---------------
-- To_String --
---------------
function To_String
(Self : Query_Insert_Contents; Format : Formatter'Class)
return Unbounded_String
is
Result : Unbounded_String;
begin
if Self.Qualifier = "" then
Result := To_Unbounded_String ("INSERT INTO ");
else
Result := "INSERT " & Self.Qualifier & " INTO ";
end if;
Append (Result, To_String (Self.Into));
if Self.Default_Values then
Append (Result, " DEFAULT VALUES");
else
if Self.Fields /= Empty_Field_List then
Append (Result, " (");
Append (Result, To_String (Self.Fields, Format, Long => False));
Append (Result, ")");
end if;
declare
Assign : constant String :=
To_String (Self.Values, Format, With_Field => False);
begin
if Assign /= "" then
Append (Result, " VALUES (" & Assign & ")");
end if;
end;
if Self.Subquery /= No_Query then
Append (Result, " ");
Append (Result, To_String (Self.Subquery, Format));
end if;
end if;
return Result;
end To_String;
-------------------
-- Auto_Complete --
-------------------
procedure Auto_Complete
(Self : in out Query_Insert_Contents;
Auto_Complete_From : Boolean := True;
Auto_Complete_Group_By : Boolean := True)
is
pragma Unreferenced (Auto_Complete_Group_By);
List, List2 : Table_Sets.Set;
Subfields : SQL_Field_List;
begin
if Auto_Complete_From then
-- Get the list of fields first, so that we'll also know what table
-- is being updated
if Self.Fields = Empty_Field_List then
Get_Fields (Self.Values, Self.Fields);
end if;
if Self.Into = No_Names then
-- For each field, make sure the table is in the list
Append_Tables (Self.Fields, List);
-- We must have a single table here, or that's a bug
if Length (List) /= 1 then
raise Program_Error
with "Invalid list of fields to insert, they all must modify"
& " the same table";
end if;
-- Grab the table from the first field
Self.Into := Element (First (List));
end if;
if Self.Subquery = No_Query then
-- Do we have other tables impacted from the list of values we
-- set for the fields ? If yes, we'll need to transform the
-- simple query into a subquery
Clear (List);
Append_Tables (Self.Values, List);
if Self.Into /= No_Names then
Table_Sets.Include (List2, Self.Into);
end if;
Difference (List, List2); -- Remove tables already in the list
if Length (List) > 0 then
To_List (Self.Values, Subfields);
Self.Subquery := SQL_Select
(Fields => Subfields, Where => Self.Where,
Limit => Self.Limit);
Auto_Complete (Self.Subquery);
Self.Values := No_Assignment;
end if;
end if;
end if;
end Auto_Complete;
----------------
-- SQL_Update --
----------------
function SQL_Update
(Table : SQL_Table'Class;
Set : SQL_Assignment;
Where : SQL_Criteria := No_Criteria;
From : SQL_Table_Or_List'Class := Empty_Table_List) return SQL_Query
is
Data : Query_Update_Contents;
Q : SQL_Query;
begin
Data.Table := +Table;
Data.Set := Set;
Data.Where := Where;
if From in SQL_Table'Class then
Data.From := +SQL_Table'Class (From);
else
Data.From := SQL_Table_List (From);
end if;
Q.Set (Data);
return Q;
end SQL_Update;
---------------
-- To_String --
---------------
function To_String
(Self : Query_Update_Contents; Format : Formatter'Class)
return Unbounded_String
is
Result : Unbounded_String;
begin
Result := To_Unbounded_String ("UPDATE ");
Append (Result,
To_String (Element (First (Self.Table.Data.Get)), Format));
Append (Result, " SET ");
Append (Result, To_String (Self.Set, Format, With_Field => True));
if Self.From /= Empty_Table_List
or else not Is_Empty (Self.Extra_From)
then
Append (Result, " FROM ");
if Self.From.Data.Is_Null or else Self.From.Data.Get.Is_Empty then
Append (Result, To_String (Self.Extra_From));
elsif Is_Empty (Self.Extra_From) then
Append (Result, To_String (Self.From, Format));
else
Append (Result, To_String (Self.From, Format));
Append (Result, ", ");
Append (Result, To_String (Self.Extra_From));
end if;
end if;
if Self.Where /= No_Criteria then
Append (Result, " WHERE ");
Append
(Result,
GNATCOLL.SQL_Impl.To_String (Self.Where, Format, Long => True));
end if;
return Result;
end To_String;
-------------------
-- Auto_Complete --
-------------------
procedure Auto_Complete
(Self : in out Query_Update_Contents;
Auto_Complete_From : Boolean := True;
Auto_Complete_Group_By : Boolean := True)
is
pragma Unreferenced (Auto_Complete_Group_By);
List2 : Table_Sets.Set;
begin
if Auto_Complete_From then
-- For each field, make sure the table is in the list
Append_Tables (Self.Set, Self.Extra_From);
Append_Tables (Self.Where, Self.Extra_From);
-- Remove tables already in the list
Append_Tables (Self.From, List2);
Append_Tables (Self.Table, List2);
Difference (Self.Extra_From, List2);
end if;
end Auto_Complete;
---------------
-- Left_Join --
---------------
function Left_Join
(Full : SQL_Single_Table'Class;
Partial : SQL_Single_Table'Class;
On : SQL_Criteria) return SQL_Left_Join_Table
is
begin
return Result : SQL_Left_Join_Table
(Instance => null, Instance_Index => -1)
do
Result.Data.Set
(Join_Table_Internal'
(Tables => Full & Partial,
Is_Left_Join => True,
On => On));
end return;
end Left_Join;
----------
-- Join --
----------
function Join
(Table1 : SQL_Single_Table'Class;
Table2 : SQL_Single_Table'Class;
On : SQL_Criteria := No_Criteria) return SQL_Left_Join_Table
is
R : constant SQL_Left_Join_Table := Left_Join (Table1, Table2, On);
begin
R.Data.Get.Is_Left_Join := False;
return R;
end Join;
------------
-- Rename --
------------
function Rename
(Self : SQL_Left_Join_Table; Name : Cst_String_Access)
return SQL_Left_Join_Table'Class
is
R : SQL_Left_Join_Table (Instance => Name, Instance_Index => -1);
begin
R.Data := Self.Data;
return R;
end Rename;
---------------
-- To_String --
---------------
function To_String
(Self : Simple_Query_Contents; Format : Formatter'Class)
return Unbounded_String
is
pragma Unreferenced (Format);
begin
return Self.Command;
end To_String;
--------------
-- SQL_Lock --
--------------
function SQL_Lock (Table : SQL_Table'Class) return SQL_Query is
Data : Simple_Query_Contents;
Q : SQL_Query;
begin
Data.Command := To_Unbounded_String ("LOCK " & To_String (Table));
Q.Set (Data);
return Q;
end SQL_Lock;
---------------
-- SQL_Begin --
---------------
function SQL_Begin return SQL_Query is
Data : Simple_Query_Contents;
Q : SQL_Query;
begin
Data.Command := To_Unbounded_String ("BEGIN");
Q.Set (Data);
return Q;
end SQL_Begin;
------------------
-- SQL_Rollback --
------------------
function SQL_Rollback return SQL_Query is
Data : Simple_Query_Contents;
Q : SQL_Query;
begin
Data.Command := To_Unbounded_String ("ROLLBACK");
Q.Set (Data);
return Q;
end SQL_Rollback;
----------------
-- SQL_Commit --
----------------
function SQL_Commit return SQL_Query is
Data : Simple_Query_Contents;
Q : SQL_Query;
begin
Data.Command := To_Unbounded_String ("COMMIT");
Q.Set (Data);
return Q;
end SQL_Commit;
--------------
-- Subquery --
--------------
function Subquery
(Query : SQL_Query'Class; Table_Name : Cst_String_Access)
return Subquery_Table
is
begin
return R : Subquery_Table
(Instance => Table_Name, Instance_Index => -1)
do
R.Query := SQL_Query (Query);
end return;
end Subquery;
----------
-- Free --
----------
procedure Free (A : in out SQL_Table_Access) is
begin
Unchecked_Free (A);
end Free;
---------------
-- Where_And --
---------------
function Where_And
(Query : SQL_Query; Where : SQL_Criteria) return SQL_Query
is
Q2 : SQL_Query;
begin
if Query.Get.all not in Query_Select_Contents'Class then
raise Program_Error with "not a SELECT query";
end if;
declare
Contents : Query_Select_Contents'Class := -- clone contents
Query_Select_Contents'Class (Query.Get.all);
begin
Contents.Criteria := Contents.Criteria and Where;
Q2.Set (Contents);
return Q2;
end;
end Where_And;
--------------
-- Where_Or --
--------------
function Where_Or
(Query : SQL_Query; Where : SQL_Criteria) return SQL_Query
is
Q2 : SQL_Query;
begin
if Query.Get.all not in Query_Select_Contents'Class then
raise Program_Error with "not a SELECT query";
end if;
declare
Contents : Query_Select_Contents'Class := -- clone contents
Query_Select_Contents'Class (Query.Get.all);
begin
Contents.Criteria := Contents.Criteria or Where;
Q2.Set (Contents);
return Q2;
end;
end Where_Or;
--------------
-- Order_By --
--------------
function Order_By
(Query : SQL_Query; Order_By : SQL_Field_Or_List'Class)
return SQL_Query
is
Q2 : SQL_Query;
begin
if Query.Get.all not in Query_Select_Contents'Class then
raise Program_Error with "not a SELECT query";
end if;
declare
Contents : Query_Select_Contents'Class := -- clone contents
Query_Select_Contents'Class (Query.Get.all);
begin
if Order_By in SQL_Field'Class then
Contents.Order_By :=
SQL_Field'Class (Order_By) & Contents.Order_By;
else
Contents.Order_By := SQL_Field_List (Order_By) & Contents.Order_By;
end if;
Q2.Set (Contents);
return Q2;
end;
end Order_By;
--------------
-- Distinct --
--------------
function Distinct (Query : SQL_Query) return SQL_Query is
Q2 : SQL_Query;
begin
if Query.Get.all not in Query_Select_Contents'Class then
raise Program_Error with "not a SELECT query";
end if;
declare
Contents : Query_Select_Contents'Class := -- clone contents
Query_Select_Contents'Class (Query.Get.all);
begin
Contents.Distinct := True;
Q2.Set (Contents);
return Q2;
end;
end Distinct;
-----------
-- Limit --
-----------
function Limit (Query : SQL_Query; Limit : Natural) return SQL_Query is
Q2 : SQL_Query;
begin
if Query.Get.all not in Query_Select_Contents'Class then
raise Program_Error with "not a SELECT query";
end if;
declare
Contents : Query_Select_Contents'Class := -- clone contents
Query_Select_Contents'Class (Query.Get.all);
begin
Contents.Limit := Limit;
Q2.Set (Contents);
return Q2;
end;
end Limit;
------------
-- Offset --
------------
function Offset (Query : SQL_Query; Offset : Natural) return SQL_Query is
Q2 : SQL_Query;
begin
if Query.Get.all not in Query_Select_Contents'Class then
raise Program_Error with "not a SELECT query";
end if;
declare
Contents : Query_Select_Contents'Class := -- clone contents
Query_Select_Contents'Class (Query.Get.all);
begin
Contents.Offset := Offset;
Q2.Set (Contents);
return Q2;
end;
end Offset;
---------
-- "=" --
---------
function "="
(Left : SQL_Field'Class; Query : SQL_Query) return SQL_Assignment
is
Data : Query_Field_Internal;
F : Field_Pointers.Ref;
begin
Data.Query := Query;
F.Set (Data);
return Create
(Left,
SQL_Field_Any'
(Table => null,
Instance => null,
Name => null,
Instance_Index => -1,
Data => F));
end "=";
---------------
-- To_String --
---------------
overriding function To_String
(Self : Query_Field_Internal;
Format : Formatter'Class;
Long : Boolean) return String
is
pragma Unreferenced (Long);
begin
return "(" & To_String (To_String (Self.Query, Format)) & ")";
end To_String;
end GNATCOLL.SQL;