You've already forked gnatcoll-db
mirror of
https://github.com/AdaCore/gnatcoll-db.git
synced 2026-02-12 12:59:31 -08:00
no-tn-check GNAT style check became more strict. Change-Id: I24b59f7b3ffdfa58d8a54b3ede6bfa56fbdcc46d
3415 lines
125 KiB
Ada
3415 lines
125 KiB
Ada
------------------------------------------------------------------------------
|
|
-- G N A T C O L L --
|
|
-- --
|
|
-- Copyright (C) 2011-2020, 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/>. --
|
|
-- --
|
|
------------------------------------------------------------------------------
|
|
|
|
pragma Ada_2012;
|
|
with Ada.Characters.Handling; use Ada.Characters.Handling;
|
|
with Ada.Command_Line;
|
|
with Ada.Containers.Hashed_Sets; use Ada.Containers;
|
|
with Ada.Exceptions; use Ada.Exceptions;
|
|
with Ada.Strings.Fixed; use Ada.Strings, Ada.Strings.Fixed;
|
|
with Ada.Strings.Maps; use Ada.Strings.Maps;
|
|
with Ada.Text_IO; use Ada.Text_IO;
|
|
with Ada.Unchecked_Deallocation;
|
|
with GNAT.Strings; use GNAT.Strings;
|
|
with GNATCOLL.Mmap; use GNATCOLL.Mmap;
|
|
with GNATCOLL.Strings; use GNATCOLL.Strings;
|
|
with GNATCOLL.Traces; use GNATCOLL.Traces;
|
|
with GNATCOLL.VFS; use GNATCOLL.VFS;
|
|
with GNATCOLL.Utils; use GNATCOLL.Utils;
|
|
|
|
package body GNATCOLL.SQL.Inspect is
|
|
Me : constant Trace_Handle := Create ("SQL.INSPECT");
|
|
|
|
use Tables_Maps, Field_Lists, Foreign_Refs;
|
|
use Foreign_Keys, Pair_Lists, Tables_Lists;
|
|
use String_Lists, String_Sets;
|
|
|
|
package Field_Mapping_Vectors is new Ada.Containers.Indefinite_Vectors
|
|
(Positive, Field_Mapping'Class);
|
|
All_Field_Mappings : Field_Mapping_Vectors.Vector;
|
|
-- When you create new field types, they should be registered in this list.
|
|
-- Put an uninitialized instance of the field type in the list. A copy of
|
|
-- it will be used to call Type_From_SQL when parsing the database schema.
|
|
|
|
Invalid_Schema : exception;
|
|
|
|
Keywords : String_Sets.Set;
|
|
|
|
Max_Fields_Per_Line : constant := 30;
|
|
-- Maximum number of fields per line (separated by '|')
|
|
|
|
procedure Parse_Line
|
|
(Line : in out String_List;
|
|
Line_Number : in out Natural;
|
|
Fields_Count : out Natural;
|
|
Data : String;
|
|
First : in out Integer;
|
|
Replace_Newline : Boolean := True);
|
|
-- Parse the current line and set Line and Fields_Count as appropriate.
|
|
-- Fields_Count is set to 0 if the current line is not part of a table
|
|
-- and should be ignored.
|
|
|
|
procedure Parse_Table
|
|
(Self : DB_Schema_IO'Class;
|
|
Table : Table_Description;
|
|
Attributes : in out Field_List);
|
|
-- Get the attributes of the specified table
|
|
|
|
procedure Mark_FK_As_Ambiguous
|
|
(Table : in out Table_Description;
|
|
Foreign : Table_Description;
|
|
Ambiguous : out Boolean);
|
|
-- Mark all foreign keys from Table to Foreign as ambiguous (ie there are
|
|
-- multiple references to the same foreign table, so we need special care
|
|
-- in code generation). Ambiguous is set to False if there was no such
|
|
-- FK yet.
|
|
|
|
function Get_To (FK : Foreign_Key; Pair : Field_Pair) return Field;
|
|
-- Return the field this points to (possibly the primary key of another
|
|
-- table if FK.To is unset)
|
|
|
|
function EOW (Str : String; First : Integer) return Natural;
|
|
-- Return the position of the next '|'
|
|
|
|
procedure Append
|
|
(List : in out String_List; Last : in out Natural; Str : String);
|
|
-- Add a new element to the list
|
|
|
|
procedure Format_Field
|
|
(DB : access Database_Connection_Record'Class;
|
|
Value : String;
|
|
Typ : Field_Mapping'Class;
|
|
Val : out GNAT.Strings.String_Access;
|
|
Param : out SQL_Parameter;
|
|
Has_Xref : Boolean);
|
|
-- Format a value for proper use in SQL.
|
|
-- This translates boolean values "true" and "false" as appropriate for
|
|
-- the backend.
|
|
|
|
----------------------------
|
|
-- Register_Field_Mapping --
|
|
----------------------------
|
|
|
|
procedure Register_Field_Mapping (Self : Field_Mapping'Class) is
|
|
begin
|
|
All_Field_Mappings.Append (Self);
|
|
end Register_Field_Mapping;
|
|
|
|
---------------------------
|
|
-- Simple_Field_Mappings --
|
|
---------------------------
|
|
|
|
package body Simple_Field_Mappings is
|
|
|
|
--------------------
|
|
-- Parameter_Type --
|
|
--------------------
|
|
|
|
overriding function Parameter_Type
|
|
(Self : Simple_Field_Mapping) return SQL_Parameter_Type'Class
|
|
is
|
|
pragma Unreferenced (Self);
|
|
Dummy : Param_Type;
|
|
-- intentionally uninitialized, only the specific type is relevant
|
|
-- as return value
|
|
begin
|
|
return Dummy;
|
|
end Parameter_Type;
|
|
|
|
-----------------
|
|
-- Type_To_SQL --
|
|
-----------------
|
|
|
|
overriding function Type_To_SQL
|
|
(Self : Simple_Field_Mapping;
|
|
Format : access Formatter'Class := null;
|
|
For_Database : Boolean := True) return String
|
|
is
|
|
pragma Unreferenced (Self, Format);
|
|
begin
|
|
return (if For_Database then SQL_Type else Ada_Field_Mapping);
|
|
end Type_To_SQL;
|
|
|
|
begin
|
|
Register_Field_Mapping
|
|
(Simple_Field_Mapping'(Field_Mapping with null record));
|
|
end Simple_Field_Mappings;
|
|
|
|
package Bigint_Mappings is new Simple_Field_Mappings
|
|
("bigint", "SQL_Field_Bigint", SQL_Parameter_Bigint);
|
|
package Boolean_Mappings is new GNATCOLL.SQL.Inspect.Simple_Field_Mappings
|
|
("boolean", "SQL_Field_Boolean", SQL_Parameter_Boolean);
|
|
package Time_Mappings is new Simple_Field_Mappings
|
|
("time", "SQL_Field_Time", SQL_Parameter_Time);
|
|
package Date_Mappings is new Simple_Field_Mappings
|
|
("date", "SQL_Field_Date", SQL_Parameter_Date);
|
|
pragma Unreferenced (Bigint_Mappings, Time_Mappings, Date_Mappings);
|
|
-- The side effect is to register the mappings
|
|
|
|
---------
|
|
-- EOW --
|
|
---------
|
|
|
|
function EOW (Str : String; First : Integer) return Natural is
|
|
begin
|
|
return Find_Char (Str (First .. Str'Last), '|');
|
|
end EOW;
|
|
|
|
------------
|
|
-- Get_To --
|
|
------------
|
|
|
|
function Get_To (FK : Foreign_Key; Pair : Field_Pair) return Field is
|
|
begin
|
|
if Pair.To = No_Field then
|
|
return Get_PK (FK.To_Table);
|
|
else
|
|
return Pair.To;
|
|
end if;
|
|
end Get_To;
|
|
|
|
--------
|
|
-- Id --
|
|
--------
|
|
|
|
function Id (Self : Field) return Positive is
|
|
begin
|
|
return Self.Get.Id;
|
|
end Id;
|
|
|
|
----------
|
|
-- Name --
|
|
----------
|
|
|
|
function Name (Self : Field) return String is
|
|
begin
|
|
return Self.Get.Name.all;
|
|
end Name;
|
|
|
|
-----------------
|
|
-- Description --
|
|
-----------------
|
|
|
|
function Description (Self : Field) return String is
|
|
Descr : constant GNAT.Strings.String_Access := Self.Get.Description;
|
|
begin
|
|
if Descr = null then
|
|
return "";
|
|
else
|
|
return Descr.all;
|
|
end if;
|
|
end Description;
|
|
|
|
---------------
|
|
-- Get_Table --
|
|
---------------
|
|
|
|
function Get_Table (Self : Field) return Table_Description'Class is
|
|
R : Table_Description;
|
|
begin
|
|
R.Set (Self.Get.Table);
|
|
return R;
|
|
end Get_Table;
|
|
|
|
--------------
|
|
-- Get_Type --
|
|
--------------
|
|
|
|
function Get_Type (Self : Field) return Field_Mapping_Access is
|
|
FK : constant Field := Self.Is_FK;
|
|
T : Field_Mapping_Access;
|
|
begin
|
|
if FK = No_Field then
|
|
return Self.Get.Typ;
|
|
else
|
|
T := Get_Type (FK);
|
|
if T.all in Field_Mapping_Autoincrement'Class then
|
|
-- Do not return T itself, or the table would end up with two
|
|
-- primary keys (since autoincrement fields are only used for
|
|
-- primary keys).
|
|
return new Field_Mapping_Integer; -- ??? memory leak
|
|
else
|
|
return T;
|
|
end if;
|
|
end if;
|
|
end Get_Type;
|
|
|
|
----------------
|
|
-- Set_Active --
|
|
----------------
|
|
|
|
procedure Set_Active (Self : in out Field; Active : Boolean) is
|
|
begin
|
|
Self.Get.Active := Active;
|
|
end Set_Active;
|
|
|
|
---------------
|
|
-- Is_Active --
|
|
---------------
|
|
|
|
function Is_Active (Self : Field) return Boolean is
|
|
begin
|
|
return Self.Get.Active;
|
|
end Is_Active;
|
|
|
|
-----------------
|
|
-- Can_Be_Null --
|
|
-----------------
|
|
|
|
function Can_Be_Null (Self : Field) return Boolean is
|
|
begin
|
|
return not Self.Get.Props.Not_Null;
|
|
end Can_Be_Null;
|
|
|
|
-------------
|
|
-- Default --
|
|
-------------
|
|
|
|
function Default (Self : Field) return String is
|
|
Def : constant GNAT.Strings.String_Access := Self.Get.Default;
|
|
begin
|
|
if Def = null then
|
|
return "";
|
|
else
|
|
return Def.all;
|
|
end if;
|
|
end Default;
|
|
|
|
-----------
|
|
-- Is_PK --
|
|
-----------
|
|
|
|
function Is_PK (Self : Field) return Boolean is
|
|
begin
|
|
return Self.Get.Props.PK;
|
|
end Is_PK;
|
|
|
|
------------
|
|
-- Get_PK --
|
|
------------
|
|
|
|
function Get_PK (Self : Table_Description'Class) return Field is
|
|
F : Field_Lists.Cursor := TDR (Self.Unchecked_Get).Fields.First;
|
|
PK : Field := No_Field;
|
|
begin
|
|
while Has_Element (F) loop
|
|
if Element (F).Get.Props.PK then
|
|
if PK = No_Field then
|
|
PK := Element (F);
|
|
else
|
|
return No_Field; -- Primary key is a tuple
|
|
end if;
|
|
end if;
|
|
|
|
Next (F);
|
|
end loop;
|
|
|
|
return PK;
|
|
end Get_PK;
|
|
|
|
-----------
|
|
-- Is_FK --
|
|
-----------
|
|
|
|
function Is_FK (Self : Field) return Field is
|
|
T : Table_Description;
|
|
C : Foreign_Keys.Cursor;
|
|
begin
|
|
if Self.Get.FK then
|
|
T := Table_Description (Get_Table (Self));
|
|
C := TDR (T.Unchecked_Get).FK.First;
|
|
while Has_Element (C) loop
|
|
declare
|
|
A : Pair_Lists.Cursor := Element (C).Get.Fields.First;
|
|
P : Field_Pair;
|
|
begin
|
|
while Has_Element (A) loop
|
|
P := Element (A);
|
|
if P.From = Self then
|
|
return Get_To (Element (C), P);
|
|
end if;
|
|
|
|
Next (A);
|
|
end loop;
|
|
end;
|
|
|
|
Next (C);
|
|
end loop;
|
|
end if;
|
|
|
|
return No_Field;
|
|
end Is_FK;
|
|
|
|
----------
|
|
-- Free --
|
|
----------
|
|
|
|
procedure Free (Self : in out Field_Description) is
|
|
procedure Unchecked_Free is new Ada.Unchecked_Deallocation
|
|
(Field_Mapping'Class, Field_Mapping_Access);
|
|
begin
|
|
Free (Self.Name);
|
|
Free (Self.Description);
|
|
Free (Self.Default);
|
|
Unchecked_Free (Self.Typ);
|
|
end Free;
|
|
|
|
--------
|
|
-- Id --
|
|
--------
|
|
|
|
function Id (Self : Table_Description) return Positive is
|
|
begin
|
|
return TDR (Self.Unchecked_Get).Id;
|
|
end Id;
|
|
|
|
--------------
|
|
-- Get_Kind --
|
|
--------------
|
|
|
|
function Get_Kind (Self : Table_Description) return Relation_Kind is
|
|
begin
|
|
return TDR (Self.Unchecked_Get).Kind;
|
|
end Get_Kind;
|
|
|
|
----------
|
|
-- Name --
|
|
----------
|
|
|
|
function Name (Self : Table_Description) return String is
|
|
begin
|
|
return TDR (Self.Unchecked_Get).Name.all;
|
|
end Name;
|
|
|
|
--------------
|
|
-- Row_Name --
|
|
--------------
|
|
|
|
function Row_Name (Self : Table_Description) return String is
|
|
Row : constant GNAT.Strings.String_Access :=
|
|
TDR (Self.Unchecked_Get).Row;
|
|
begin
|
|
if Row = null then
|
|
return Name (Self);
|
|
else
|
|
return Row.all;
|
|
end if;
|
|
end Row_Name;
|
|
|
|
-----------------
|
|
-- Description --
|
|
-----------------
|
|
|
|
function Description (Self : Table_Description) return String is
|
|
Descr : constant GNAT.Strings.String_Access :=
|
|
TDR (Self.Unchecked_Get).Description;
|
|
begin
|
|
if Descr = null then
|
|
return "";
|
|
else
|
|
return Descr.all;
|
|
end if;
|
|
end Description;
|
|
|
|
-----------------
|
|
-- Is_Abstract --
|
|
-----------------
|
|
|
|
function Is_Abstract (Self : Table_Description) return Boolean is
|
|
begin
|
|
return TDR (Self.Unchecked_Get).Is_Abstract;
|
|
end Is_Abstract;
|
|
|
|
----------------
|
|
-- Set_Active --
|
|
----------------
|
|
|
|
procedure Set_Active (Self : in out Table_Description; Active : Boolean) is
|
|
begin
|
|
TDR (Self.Unchecked_Get).Active := Active;
|
|
end Set_Active;
|
|
|
|
---------------
|
|
-- Is_Active --
|
|
---------------
|
|
|
|
function Is_Active (Self : Table_Description) return Boolean is
|
|
begin
|
|
return TDR (Self.Unchecked_Get).Active;
|
|
end Is_Active;
|
|
|
|
-----------------
|
|
-- Super_Table --
|
|
-----------------
|
|
|
|
function Super_Table (Self : Table_Description) return Table_Description is
|
|
begin
|
|
return TDR (Self.Unchecked_Get).Super_Table;
|
|
end Super_Table;
|
|
|
|
--------------------
|
|
-- For_Each_Field --
|
|
--------------------
|
|
|
|
procedure For_Each_Field
|
|
(Self : Table_Description;
|
|
Callback : access procedure (F : in out Field);
|
|
Include_Inherited : Boolean := False)
|
|
is
|
|
C : Field_Lists.Cursor := TDR (Self.Unchecked_Get).Fields.First;
|
|
F : Field;
|
|
begin
|
|
while Has_Element (C) loop
|
|
F := Element (C);
|
|
Callback (F);
|
|
Next (C);
|
|
end loop;
|
|
|
|
if Include_Inherited
|
|
and then TDR (Self.Unchecked_Get).Super_Table /= No_Table
|
|
then
|
|
For_Each_Field (Self.Super_Table, Callback, Include_Inherited);
|
|
end if;
|
|
end For_Each_Field;
|
|
|
|
---------------------
|
|
-- Field_From_Name --
|
|
---------------------
|
|
|
|
function Field_From_Name
|
|
(Self : Table_Description'Class; Name : String) return Field
|
|
is
|
|
Result : Field := No_Field;
|
|
|
|
procedure For_Field (F : in out Field);
|
|
procedure For_Field (F : in out Field) is
|
|
begin
|
|
if F.Name = Name then
|
|
Result := F;
|
|
end if;
|
|
end For_Field;
|
|
|
|
begin
|
|
For_Each_Field (Self, For_Field'Access, Include_Inherited => True);
|
|
return Result;
|
|
end Field_From_Name;
|
|
|
|
----------
|
|
-- Free --
|
|
----------
|
|
|
|
procedure Free (Self : in out Foreign_Key_Description) is
|
|
begin
|
|
Free (Self.Revert_Name);
|
|
end Free;
|
|
|
|
----------
|
|
-- Free --
|
|
----------
|
|
|
|
overriding procedure Free (Self : in out Table_Description_Record) is
|
|
begin
|
|
Free (Self.Name);
|
|
Free (Self.Row);
|
|
Free (Self.Description);
|
|
end Free;
|
|
|
|
---------------
|
|
-- Get_Table --
|
|
---------------
|
|
|
|
function Get_Table
|
|
(Self : DB_Schema; Name : String) return Table_Description
|
|
is
|
|
C : constant Tables_Maps.Cursor := Self.Tables.Find (Name);
|
|
begin
|
|
if C = Tables_Maps.No_Element then
|
|
raise Invalid_Table with "No such table: " & Name;
|
|
end if;
|
|
|
|
return Element (C);
|
|
end Get_Table;
|
|
|
|
--------------------
|
|
-- For_Each_Table --
|
|
--------------------
|
|
|
|
procedure For_Each_Table
|
|
(Self : DB_Schema;
|
|
Callback : access procedure (T : in out Table_Description);
|
|
Alphabetical : Boolean := True)
|
|
is
|
|
T : Table_Description;
|
|
begin
|
|
if Alphabetical
|
|
or else Self.Ordered_Tables.Is_Empty
|
|
then
|
|
declare
|
|
C : Tables_Maps.Cursor := Self.Tables.First;
|
|
begin
|
|
while Has_Element (C) loop
|
|
T := Element (C);
|
|
Callback (T);
|
|
Next (C);
|
|
end loop;
|
|
end;
|
|
|
|
else
|
|
declare
|
|
C : Tables_Lists.Cursor := Self.Ordered_Tables.First;
|
|
begin
|
|
while Has_Element (C) loop
|
|
T := Self.Tables.Element (Element (C));
|
|
Callback (T);
|
|
Next (C);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
end For_Each_Table;
|
|
|
|
-----------------
|
|
-- For_Each_FK --
|
|
-----------------
|
|
|
|
procedure For_Each_FK
|
|
(Self : Table_Description;
|
|
Callback : access procedure
|
|
(From, To : Field; Id : Natural; Ambiguous : Boolean))
|
|
is
|
|
F : Foreign_Keys.Cursor;
|
|
P : Pair_Lists.Cursor;
|
|
Id : Integer := 1;
|
|
begin
|
|
F := TDR (Self.Unchecked_Get).FK.First;
|
|
while Has_Element (F) loop
|
|
P := Element (F).Get.Fields.First;
|
|
while Has_Element (P) loop
|
|
Callback (From => Element (P).From,
|
|
To => Get_To (Element (F), Element (P)),
|
|
Ambiguous => Element (F).Get.Ambiguous,
|
|
Id => Id);
|
|
Next (P);
|
|
end loop;
|
|
|
|
Id := Id + 1;
|
|
Next (F);
|
|
end loop;
|
|
end For_Each_FK;
|
|
|
|
-------------------
|
|
-- Type_From_SQL --
|
|
-------------------
|
|
|
|
overriding function Type_From_SQL
|
|
(Self : in out Field_Mapping_Text; Str : String) return Boolean
|
|
is
|
|
function Process_Max_Length (Kind : String) return Boolean;
|
|
-- Try to take max length from sql type like varchar(123), returns True
|
|
-- on success, raises Invalid_Schema is value in brackets is not an
|
|
-- number.
|
|
|
|
------------------------
|
|
-- Process_Max_Length --
|
|
------------------------
|
|
|
|
function Process_Max_Length (Kind : String) return Boolean is
|
|
begin
|
|
if Str'Length > Kind'Length
|
|
and then Str (Str'First .. Str'First + Kind'Length) = Kind & '('
|
|
and then Str (Str'Last) = ')'
|
|
then
|
|
begin
|
|
Self.Max_Length := Integer'Value
|
|
(Str (Str'First + Kind'Length + 1 .. Str'Last - 1));
|
|
|
|
return True;
|
|
|
|
exception
|
|
when Constraint_Error =>
|
|
Put_Line
|
|
("Missing max length after '" & Kind & "' in " & Str);
|
|
raise Invalid_Schema;
|
|
end;
|
|
end if;
|
|
|
|
return False;
|
|
end Process_Max_Length;
|
|
|
|
begin
|
|
if Str in "text" | "varchar" | "nvarchar"
|
|
or else (Str'Length >= 10 -- "character varying(...)"
|
|
and then Str (Str'First .. Str'First + 9) = "character ")
|
|
then
|
|
Self.Max_Length := Integer'Last;
|
|
return True;
|
|
else
|
|
return Process_Max_Length ("varchar")
|
|
or else Process_Max_Length ("character")
|
|
or else Process_Max_Length ("nvarchar");
|
|
end if;
|
|
end Type_From_SQL;
|
|
|
|
-------------------
|
|
-- Type_From_SQL --
|
|
-------------------
|
|
|
|
overriding function Type_From_SQL
|
|
(Self : in out Field_Mapping_Integer; Str : String) return Boolean
|
|
is
|
|
pragma Unreferenced (Self);
|
|
begin
|
|
if Str in "integer" | "smallint" | "oid" then
|
|
return True;
|
|
|
|
elsif Str'Length > 7
|
|
and then Str (Str'First .. Str'First + 6) in "numeric" | "decimal"
|
|
then
|
|
-- Check the scale
|
|
|
|
for Comma in reverse Str'Range loop
|
|
if Str (Comma) = ',' then
|
|
return Str (Comma + 1 .. Str'Last - 1) = "0";
|
|
end if;
|
|
end loop;
|
|
|
|
return True;
|
|
|
|
else
|
|
return False;
|
|
end if;
|
|
end Type_From_SQL;
|
|
|
|
-------------------
|
|
-- Type_From_SQL --
|
|
-------------------
|
|
|
|
overriding function Type_From_SQL
|
|
(Self : in out Field_Mapping_Float; Str : String) return Boolean
|
|
is
|
|
pragma Unreferenced (Self);
|
|
begin
|
|
if Str in "float" | "numeric" | "decimal" then
|
|
return True;
|
|
|
|
elsif Str'Length > 7
|
|
and then Str (Str'First .. Str'First + 6) in "numeric" | "decimal"
|
|
then
|
|
-- Check the scale
|
|
|
|
for Comma in reverse Str'Range loop
|
|
if Str (Comma) = ',' then
|
|
return Str (Comma + 1 .. Str'Last - 1) /= "0";
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
return False;
|
|
end Type_From_SQL;
|
|
|
|
-----------------
|
|
-- Type_To_SQL --
|
|
-----------------
|
|
|
|
overriding function Type_To_SQL
|
|
(Self : Field_Mapping_Text;
|
|
Format : access Formatter'Class := null;
|
|
For_Database : Boolean := True) return String
|
|
is
|
|
pragma Unreferenced (Format);
|
|
begin
|
|
return (if not For_Database then "SQL_Field_Text"
|
|
elsif Self.Max_Length = Integer'Last then "Text"
|
|
else "Character(" & Image (Self.Max_Length, 1) & ')');
|
|
end Type_To_SQL;
|
|
|
|
-----------------
|
|
-- Type_To_SQL --
|
|
-----------------
|
|
|
|
overriding function Type_To_SQL
|
|
(Self : Field_Mapping_Float;
|
|
Format : access Formatter'Class := null;
|
|
For_Database : Boolean := True) return String
|
|
is
|
|
pragma Unreferenced (Self, Format);
|
|
begin
|
|
return (if For_Database then "Float" else "SQL_Field_Float");
|
|
end Type_To_SQL;
|
|
|
|
-----------------
|
|
-- Type_To_SQL --
|
|
-----------------
|
|
|
|
overriding function Type_To_SQL
|
|
(Self : Field_Mapping_Integer;
|
|
Format : access Formatter'Class := null;
|
|
For_Database : Boolean := True) return String
|
|
is
|
|
pragma Unreferenced (Self, Format);
|
|
begin
|
|
return (if For_Database then "Integer" else "SQL_Field_Integer");
|
|
end Type_To_SQL;
|
|
|
|
-----------------
|
|
-- Type_To_SQL --
|
|
-----------------
|
|
|
|
overriding function Type_To_SQL
|
|
(Self : Field_Mapping_Timestamp;
|
|
Format : access Formatter'Class := null;
|
|
For_Database : Boolean := True) return String
|
|
is
|
|
pragma Unreferenced (Self, Format);
|
|
begin
|
|
return (if For_Database then "timestamp with time zone"
|
|
else "SQL_Field_Time");
|
|
end Type_To_SQL;
|
|
|
|
--------------
|
|
-- From_SQL --
|
|
--------------
|
|
|
|
function From_SQL (SQL_Type : String) return Field_Mapping_Access is
|
|
T : constant String := To_Lower (SQL_Type);
|
|
begin
|
|
-- Go into reverse order, so that custom fields take precedence
|
|
-- over the predefined fields
|
|
for F of reverse All_Field_Mappings loop
|
|
if F.Type_From_SQL (T) then
|
|
return new Field_Mapping'Class'(F);
|
|
end if;
|
|
end loop;
|
|
return null;
|
|
end From_SQL;
|
|
|
|
-----------------
|
|
-- Parse_Table --
|
|
-----------------
|
|
|
|
procedure Parse_Table
|
|
(Self : DB_Schema_IO'Class;
|
|
Table : Table_Description;
|
|
Attributes : in out Field_List)
|
|
is
|
|
procedure On_Field
|
|
(Name : String;
|
|
Typ : String;
|
|
Index : Natural;
|
|
Description : String;
|
|
Default_Value : String;
|
|
Is_Primary_Key : Boolean;
|
|
Not_Null : Boolean);
|
|
-- Called when a new field is discovered
|
|
|
|
procedure On_Field
|
|
(Name : String;
|
|
Typ : String;
|
|
Index : Natural;
|
|
Description : String;
|
|
Default_Value : String;
|
|
Is_Primary_Key : Boolean;
|
|
Not_Null : Boolean)
|
|
is
|
|
Descr : Field_Description;
|
|
Ref : Field;
|
|
T : constant Field_Mapping_Access := From_SQL (Typ);
|
|
begin
|
|
if T = null then
|
|
Put_Line
|
|
("Error: unknown field type " & Typ & " in " & Table.Name);
|
|
raise Invalid_Type;
|
|
end if;
|
|
|
|
Descr := Field_Description'
|
|
(Name => new String'(Name),
|
|
Typ => T,
|
|
Id => Index,
|
|
Description => new String'(Description),
|
|
Default => null,
|
|
Props => (PK => Is_Primary_Key,
|
|
Not_Null => Not_Null or else Is_Primary_Key,
|
|
others => <>),
|
|
FK => False,
|
|
Table => Table.Weak,
|
|
Active => True);
|
|
|
|
if Default_Value'Length < 8
|
|
or else Default_Value
|
|
(Default_Value'First .. Default_Value'First + 7)
|
|
/= "nextval("
|
|
then
|
|
Descr.Default := new String'(Default_Value);
|
|
end if;
|
|
|
|
Ref.Set (Descr);
|
|
Append (Attributes, Ref);
|
|
end On_Field;
|
|
|
|
begin
|
|
Foreach_Field
|
|
(Self.DB,
|
|
Table_Name => Table.Name,
|
|
Callback => On_Field'Access);
|
|
end Parse_Table;
|
|
|
|
-----------------
|
|
-- Read_Schema --
|
|
-----------------
|
|
|
|
overriding function Read_Schema
|
|
(Self : DB_Schema_IO) return DB_Schema
|
|
is
|
|
Schema : DB_Schema;
|
|
T : Natural := 0;
|
|
|
|
procedure On_Table (Name, Description : String; Kind : Relation_Kind);
|
|
-- Called when a new table is discovered
|
|
|
|
procedure Compute_Foreign_Keys
|
|
(Name : String; Table : in out Table_Description);
|
|
-- Compute the foreign keys for a specific table
|
|
|
|
function Field_From_Index
|
|
(Descr : Table_Description;
|
|
Index : Natural) return Field;
|
|
-- Return the field given its index in the table. Information
|
|
-- is extracted from All_Attrs
|
|
|
|
--------------
|
|
-- On_Table --
|
|
--------------
|
|
|
|
procedure On_Table (Name, Description : String; Kind : Relation_Kind) is
|
|
Descr : Table_Description_Record;
|
|
Ref : Table_Description;
|
|
begin
|
|
if not Match (Name, Self.Filter) then
|
|
return;
|
|
end if;
|
|
|
|
T := T + 1;
|
|
Descr.Id := T;
|
|
Descr.Kind := Kind;
|
|
Descr.Name := new String'(Name);
|
|
Descr.Row := null; -- Will default to Descr.Name
|
|
Descr.Description := new String'(Description);
|
|
Ref.Set (Descr);
|
|
|
|
Parse_Table (Self, Ref, TDR (Ref.Unchecked_Get).Fields);
|
|
|
|
Insert (Schema.Tables, Name, Ref);
|
|
Schema.Ordered_Tables.Append (Name);
|
|
end On_Table;
|
|
|
|
----------------------
|
|
-- Field_From_Index --
|
|
----------------------
|
|
|
|
function Field_From_Index
|
|
(Descr : Table_Description;
|
|
Index : Natural) return Field
|
|
is
|
|
A : Field_Lists.Cursor := First (TDR (Descr.Unchecked_Get).Fields);
|
|
begin
|
|
while Has_Element (A) loop
|
|
if Element (A).Id = Index then
|
|
return Element (A);
|
|
end if;
|
|
Next (A);
|
|
end loop;
|
|
return No_Field;
|
|
end Field_From_Index;
|
|
|
|
--------------------------
|
|
-- Compute_Foreign_Keys --
|
|
--------------------------
|
|
|
|
procedure Compute_Foreign_Keys
|
|
(Name : String; Table : in out Table_Description)
|
|
is
|
|
Prev_Index : Integer := -1;
|
|
To_Table : Table_Description;
|
|
Descr : Foreign_Key_Description;
|
|
R : Foreign_Key;
|
|
|
|
procedure On_Key
|
|
(Index : Positive;
|
|
Local_Attribute : Integer;
|
|
Foreign_Table : String;
|
|
Foreign_Attribute : Integer);
|
|
-- Called for each foreign key in the table
|
|
|
|
procedure On_Key
|
|
(Index : Positive;
|
|
Local_Attribute : Integer;
|
|
Foreign_Table : String;
|
|
Foreign_Attribute : Integer)
|
|
is
|
|
From : Field;
|
|
begin
|
|
if Prev_Index /= Index then
|
|
-- A new foreign key, as opposed to a new attribute in the same
|
|
-- key
|
|
|
|
if Prev_Index /= -1 then
|
|
R.Set (Descr);
|
|
Append (TDR (Table.Unchecked_Get).FK, R);
|
|
end if;
|
|
|
|
Prev_Index := Index;
|
|
|
|
To_Table := Get_Table (Schema, Foreign_Table);
|
|
Descr :=
|
|
(To_Table => To_Table.Weak,
|
|
Revert_Name => null,
|
|
Fields => Pair_Lists.Empty_Vector,
|
|
Ambiguous => False);
|
|
|
|
Mark_FK_As_Ambiguous (Table, To_Table, Descr.Ambiguous);
|
|
end if;
|
|
|
|
From := Field_From_Index (Table, Local_Attribute);
|
|
From.Get.FK := True;
|
|
Append
|
|
(Descr.Fields,
|
|
Field_Pair'
|
|
(From => From,
|
|
To => Field_From_Index (To_Table, Foreign_Attribute)));
|
|
end On_Key;
|
|
|
|
begin
|
|
Foreach_Foreign_Key
|
|
(Self.DB,
|
|
Table_Name => Name,
|
|
Callback => On_Key'Access);
|
|
|
|
if Prev_Index /= -1 then
|
|
R.Set (Descr);
|
|
Append (TDR (Table.Unchecked_Get).FK, R);
|
|
end if;
|
|
end Compute_Foreign_Keys;
|
|
|
|
C : Tables_Maps.Cursor;
|
|
|
|
begin
|
|
Foreach_Table (Self.DB, On_Table'Access);
|
|
|
|
C := First (Schema.Tables);
|
|
while Has_Element (C) loop
|
|
Update_Element (Schema.Tables, C, Compute_Foreign_Keys'Access);
|
|
Next (C);
|
|
end loop;
|
|
|
|
return Schema;
|
|
end Read_Schema;
|
|
|
|
--------------
|
|
-- To_Table --
|
|
--------------
|
|
|
|
function To_Table (FK : Foreign_Key) return Table_Description'Class is
|
|
R : Tables_Ref.Ref;
|
|
begin
|
|
R.Set (FK.Get.To_Table);
|
|
return Table_Description'(R with null record);
|
|
end To_Table;
|
|
|
|
--------------------------
|
|
-- Mark_FK_As_Ambiguous --
|
|
--------------------------
|
|
|
|
procedure Mark_FK_As_Ambiguous
|
|
(Table : in out Table_Description;
|
|
Foreign : Table_Description;
|
|
Ambiguous : out Boolean)
|
|
is
|
|
use Tables_Ref;
|
|
R : Tables_Ref.Ref;
|
|
begin
|
|
Ambiguous := False;
|
|
for FK of TDR (Table.Unchecked_Get).FK loop
|
|
R.Set (FK.Get.To_Table);
|
|
if R = Tables_Ref.Ref (Foreign) then
|
|
if not FK.Get.Ambiguous then
|
|
FK.Get.Ambiguous := True;
|
|
end if;
|
|
|
|
Ambiguous := True;
|
|
return;
|
|
end if;
|
|
end loop;
|
|
end Mark_FK_As_Ambiguous;
|
|
|
|
------------
|
|
-- Append --
|
|
------------
|
|
|
|
procedure Append
|
|
(List : in out String_List; Last : in out Natural; Str : String) is
|
|
begin
|
|
Last := Last + 1;
|
|
List (Last) := new String'(Str);
|
|
end Append;
|
|
|
|
------------------
|
|
-- Format_Field --
|
|
------------------
|
|
|
|
procedure Format_Field
|
|
(DB : access Database_Connection_Record'Class;
|
|
Value : String;
|
|
Typ : Field_Mapping'Class;
|
|
Val : out GNAT.Strings.String_Access;
|
|
Param : out SQL_Parameter;
|
|
Has_Xref : Boolean)
|
|
is
|
|
V : constant String := To_Lower (Value);
|
|
B : Boolean;
|
|
begin
|
|
if Typ in Boolean_Mappings.Simple_Field_Mapping'Class then
|
|
if V = "true" or else V = "false" then
|
|
B := Boolean'Value (Value);
|
|
Val := new String'(Boolean_Image (DB.all, B));
|
|
Param := +B;
|
|
else
|
|
Val := new String'(Value);
|
|
Param := +Val;
|
|
end if;
|
|
|
|
elsif Value'Length = 0 then
|
|
if Has_Xref then
|
|
Val := new String'("''");
|
|
else
|
|
Val := new String'("");
|
|
end if;
|
|
Param := +Val;
|
|
|
|
elsif V = "null" then
|
|
Val := new String'("NULL");
|
|
if Has_Xref then
|
|
Param := +Val;
|
|
else
|
|
Param := Null_Parameter;
|
|
end if;
|
|
|
|
elsif Typ in Field_Mapping_Integer'Class then
|
|
Val := new String'(Value);
|
|
Param := +Val;
|
|
|
|
elsif Typ in Field_Mapping_Money'Class then
|
|
Param := +T_Money'Value (Value);
|
|
Val := new String'(Param.Image (DB.all));
|
|
|
|
else
|
|
if Has_Xref then
|
|
Val := new String'
|
|
(String_To_SQL (DB.all, Value, Quote => True));
|
|
else
|
|
Val := new String'(Value);
|
|
end if;
|
|
|
|
Param := +Val;
|
|
end if;
|
|
end Format_Field;
|
|
|
|
-----------------
|
|
-- Read_Schema --
|
|
-----------------
|
|
|
|
function Read_Schema
|
|
(Self : File_Schema_IO; Data : String) return DB_Schema
|
|
is
|
|
Schema : DB_Schema;
|
|
T : Natural := 0; -- Index of the table we are creating
|
|
First : Natural; -- Current index in Data
|
|
Line_Number : Natural := 0;
|
|
|
|
Fields_Per_Line : constant := 5;
|
|
-- Maximum number of fields per line (fields are separated with |)
|
|
|
|
type Line_Fields is new String_List (1 .. Fields_Per_Line);
|
|
|
|
procedure Parse_Line (Result : in out Line_Fields);
|
|
-- Split the line that starts at First into its fields.
|
|
-- On exit, First points to the beginning of the next line
|
|
|
|
procedure Parse_Table (Table_Def, Name : String; Is_View : Boolean);
|
|
-- Parse a table description
|
|
|
|
procedure Parse_Table_Inheritance
|
|
(Table_Def : String; Table : in out Table_Description);
|
|
-- Parse the description of table inheritance
|
|
|
|
procedure Parse_Table_Properties (Name : String);
|
|
-- Parse all foreign keys and indices for table Name, when they are
|
|
-- described on their own line
|
|
|
|
function Parse_Properties (Str : String) return Field_Properties;
|
|
-- Parse the third column of a field description
|
|
|
|
----------------------
|
|
-- Parse_Properties --
|
|
----------------------
|
|
|
|
function Parse_Properties (Str : String) return Field_Properties is
|
|
S : String_List_Access := Split (Str, On => ',');
|
|
Props : Field_Properties;
|
|
begin
|
|
for P in S'Range loop
|
|
declare
|
|
T : constant String := Trim (S (P).all, Both);
|
|
begin
|
|
if T = "NOT NULL" then
|
|
Props.Not_Null := True;
|
|
elsif T = "INDEX" then
|
|
Props.Indexed := True;
|
|
elsif T = "UNIQUE" then
|
|
Props.Unique := True;
|
|
elsif T = "NOINDEX" then
|
|
Props.Noindex := True;
|
|
elsif T = "PK" then
|
|
Props.PK := True;
|
|
Props.Not_Null := True;
|
|
elsif T = "NOCASE" then
|
|
Props.Case_Insensitive := True;
|
|
end if;
|
|
end;
|
|
end loop;
|
|
|
|
Free (S);
|
|
return Props;
|
|
end Parse_Properties;
|
|
|
|
----------------
|
|
-- Parse_Line --
|
|
----------------
|
|
|
|
procedure Parse_Line (Result : in out Line_Fields) is
|
|
Index : Natural := Result'First - 1;
|
|
Last, Tmp : Natural;
|
|
Current_Line_End : constant Natural :=
|
|
EOL (Data (First .. Data'Last));
|
|
begin
|
|
pragma Assert (Data (First) = '|');
|
|
Line_Number := Line_Number + 1;
|
|
|
|
Free (String_List (Result));
|
|
|
|
First := First + 1;
|
|
|
|
while First <= Current_Line_End loop
|
|
Skip_Blanks (Data, First);
|
|
-- First now points to first non-blank char
|
|
|
|
Last := EOW (Data, First);
|
|
Tmp := Last - 1;
|
|
|
|
Skip_Blanks_Backward (Data (First .. Tmp), Tmp);
|
|
|
|
Append (String_List (Result), Index, Data (First .. Tmp));
|
|
exit when Index = Fields_Per_Line;
|
|
|
|
First := Last + 1;
|
|
end loop;
|
|
|
|
First := Current_Line_End + 1;
|
|
end Parse_Line;
|
|
|
|
-----------------------------
|
|
-- Parse_Table_Inheritance --
|
|
-----------------------------
|
|
|
|
procedure Parse_Table_Inheritance
|
|
(Table_Def : String; Table : in out Table_Description)
|
|
is
|
|
First : Natural := Table_Def'First;
|
|
Last : Natural;
|
|
begin
|
|
while First <= Table_Def'Last loop
|
|
if Table_Def (First) = '(' then
|
|
Last := First + 1;
|
|
while Last <= Table_Def'Last loop
|
|
if Table_Def (Last) = ')' then
|
|
TDR (Table.Unchecked_Get).Super_Table :=
|
|
Get_Table (Schema, Table_Def (First + 1 .. Last - 1));
|
|
return;
|
|
end if;
|
|
Last := Last + 1;
|
|
end loop;
|
|
end if;
|
|
|
|
First := First + 1;
|
|
end loop;
|
|
end Parse_Table_Inheritance;
|
|
|
|
-----------------
|
|
-- Parse_Table --
|
|
-----------------
|
|
|
|
procedure Parse_Table (Table_Def, Name : String; Is_View : Boolean) is
|
|
Table : Table_Description;
|
|
Line : Line_Fields;
|
|
Attr_Id : Natural := 0;
|
|
Props : Field_Properties;
|
|
Kind : Relation_Kind;
|
|
begin
|
|
T := T + 1;
|
|
|
|
-- The code below might be creating table before we actually see
|
|
-- their schema (in the case of FK to them). Reuse the prior
|
|
-- definition if one is found.
|
|
declare
|
|
C : Tables_Maps.Cursor;
|
|
begin
|
|
C := Find (Schema.Tables, Name);
|
|
if C = Tables_Maps.No_Element then
|
|
if Is_View then
|
|
Kind := Kind_View;
|
|
else
|
|
Kind := Kind_Table;
|
|
end if;
|
|
|
|
Table.Set (Table_Description_Record'
|
|
(Name => new String'(Name),
|
|
Row => null,
|
|
Kind => Kind,
|
|
Id => T,
|
|
Description => null,
|
|
Fields => Empty_Field_List,
|
|
Indexes => String_Lists.Empty_Vector,
|
|
Uniques => String_Lists.Empty_Vector,
|
|
Is_Abstract => False,
|
|
Has_PK => False,
|
|
FK => Foreign_Keys.Empty_Vector,
|
|
Active => True,
|
|
Super_Table => No_Table));
|
|
Include (Schema.Tables, Name, Table);
|
|
else
|
|
Table := Element (C);
|
|
end if;
|
|
|
|
TDR (Table.Unchecked_Get).Is_Abstract :=
|
|
Starts_With (Table_Def, "ABSTRACT");
|
|
end;
|
|
|
|
Parse_Table_Inheritance (Table_Def, Table);
|
|
|
|
while First <= Data'Last and then Data (First) = '|' loop
|
|
Parse_Line (Result => Line);
|
|
|
|
if Starts_With (Line (1).all, "--") then
|
|
-- A comment line, skip this line
|
|
null;
|
|
|
|
elsif Line (1).all = "FK:"
|
|
or else Line (1).all = "INDEX:"
|
|
or else Line (1).all = "UNIQUE:"
|
|
then
|
|
null; -- Skip for now, will do in second pass
|
|
|
|
else
|
|
if Line (2) = null
|
|
or else Line (3) = null
|
|
or else Line (4) = null
|
|
or else Line (5) = null
|
|
then
|
|
Put_Line ("Error: missing fields on line "
|
|
& Join (" | ", String_List (Line)));
|
|
return;
|
|
end if;
|
|
|
|
Attr_Id := Attr_Id + 1;
|
|
|
|
Props := Parse_Properties (Line (3).all);
|
|
|
|
declare
|
|
Typ : String renames Line (2).all;
|
|
Tmp, Tmp2 : Natural;
|
|
|
|
Att : Field;
|
|
FKD : Foreign_Key_Description;
|
|
FK : Foreign_Key;
|
|
To_Table : Table_Description;
|
|
|
|
begin
|
|
TDR (Table.Unchecked_Get).Has_PK :=
|
|
Props.PK or else TDR (Table.Unchecked_Get).Has_PK;
|
|
|
|
Att.Set (Field_Description'
|
|
(Name => new String'(Line (1).all),
|
|
Typ => null,
|
|
Id => Attr_Id,
|
|
Description => new String'(Line (5).all),
|
|
Default => new String'(Line (4).all),
|
|
Props => Props,
|
|
FK => Typ'Length > 3
|
|
and then Typ (Typ'First .. Typ'First + 2) = "FK ",
|
|
Table => Table.Weak,
|
|
Active => True));
|
|
Append (TDR (Table.Unchecked_Get).Fields, Att);
|
|
|
|
if Att.Get.FK then
|
|
Tmp := Find_Char (Typ (Typ'First + 3 .. Typ'Last), '(');
|
|
|
|
if Tmp < Typ'Last then
|
|
Tmp2 := Find_Char (Typ (Tmp + 1 .. Typ'Last), ')');
|
|
else
|
|
Tmp2 := Typ'Last;
|
|
end if;
|
|
|
|
declare
|
|
To : constant String :=
|
|
Trim (Typ (Typ'First + 3 .. Tmp - 1), Both);
|
|
begin
|
|
if To = Name then
|
|
To_Table := Table;
|
|
else
|
|
To_Table := Get_Table (Schema, To);
|
|
end if;
|
|
|
|
exception
|
|
when Invalid_Table =>
|
|
-- The table might be declared later on
|
|
To_Table.Set (Table_Description_Record'
|
|
(Name => new String'(To),
|
|
others => <>));
|
|
Include (Schema.Tables, To, To_Table);
|
|
end;
|
|
|
|
FKD := Foreign_Key_Description'
|
|
(To_Table => To_Table.Weak,
|
|
Revert_Name => new String'(Typ (Tmp + 1 .. Tmp2 - 1)),
|
|
Fields => Pair_Lists.Empty_Vector,
|
|
Ambiguous => False);
|
|
Mark_FK_As_Ambiguous (Table, To_Table, FKD.Ambiguous);
|
|
|
|
Append (FKD.Fields,
|
|
Field_Pair'
|
|
(From => Att,
|
|
To => No_Field)); -- To primary key
|
|
|
|
FK.Set (FKD);
|
|
Append (TDR (Table.Unchecked_Get).FK, FK);
|
|
|
|
Att.Get.FK := True;
|
|
|
|
else
|
|
Att.Get.Typ := From_SQL (Typ);
|
|
if Att.Get.Typ = null then
|
|
Put_Line ("Error: unknown field type """ & Typ & '"');
|
|
raise Invalid_Type;
|
|
end if;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Check that the table has a valid Primary Key
|
|
-- ??? Code is commented out for reference in case we decide to
|
|
-- output such a warning after all. For now, since there is no way
|
|
-- to hide the warning for the user, this is too verbose.
|
|
|
|
-- if not TDR (Table.Get).Has_PK
|
|
-- and then not Table.Is_Abstract
|
|
-- and then (Table.Super_Table = No_Table
|
|
-- or else not TDR (Table.Super_Table.Get).Has_PK)
|
|
-- then
|
|
-- Put_Line ("Warning: table '"
|
|
-- & Table.Name & "' has no primary key");
|
|
-- Put_Line (" No Delete operation generated for this table");
|
|
-- end if;
|
|
|
|
Free (String_List (Line));
|
|
Schema.Ordered_Tables.Append (Name);
|
|
end Parse_Table;
|
|
|
|
----------------------------
|
|
-- Parse_Table_Properties --
|
|
----------------------------
|
|
|
|
procedure Parse_Table_Properties (Name : String) is
|
|
Curs : constant Tables_Maps.Cursor := Schema.Tables.Find (Name);
|
|
From_Table : Table_Description := Element (Curs);
|
|
To_Table : Table_Description;
|
|
FK : Foreign_Key;
|
|
Line : Line_Fields;
|
|
Index_Count : Natural := 1;
|
|
begin
|
|
while First <= Data'Last and then Data (First) = '|' loop
|
|
Parse_Line (Result => Line);
|
|
|
|
if Line (1).all = "FK:" then
|
|
To_Table := Get_Table (Schema, Line (2).all);
|
|
FK.Set (Foreign_Key_Description'
|
|
(To_Table => To_Table.Weak,
|
|
Revert_Name => null,
|
|
Ambiguous => False,
|
|
Fields => Pair_Lists.Empty_Vector));
|
|
Mark_FK_As_Ambiguous (From_Table, To_Table, FK.Get.Ambiguous);
|
|
|
|
declare
|
|
From : String renames Line (3).all;
|
|
To : String renames Line (4).all;
|
|
First, First2, Tmp, Tmp2 : Natural;
|
|
begin
|
|
First := From'First;
|
|
First2 := To'First;
|
|
|
|
while First <= From'Last loop
|
|
Skip_Blanks (From, First);
|
|
Skip_Blanks (To, First2);
|
|
|
|
Tmp := Find_Char (From (First + 1 .. From'Last), ' ');
|
|
Tmp2 := Find_Char (To (First2 + 1 .. To'Last), ' ');
|
|
|
|
Append (FK.Get.Fields,
|
|
(From => From_Table.Field_From_Name
|
|
(From (First .. Tmp - 1)),
|
|
To => To_Table.Field_From_Name
|
|
(To (First2 .. Tmp2 - 1))));
|
|
First := Tmp + 1;
|
|
First2 := Tmp2 + 1;
|
|
end loop;
|
|
end;
|
|
|
|
TDR (From_Table.Unchecked_Get).FK.Append (FK);
|
|
|
|
elsif Line (1).all = "INDEX:" then
|
|
if Line (3).all = "" then
|
|
TDR (From_Table.Unchecked_Get).Indexes.Append
|
|
(String'(Line (2).all
|
|
& "|"
|
|
& Name & "_idx"
|
|
& Image (Index_Count, Min_Width => 1)));
|
|
else
|
|
TDR (From_Table.Unchecked_Get).Indexes.Append
|
|
(String'(Line (2).all & "|" & Line (3).all));
|
|
end if;
|
|
|
|
Index_Count := Index_Count + 1;
|
|
|
|
elsif Line (1).all = "UNIQUE:" then
|
|
TDR (From_Table.Unchecked_Get).Uniques.Append
|
|
(String'(Line (2).all & "|" & Line (3).all));
|
|
end if;
|
|
end loop;
|
|
|
|
Free (String_List (Line));
|
|
Replace_Element (Schema.Tables, Curs, From_Table);
|
|
end Parse_Table_Properties;
|
|
|
|
Line : Line_Fields;
|
|
type Parse_Mode is (Parsing_Table, Parsing_Properties);
|
|
|
|
begin
|
|
for Mode in Parse_Mode loop
|
|
First := Data'First;
|
|
Line_Number := 0;
|
|
|
|
while First <= Data'Last loop
|
|
if Data (First) = '|' then
|
|
Parse_Line (Result => Line);
|
|
|
|
if Starts_With (Line (1).all, "ABSTRACT TABLE")
|
|
or else Starts_With (Line (1).all, "TABLE")
|
|
or else Starts_With (Line (1).all, "VIEW")
|
|
then
|
|
case Mode is
|
|
when Parsing_Table =>
|
|
Parse_Table
|
|
(Line (1).all,
|
|
Line (2).all,
|
|
Is_View => Starts_With (Line (1).all, "VIEW"));
|
|
when Parsing_Properties =>
|
|
Parse_Table_Properties (Line (2).all);
|
|
end case;
|
|
end if;
|
|
else
|
|
First := EOL (Data (First .. Data'Last)) + 1;
|
|
Line_Number := Line_Number + 1;
|
|
end if;
|
|
end loop;
|
|
end loop;
|
|
|
|
-- Check that all foreign keys reference valid tables
|
|
|
|
declare
|
|
Has_Errors : Boolean := False;
|
|
|
|
procedure Cb (From, To : Field; Id : Natural; Ambiguous : Boolean);
|
|
procedure Cb (From, To : Field; Id : Natural; Ambiguous : Boolean) is
|
|
pragma Unreferenced (Id, Ambiguous);
|
|
begin
|
|
if To = No_Field then
|
|
Put_Line ("Invalid foreign key: "
|
|
& From.Get_Table.Name & "." & From.Name
|
|
& " references an invalid table or field");
|
|
Has_Errors := True;
|
|
end if;
|
|
end Cb;
|
|
|
|
procedure On_Table (Descr : in out Table_Description);
|
|
procedure On_Table (Descr : in out Table_Description) is
|
|
begin
|
|
For_Each_FK (Descr, Cb'Access);
|
|
end On_Table;
|
|
|
|
begin
|
|
For_Each_Table (Schema, On_Table'Access);
|
|
|
|
Free (String_List (Line));
|
|
|
|
if Has_Errors then
|
|
return No_Schema;
|
|
else
|
|
return Schema;
|
|
end if;
|
|
end;
|
|
|
|
exception
|
|
when E : Invalid_Type =>
|
|
Free (String_List (Line));
|
|
Put_Line (Standard_Error,
|
|
Self.File.Display_Full_Name
|
|
& ":" & Image (Line_Number, Min_Width => 1) & " "
|
|
& Exception_Message (E));
|
|
raise;
|
|
|
|
when Name_Error =>
|
|
Put_Line ("Could not open " & Self.File.Display_Full_Name);
|
|
return No_Schema;
|
|
end Read_Schema;
|
|
|
|
-----------------
|
|
-- Read_Schema --
|
|
-----------------
|
|
|
|
overriding function Read_Schema
|
|
(Self : File_Schema_IO) return DB_Schema
|
|
is
|
|
Str : GNAT.Strings.String_Access := Self.File.Read_File;
|
|
Schema : DB_Schema;
|
|
begin
|
|
if Str = null then
|
|
Put_Line ("File not found: " & Self.File.Display_Full_Name);
|
|
return No_Schema;
|
|
end if;
|
|
Schema := Read_Schema (Self, Str.all);
|
|
Free (Str);
|
|
return Schema;
|
|
|
|
exception
|
|
when others =>
|
|
Free (Str);
|
|
raise;
|
|
end Read_Schema;
|
|
|
|
------------------
|
|
-- Write_Schema --
|
|
------------------
|
|
|
|
overriding procedure Write_Schema
|
|
(Self : DB_Schema_IO; Schema : DB_Schema)
|
|
is
|
|
Created : String_Lists.Vector;
|
|
-- List of tables that have been created. When a table has already been
|
|
-- created, we set the foreign key constraints to it immediately,
|
|
-- otherwise we defer them till all tables have been created.
|
|
|
|
package XString_Sets is new Ada.Containers.Hashed_Sets
|
|
(XString, Hash, "=");
|
|
Dummy : XString_Sets.Cursor;
|
|
|
|
Namespaces : XString_Sets.Set;
|
|
|
|
Deferred : String_Lists.Vector;
|
|
Deferred_Indexes : String_Lists.Vector;
|
|
-- Statements to execute to create the indexes
|
|
|
|
type Namespaced_Name is record
|
|
Namespace : XString;
|
|
Full_Name : XString;
|
|
end record;
|
|
|
|
procedure For_Table (Table : in out Table_Description);
|
|
-- Process a table
|
|
|
|
function Quoted (Item : String) return Namespaced_Name;
|
|
-- Returns quoted namespace and quoted full table name
|
|
|
|
procedure Do_Statement (SQL : String);
|
|
-- Execute or output the statement, depending on user's choice
|
|
|
|
------------------
|
|
-- Do_Statement --
|
|
------------------
|
|
|
|
procedure Do_Statement (SQL : String) is
|
|
begin
|
|
if SQL /= "" then
|
|
if Self.DB = null then
|
|
Put_Line (SQL & ";");
|
|
else
|
|
Execute (Self.DB, SQL);
|
|
end if;
|
|
end if;
|
|
end Do_Statement;
|
|
|
|
------------
|
|
-- Quoted --
|
|
------------
|
|
|
|
function Quoted (Item : String) return Namespaced_Name is
|
|
Dot_Idx : Natural;
|
|
Result : Namespaced_Name;
|
|
begin
|
|
if Item (Item'First) = '"' then
|
|
if Item (Item'Last) = '"' then
|
|
Dot_Idx := Fixed.Index (Item, """.""");
|
|
|
|
Result.Full_Name := To_XString (Item);
|
|
Result.Namespace :=
|
|
(if Dot_Idx = 0 then Null_XString
|
|
else To_XString (Item (Item'First .. Dot_Idx)));
|
|
|
|
else
|
|
Dot_Idx := Fixed.Index (Item, """.");
|
|
|
|
if Dot_Idx = 0 then
|
|
raise Constraint_Error with
|
|
"Unsupported table name format " & Item;
|
|
else
|
|
Result.Namespace :=
|
|
To_XString (Item (Item'First .. Dot_Idx));
|
|
Result.Full_Name := Result.Namespace;
|
|
Result.Full_Name.Append
|
|
(".""" & Item (Dot_Idx + 2 .. Item'Last) & '"');
|
|
end if;
|
|
end if;
|
|
|
|
elsif Item (Item'Last) = '"' then
|
|
Dot_Idx := Fixed.Index (Item, ".""");
|
|
|
|
if Dot_Idx = 0 then
|
|
raise Constraint_Error with
|
|
"Unsupported table name format " & Item;
|
|
else
|
|
Result.Namespace :=
|
|
To_XString ('"' & Item (Item'First .. Dot_Idx - 1) & '"');
|
|
Result.Full_Name := Result.Namespace;
|
|
Result.Full_Name.Append ('.' & Item (Dot_Idx + 1 .. Item'Last));
|
|
end if;
|
|
|
|
else
|
|
Dot_Idx := Fixed.Index (Item, ".");
|
|
if Dot_Idx = 0 then
|
|
Result.Namespace := Null_XString;
|
|
Result.Full_Name := To_XString ('"' & Item & '"');
|
|
else
|
|
Result.Namespace :=
|
|
To_XString ('"' & Item (Item'First .. Dot_Idx - 1) & '"');
|
|
Result.Full_Name := Result.Namespace;
|
|
Result.Full_Name.Append
|
|
(".""" & Item (Dot_Idx + 1 .. Item'Last) & '"');
|
|
end if;
|
|
end if;
|
|
|
|
return Result;
|
|
end Quoted;
|
|
|
|
---------------
|
|
-- For_Table --
|
|
---------------
|
|
|
|
procedure For_Table (Table : in out Table_Description) is
|
|
SQL : Unbounded_String;
|
|
-- The statement to execute
|
|
|
|
TNS : constant Namespaced_Name := Quoted (Table.Name);
|
|
Table_Name : constant String := To_String (TNS.Full_Name);
|
|
New_NS : Boolean;
|
|
|
|
SQL_PK : Unbounded_String;
|
|
-- The SQL to create the primary key
|
|
|
|
Is_First_Attribute : Boolean := True;
|
|
|
|
procedure Print_PK (F : in out Field);
|
|
procedure Add_Field_To_SQL (F : in out Field);
|
|
|
|
procedure Print_Uniques;
|
|
|
|
procedure Get_Field_Def
|
|
(F : Field;
|
|
Stmt : out Unbounded_String;
|
|
Can_Be_Not_Null : Boolean := True;
|
|
FK_Table : String := "");
|
|
-- Set Stmt to the definition for the field F.
|
|
-- If Can_Be_Not_Null is False, the field will never have NOT NULL.
|
|
-- This is needed in sqlite3 when adding FK columns later on. When
|
|
-- Can_Be_Not_Null is set to False, you must set FK_Table to the
|
|
-- table pointed to by the field.
|
|
|
|
procedure Print_Indexes (Table : Table_Description);
|
|
-- Create the multi-column indexes for the table
|
|
|
|
procedure Print_FK (Table : Table_Description);
|
|
-- Process the foreign key and indexes constraints. They are either
|
|
-- added to the table creation statement, or deferred until all
|
|
-- tables have been created.
|
|
|
|
-------------------
|
|
-- Print_Indexes --
|
|
-------------------
|
|
|
|
procedure Print_Indexes (Table : Table_Description) is
|
|
Name_Start : Positive;
|
|
begin
|
|
for Descr of TDR (Table.Unchecked_Get).Indexes loop
|
|
Name_Start := Index (Descr, "|", Descr'First + 1);
|
|
|
|
Deferred_Indexes.Append
|
|
(String'
|
|
("CREATE INDEX """
|
|
& Descr (Name_Start + 1 .. Descr'Last)
|
|
& """ ON "
|
|
& Table_Name & " ("
|
|
& Descr (Descr'First .. Name_Start - 1)
|
|
& ")"));
|
|
end loop;
|
|
end Print_Indexes;
|
|
|
|
-------------------
|
|
-- Print_Uniques --
|
|
-------------------
|
|
|
|
procedure Print_Uniques is
|
|
Name_Start : Positive;
|
|
begin
|
|
for Descr of TDR (Table.Unchecked_Get).Uniques loop
|
|
Append (SQL, ',' & ASCII.LF);
|
|
|
|
Name_Start := Index (Descr, "|", Descr'First + 1);
|
|
|
|
if Name_Start < Descr'Last then
|
|
Append
|
|
(SQL,
|
|
"CONSTRAINT """ & Descr (Name_Start + 1 .. Descr'Last)
|
|
& """ ");
|
|
end if;
|
|
|
|
Append
|
|
(SQL,
|
|
"UNIQUE (" & Descr (Descr'First .. Name_Start - 1) & ')');
|
|
end loop;
|
|
end Print_Uniques;
|
|
|
|
--------------
|
|
-- Print_FK --
|
|
--------------
|
|
|
|
procedure Print_FK (Table : Table_Description) is
|
|
Stmt2 : Unbounded_String;
|
|
-- The deferred statement to execute
|
|
|
|
Stmt_FK, Stmt_References : Unbounded_String;
|
|
P : Pair_Lists.Cursor;
|
|
Is_First : Boolean;
|
|
Table_To : XString;
|
|
begin
|
|
for R of TDR (Table.Unchecked_Get).FK loop
|
|
-- Prepare the constraint
|
|
|
|
Table_To := Quoted (R.To_Table.Name).Full_Name;
|
|
|
|
Stmt_FK := To_Unbounded_String (" FOREIGN KEY (");
|
|
Is_First := True;
|
|
P := R.Get.Fields.First;
|
|
while Has_Element (P) loop
|
|
if not Is_First then
|
|
Append (Stmt_FK, ",");
|
|
end if;
|
|
Is_First := False;
|
|
Append (Stmt_FK, '"' & Element (P).From.Name & '"');
|
|
Next (P);
|
|
end loop;
|
|
Append (Stmt_FK, ")");
|
|
|
|
Stmt_References := To_Unbounded_String
|
|
(" REFERENCES " & To_String (Table_To) & " (");
|
|
Is_First := True;
|
|
P := R.Get.Fields.First;
|
|
while Has_Element (P) loop
|
|
if not Is_First then
|
|
Append (Stmt_References, ",");
|
|
end if;
|
|
Is_First := False;
|
|
|
|
if Element (P).To = No_Field then
|
|
Append
|
|
(Stmt_References, '"' & R.To_Table.Get_PK.Name & '"');
|
|
else
|
|
Append
|
|
(Stmt_References, '"' & Element (P).To.Name & '"');
|
|
end if;
|
|
Next (P);
|
|
end loop;
|
|
Append (Stmt_References, ")");
|
|
|
|
Append (Stmt_FK, Stmt_References);
|
|
|
|
-- If the other table has already been created, we can add the
|
|
-- new constraint directly in the table creation which is more
|
|
-- efficient (a single SQL statement).
|
|
|
|
if Created.Contains (To_String (Table_To)) then
|
|
Append (SQL, "," & ASCII.LF & Stmt_FK);
|
|
|
|
elsif Self.DB.Can_Alter_Table_Constraints then
|
|
Append
|
|
(Deferred,
|
|
To_String
|
|
("ALTER TABLE " & Table_Name & " ADD CONSTRAINT "
|
|
& Element (R.Get.Fields.First).From.Name
|
|
& "_fk" & Stmt_FK));
|
|
|
|
else
|
|
P := R.Get.Fields.First;
|
|
while Has_Element (P) loop
|
|
-- Sqlite only allows adding a NON NULL REFERENCES column
|
|
-- if it has a non-null default. So we need to provide a
|
|
-- random default in such a case.
|
|
|
|
Get_Field_Def (Element (P).From, Stmt2,
|
|
Can_Be_Not_Null => False,
|
|
FK_Table => To_String (Table_To));
|
|
Stmt2 := "ALTER TABLE " & Table_Name & " ADD COLUMN "
|
|
& Stmt2 & Stmt_References;
|
|
Append (Deferred, To_String (Stmt2));
|
|
Next (P);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Create indexes for the reverse relationships, since it is
|
|
-- likely the user will want to use them a lot anyway
|
|
|
|
if Length (R.Get.Fields) = 1
|
|
|
|
-- Unless already created explicitly
|
|
and not Element (R.Get.Fields.First).From.Get.Props.Indexed
|
|
|
|
-- Unless disabled by the user
|
|
and not Element (R.Get.Fields.First).From.Get.Props.Noindex
|
|
then
|
|
Deferred_Indexes.Append
|
|
(String'
|
|
("CREATE INDEX """
|
|
& Table.Name & "_"
|
|
& Element (R.Get.Fields.First).From.Name
|
|
& "_idx"" ON "
|
|
& Table_Name & " ("""
|
|
& Element (R.Get.Fields.First).From.Name
|
|
& """)"));
|
|
end if;
|
|
end loop;
|
|
end Print_FK;
|
|
|
|
-------------------
|
|
-- Get_Field_Def --
|
|
-------------------
|
|
|
|
procedure Get_Field_Def
|
|
(F : Field;
|
|
Stmt : out Unbounded_String;
|
|
Can_Be_Not_Null : Boolean := True;
|
|
FK_Table : String := "")
|
|
is
|
|
Val : GNAT.Strings.String_Access;
|
|
Val_Param : SQL_Parameter;
|
|
begin
|
|
Stmt := Null_Unbounded_String;
|
|
|
|
Append (Stmt, " """ & F.Name & """ "
|
|
& Get_Type (F).Type_To_SQL
|
|
(Self.DB, For_Database => True));
|
|
|
|
if not F.Can_Be_Null then
|
|
if not Can_Be_Not_Null then
|
|
Put_Line (Standard_Error,
|
|
"Warning: '" & F.Get_Table.Name
|
|
& "." & F.Name
|
|
& "' cannot be NOT NULL in sqlite, because it"
|
|
& " references '" & FK_Table
|
|
& "' which hasn't been defined yet." & ASCII.LF
|
|
& " Try reordering the table definitions");
|
|
else
|
|
Append (Stmt, " NOT NULL");
|
|
end if;
|
|
end if;
|
|
|
|
if F.Get.Props.Unique then
|
|
Append (Stmt, " UNIQUE");
|
|
end if;
|
|
|
|
if F.Get.Props.Case_Insensitive then
|
|
Append (Stmt, " COLLATE NOCASE");
|
|
end if;
|
|
|
|
if F.Default /= "" then
|
|
Format_Field
|
|
(Self.DB, F.Default, Get_Type (F).all, Val, Val_Param, False);
|
|
Append (Stmt, " DEFAULT " & Val.all);
|
|
Free (Val);
|
|
|
|
if not Can_Be_Not_Null then
|
|
-- When adding FK fields to an existing table, sqlite only
|
|
-- allows a NULL default value.
|
|
|
|
Put_Line
|
|
(Standard_Error,
|
|
"Error: '" & F.Get_Table.Name & "." & F.Name
|
|
& "' is a reference to table '"
|
|
& F.Is_FK.Get_Table.Name
|
|
& "' which isn't defined yet. Sqlite imposes a NULL"
|
|
& " default in this case.");
|
|
raise Invalid_Schema;
|
|
end if;
|
|
end if;
|
|
|
|
if F.Get.Props.Indexed then
|
|
Deferred_Indexes.Append
|
|
(String'
|
|
("CREATE INDEX """
|
|
& Table.Name & "_"
|
|
& F.Get.Name.all
|
|
& "_idx"" ON "
|
|
& Table_Name & " ("""
|
|
& F.Get.Name.all
|
|
& """)"));
|
|
end if;
|
|
end Get_Field_Def;
|
|
|
|
----------------------
|
|
-- Add_Field_To_SQL --
|
|
----------------------
|
|
|
|
procedure Add_Field_To_SQL (F : in out Field) is
|
|
Tmp : Unbounded_String;
|
|
begin
|
|
-- When a field is a FK to a table that hasn't been created yet,
|
|
-- we need to alter the table later to set the constraint. But in
|
|
-- some cases (sqlite3), this isn't possible, so we will create
|
|
-- the field later altogether.
|
|
|
|
if not Self.DB.Can_Alter_Table_Constraints
|
|
and then F.Is_FK /= No_Field
|
|
and then not Created.Contains
|
|
(To_String (Quoted (F.Is_FK.Get_Table.Name).Full_Name))
|
|
then
|
|
if F.Is_PK then
|
|
Put_Line (Standard_Error,
|
|
"Error: '" & F.Get_Table.Name & "." & F.Name
|
|
& "' is a primary key and references the table '"
|
|
& F.Is_FK.Get_Table.Name & "' which hasn't been"
|
|
& " defined yet.");
|
|
raise Invalid_Schema;
|
|
end if;
|
|
return;
|
|
end if;
|
|
|
|
if not Is_First_Attribute then
|
|
Append (SQL, "," & ASCII.LF);
|
|
end if;
|
|
|
|
Is_First_Attribute := False;
|
|
|
|
Get_Field_Def (F, Tmp);
|
|
Append (SQL, Tmp);
|
|
end Add_Field_To_SQL;
|
|
|
|
--------------
|
|
-- Print_PK --
|
|
--------------
|
|
|
|
procedure Print_PK (F : in out Field) is
|
|
begin
|
|
-- Auto increment fields were already setup as primary keys
|
|
-- via Field_Mapping_Autoincrement primitive operation.
|
|
if F.Is_PK
|
|
and then F.Get_Type.all not in Field_Mapping_Autoincrement'Class
|
|
then
|
|
if SQL_PK = Null_Unbounded_String then
|
|
Append (SQL_PK, '"' & F.Name & '"');
|
|
else
|
|
Append (SQL_PK, ",""" & F.Name & '"');
|
|
end if;
|
|
end if;
|
|
end Print_PK;
|
|
|
|
begin -- For_Table
|
|
if Self.DB.Success and then not Table.Is_Abstract then
|
|
if TNS.Namespace /= Null_XString then
|
|
Namespaces.Insert (TNS.Namespace, Dummy, New_NS);
|
|
|
|
if New_NS then
|
|
Append
|
|
(SQL,
|
|
"CREATE SCHEMA IF NOT EXISTS "
|
|
& To_String (TNS.Namespace) & ';' & ASCII.LF);
|
|
end if;
|
|
end if;
|
|
|
|
case Table.Get_Kind is
|
|
when Kind_Table =>
|
|
Created.Append (Table_Name); -- mark the table as created
|
|
|
|
Append (SQL, "CREATE TABLE " & Table_Name & " (" & ASCII.LF);
|
|
For_Each_Field
|
|
(Table, Add_Field_To_SQL'Access,
|
|
Include_Inherited => True);
|
|
|
|
SQL_PK := Null_Unbounded_String;
|
|
For_Each_Field (Table, Print_PK'Access, True);
|
|
if SQL_PK /= "" then
|
|
Append (SQL, ", PRIMARY KEY (" & SQL_PK & ")");
|
|
end if;
|
|
|
|
Print_Uniques;
|
|
|
|
Print_FK (Table);
|
|
Print_Indexes (Table);
|
|
Append (SQL, ")");
|
|
Do_Statement (To_String (SQL));
|
|
|
|
when Kind_View =>
|
|
null;
|
|
end case;
|
|
end if;
|
|
end For_Table;
|
|
|
|
S : String_Lists.Cursor;
|
|
|
|
begin
|
|
For_Each_Table (Schema, For_Table'Access, Alphabetical => False);
|
|
|
|
if Self.DB.Success then
|
|
S := First (Deferred);
|
|
while Has_Element (S) loop
|
|
Do_Statement (Element (S));
|
|
Next (S);
|
|
end loop;
|
|
|
|
S := First (Deferred_Indexes);
|
|
while Has_Element (S) loop
|
|
Do_Statement (Element (S));
|
|
Next (S);
|
|
end loop;
|
|
end if;
|
|
|
|
if Self.DB /= null then
|
|
if Self.DB.Automatic_Transactions then
|
|
Commit_Or_Rollback (Self.DB);
|
|
end if;
|
|
|
|
if not Self.DB.Success then
|
|
Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
|
|
end if;
|
|
end if;
|
|
|
|
exception
|
|
when Invalid_Schema =>
|
|
Self.DB.Set_Failure;
|
|
if Self.DB.Automatic_Transactions then
|
|
Rollback (Self.DB);
|
|
end if;
|
|
Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
|
|
end Write_Schema;
|
|
|
|
------------------
|
|
-- Write_Schema --
|
|
------------------
|
|
|
|
overriding procedure Write_Schema
|
|
(Self : File_Schema_IO; Schema : DB_Schema)
|
|
is
|
|
begin
|
|
Write_Schema (Self, Schema, Ada.Text_IO.Put'Access);
|
|
end Write_Schema;
|
|
|
|
------------------
|
|
-- Write_Schema --
|
|
------------------
|
|
|
|
procedure Write_Schema
|
|
(Self : File_Schema_IO;
|
|
Schema : DB_Schema;
|
|
Puts : access procedure (S : String);
|
|
Align_Columns : Boolean := True;
|
|
Show_Comments : Boolean := True)
|
|
is
|
|
To_File : File_Type;
|
|
Put : access procedure (S : String) := Puts;
|
|
|
|
Not_Null : constant String := "NOT NULL";
|
|
Column_Widths : array (1 .. 4) of Natural;
|
|
-- The maximum width of all columns
|
|
|
|
function SQL_Type (Attr : Field) return String;
|
|
-- Return the type to use for Attr. This includes foreign keys when
|
|
-- appropriate
|
|
|
|
procedure For_Table (Table : in out Table_Description);
|
|
-- Process a table
|
|
|
|
procedure For_Field (F : in out Field);
|
|
-- Process a field
|
|
|
|
function Omit_Schema (Name : String) return String;
|
|
-- Remove schema prefix if it is defined in -omit-schema parameter
|
|
|
|
-----------------
|
|
-- Omit_Schema --
|
|
-----------------
|
|
|
|
function Omit_Schema (Name : String) return String is
|
|
Dot : constant Natural := Index (Name, ".");
|
|
begin
|
|
if Dot = 0
|
|
or else not Self.Omit_Schema.Contains (Name (Name'First .. Dot - 1))
|
|
then
|
|
return Name;
|
|
end if;
|
|
|
|
return Name (Dot + 1 .. Name'Last);
|
|
end Omit_Schema;
|
|
|
|
--------------
|
|
-- SQL_Type --
|
|
--------------
|
|
|
|
function SQL_Type (Attr : Field) return String is
|
|
FK : constant Field := Attr.Is_FK;
|
|
begin
|
|
if FK = No_Field then
|
|
if Attr.Get_Type.all in Field_Mapping_Autoincrement'Class then
|
|
return "AUTOINCREMENT";
|
|
else
|
|
return Attr.Get_Type.Type_To_SQL
|
|
(Self.DB, For_Database => True);
|
|
end if;
|
|
else
|
|
return "FK " & Omit_Schema (FK.Get_Table.Name);
|
|
end if;
|
|
end SQL_Type;
|
|
|
|
---------------
|
|
-- For_Field --
|
|
---------------
|
|
|
|
procedure For_Field (F : in out Field) is
|
|
Name : constant String := F.Name;
|
|
Default : constant String := F.Default;
|
|
begin
|
|
Put
|
|
("|" & Name & (1 .. Column_Widths (1) - Name'Length => ' ') & "|");
|
|
|
|
declare
|
|
Typ : constant String := SQL_Type (F);
|
|
begin
|
|
Put (Typ & (1 .. Column_Widths (2) - Typ'Length => ' ') & "|");
|
|
end;
|
|
|
|
if F.Is_PK then
|
|
Put ("PK");
|
|
elsif not F.Can_Be_Null then
|
|
Put (Not_Null);
|
|
elsif Align_Columns then
|
|
Put ("NULL");
|
|
end if;
|
|
|
|
if F.Get.Props.Indexed then
|
|
Put (",INDEX");
|
|
elsif F.Get.Props.Noindex then
|
|
Put (",NOINDEX");
|
|
end if;
|
|
|
|
if F.Get.Props.Unique then
|
|
Put (",UNIQUE");
|
|
end if;
|
|
|
|
if F.Get.Props.Case_Insensitive then
|
|
Put (",NOCASE");
|
|
end if;
|
|
|
|
Put
|
|
("|" & Default & (1 .. Column_Widths (4) - Default'Length => ' ')
|
|
& "|");
|
|
|
|
if Show_Comments then
|
|
Put
|
|
(Translate (F.Description,
|
|
Mapping => To_Mapping ("" & ASCII.LF, " ")) & "|" & ASCII.LF);
|
|
else
|
|
Put ("" & ASCII.LF);
|
|
end if;
|
|
end For_Field;
|
|
|
|
---------------
|
|
-- For_Table --
|
|
---------------
|
|
|
|
procedure For_Table (Table : in out Table_Description) is
|
|
P : Pair_Lists.Cursor;
|
|
|
|
procedure Write_Index (Prefix : String; List : String_Lists.Vector);
|
|
|
|
-----------------
|
|
-- Write_Index --
|
|
-----------------
|
|
|
|
procedure Write_Index (Prefix : String; List : String_Lists.Vector) is
|
|
Name_Start : Positive;
|
|
begin
|
|
for Descr of List loop
|
|
Name_Start := Index (Descr, "|", Descr'First + 1);
|
|
|
|
Put ('|' & Prefix & ":|"
|
|
& Descr (Descr'First .. Name_Start - 1)
|
|
& "|" & Descr (Name_Start + 1 .. Descr'Last)
|
|
& ASCII.LF);
|
|
end loop;
|
|
end Write_Index;
|
|
|
|
Table_Name : constant String := Omit_Schema (Table.Name);
|
|
|
|
begin
|
|
-- Compute widths
|
|
-- Minimum size of column 1 is 5 (for "TABLE")
|
|
if Align_Columns then
|
|
Column_Widths := (1 => 5, 2 => 0, 3 => Not_Null'Length, 4 => 0);
|
|
for A of TDR (Table.Unchecked_Get).Fields loop
|
|
Column_Widths (1) := Integer'Max
|
|
(Column_Widths (1), A.Name'Length);
|
|
Column_Widths (2) := Integer'Max
|
|
(Column_Widths (2), SQL_Type (A)'Length);
|
|
Column_Widths (4) := Integer'Max
|
|
(Column_Widths (4), A.Default'Length);
|
|
end loop;
|
|
|
|
else
|
|
Column_Widths := (others => 0);
|
|
end if;
|
|
|
|
case Table.Get_Kind is
|
|
when Kind_Table =>
|
|
Put
|
|
("|TABLE" & (1 .. Column_Widths (1) - 5 => ' ')
|
|
& "| " & Table_Name & ASCII.LF);
|
|
when Kind_View =>
|
|
Put
|
|
("|VIEW" & (1 .. Column_Widths (1) - 4 => ' ')
|
|
& "| " & Table_Name & ASCII.LF);
|
|
end case;
|
|
|
|
For_Each_Field (Table, For_Field'Access, True);
|
|
|
|
for FK of TDR (Table.Unchecked_Get).FK loop
|
|
if Length (FK.Get.Fields) > 1 then
|
|
Put ("| FK: | " & Omit_Schema (FK.To_Table.Name) & " | ");
|
|
|
|
P := FK.Get.Fields.First;
|
|
while Has_Element (P) loop
|
|
Put (Element (P).From.Name & " ");
|
|
Next (P);
|
|
end loop;
|
|
|
|
Put (" | ");
|
|
|
|
P := FK.Get.Fields.First;
|
|
while Has_Element (P) loop
|
|
Put (Element (P).To.Name & " ");
|
|
Next (P);
|
|
end loop;
|
|
|
|
Put (" |" & ASCII.LF);
|
|
end if;
|
|
end loop;
|
|
|
|
Write_Index ("INDEX", TDR (Table.Unchecked_Get).Indexes);
|
|
Write_Index ("UNIQUE", TDR (Table.Unchecked_Get).Uniques);
|
|
|
|
Put ("" & ASCII.LF);
|
|
end For_Table;
|
|
|
|
begin
|
|
if Self.File /= No_File then
|
|
Create (To_File, Out_File, Self.File.Display_Full_Name);
|
|
Set_Output (To_File);
|
|
Put := Ada.Text_IO.Put'Access;
|
|
end if;
|
|
|
|
For_Each_Table (Schema, For_Table'Access, Alphabetical => False);
|
|
|
|
if Self.File /= No_File then
|
|
Set_Output (Standard_Output);
|
|
Close (To_File);
|
|
end if;
|
|
end Write_Schema;
|
|
|
|
---------------
|
|
-- Load_Data --
|
|
---------------
|
|
|
|
procedure Load_Data
|
|
(File : GNATCOLL.VFS.Virtual_File;
|
|
Puts : access procedure (S : String))
|
|
is
|
|
Str : GNAT.Strings.String_Access;
|
|
Line_Number : Natural := 0;
|
|
Line : String_List (1 .. Max_Fields_Per_Line);
|
|
First : Integer;
|
|
Fields_Count : Natural; -- Number of fields on current line
|
|
|
|
begin
|
|
Str := Read_Whole_File (+File.Full_Name.all);
|
|
if Str /= null then
|
|
First := Str'First;
|
|
|
|
while First <= Str'Last loop
|
|
Parse_Line
|
|
(Line, Line_Number, Fields_Count, Str.all, First,
|
|
Replace_Newline => False);
|
|
|
|
if Fields_Count > 0 then
|
|
for F in 1 .. Fields_Count loop
|
|
Puts ("|" & Line (F).all);
|
|
end loop;
|
|
Puts ("|" & ASCII.LF);
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
Free (Str);
|
|
end Load_Data;
|
|
|
|
----------------
|
|
-- Parse_Line --
|
|
----------------
|
|
|
|
procedure Parse_Line
|
|
(Line : in out String_List;
|
|
Line_Number : in out Natural;
|
|
Fields_Count : out Natural;
|
|
Data : String;
|
|
First : in out Integer;
|
|
Replace_Newline : Boolean := True)
|
|
is
|
|
Line_End : Natural := EOL (Data (First .. Data'Last));
|
|
Last, Tmp : Natural;
|
|
begin
|
|
Free (String_List (Line));
|
|
Fields_Count := Line'First - 1;
|
|
|
|
Line_Number := Line_Number + 1;
|
|
|
|
while Data (First) = '|'
|
|
and then Data (First + 1) = '-' -- Skip line like |---|----|
|
|
|
|
-- But we want to parse |-1|...
|
|
and then (Data'Length < 3 or else Data (First + 2) = '-')
|
|
loop
|
|
First := Line_End + 1;
|
|
Line_End := EOL (Data (First .. Data'Last));
|
|
Line_Number := Line_Number + 1;
|
|
end loop;
|
|
|
|
if Data (First) = '|' then
|
|
First := First + 1;
|
|
|
|
while First <= Line_End loop
|
|
Skip_Blanks (Data, First);
|
|
exit when First >= Line_End;
|
|
exit when Data (First) = '#'; -- A comment
|
|
|
|
-- First now points to first non-blank char
|
|
|
|
Last := EOW (Data, First);
|
|
exit when Last > Line_End;
|
|
|
|
Tmp := Last - 1;
|
|
Skip_Blanks_Backward (Data (First .. Tmp), Tmp);
|
|
|
|
if Replace_Newline then
|
|
declare
|
|
S : Unbounded_String :=
|
|
To_Unbounded_String (Data (First .. Tmp));
|
|
begin
|
|
Replace
|
|
(S, Pattern => "\n", Replacement => "" & ASCII.LF);
|
|
Append (Line, Fields_Count, To_String (S));
|
|
end;
|
|
|
|
else
|
|
Append (Line, Fields_Count, Data (First .. Tmp));
|
|
end if;
|
|
|
|
First := Last + 1;
|
|
end loop;
|
|
end if;
|
|
|
|
First := Line_End + 1;
|
|
end Parse_Line;
|
|
|
|
---------------
|
|
-- Load_Data --
|
|
---------------
|
|
|
|
procedure Load_Data
|
|
(DB : access Database_Connection_Record'Class;
|
|
Data : String;
|
|
Schema : DB_Schema := No_Schema;
|
|
Location : String := "data";
|
|
Replace_Newline : Boolean := True)
|
|
is
|
|
Line_Number : Natural := 0;
|
|
|
|
Line : String_List (1 .. Max_Fields_Per_Line);
|
|
First : Integer;
|
|
Fields_Count : Natural; -- Number of fields on current line
|
|
|
|
Table : Table_Description;
|
|
|
|
DB_Fields : String_List (1 .. Max_Fields_Per_Line);
|
|
DB_Fields_Count : Natural := DB_Fields'First - 1;
|
|
Xref : String_List (1 .. Max_Fields_Per_Line);
|
|
Xref_Count : Natural := Xref'First - 1;
|
|
Xref_Type : array (1 .. Max_Fields_Per_Line) of Field_Mapping_Access;
|
|
|
|
function Is_Xref (Index : Positive) return Boolean is
|
|
(Xref_Type (Index) /= null);
|
|
-- Whether a given column must be an xref
|
|
|
|
Paren : Natural;
|
|
DB_Field_Mappings : array (Line'First .. Max_Fields_Per_Line)
|
|
of Field_Mapping_Access;
|
|
|
|
-- TODO : convert for parameter_decimal
|
|
Tmp_DB_Fields_Count : Natural := DB_Fields'First - 1;
|
|
|
|
FK : Field;
|
|
Tables : String_List (1 .. Max_Fields_Per_Line);
|
|
Where : String_List (1 .. Max_Fields_Per_Line);
|
|
|
|
Select_Values : String_List (1 .. Max_Fields_Per_Line);
|
|
-- Parameters when values are queries through a SELECT
|
|
|
|
DB_Values : String_List (1 .. Max_Fields_Per_Line);
|
|
-- Parameters when all values are provided in the file
|
|
|
|
Has_Xref_Column : Boolean;
|
|
-- Whether at least one column can handle xref (values starting with &)
|
|
|
|
Q_Values : Prepared_Statement;
|
|
Q_Values_With_Select : Prepared_Statement;
|
|
|
|
procedure Parse_Line;
|
|
|
|
procedure Free_Vars;
|
|
-- Free all local variables
|
|
|
|
procedure Parse_Line is
|
|
begin
|
|
Parse_Line
|
|
(Line, Line_Number, Fields_Count, Data, First, Replace_Newline);
|
|
end Parse_Line;
|
|
|
|
---------------
|
|
-- Free_Vars --
|
|
---------------
|
|
|
|
procedure Free_Vars is
|
|
begin
|
|
Has_Xref_Column := False;
|
|
|
|
Free (DB_Fields);
|
|
DB_Fields_Count := DB_Fields'First - 1;
|
|
Tmp_DB_Fields_Count := DB_Fields'First - 1;
|
|
|
|
Free (Xref);
|
|
Xref_Count := Xref'First - 1;
|
|
|
|
Free (Tables);
|
|
Free (Where);
|
|
Free (Select_Values);
|
|
Free (DB_Values);
|
|
end Free_Vars;
|
|
|
|
begin
|
|
Trace (Me, "Loading data from " & Location & " into database");
|
|
|
|
First := Data'First;
|
|
|
|
if DB.Has_Pragmas then
|
|
Execute (DB, "PRAGMA foreign_keys=OFF");
|
|
end if;
|
|
|
|
while First <= Data'Last loop
|
|
Parse_Line;
|
|
|
|
if Fields_Count /= 0
|
|
and then (Line (1).all = "TABLE"
|
|
or else Line (1).all = "VIEW")
|
|
then
|
|
Free_Vars;
|
|
|
|
Table := Get_Table (Schema, Line (2).all);
|
|
|
|
Parse_Line; -- Parse fields
|
|
for L in Line'First .. Fields_Count loop
|
|
exit when Line (L).all = "";
|
|
|
|
Paren := Ada.Strings.Fixed.Index (Line (L).all, "(&");
|
|
|
|
if Paren >= Line (L)'First then
|
|
-- Reference to another table.column
|
|
|
|
declare
|
|
Name : constant String :=
|
|
Line (L) (Line (L)'First .. Paren - 1);
|
|
Refn : constant String :=
|
|
Line (L)
|
|
(Paren + 2 .. Index (Line (L).all, ")") - 1);
|
|
begin
|
|
Append (DB_Fields, DB_Fields_Count, Quote_Keyword (Name));
|
|
Append (Xref, Xref_Count,
|
|
Line (L) (Paren + 2 .. Line (L)'Last - 1));
|
|
FK := Table.Field_From_Name (Name).Is_FK;
|
|
Xref_Type (L) := Get_Table (Schema, FK.Get_Table.Name)
|
|
.Field_From_Name (Refn).Get_Type;
|
|
Has_Xref_Column := True;
|
|
Tables (L) := new String'
|
|
(FK.Get_Table.Name & " t" & Image (L, 0));
|
|
end;
|
|
else
|
|
Append (DB_Fields, DB_Fields_Count,
|
|
Quote_Keyword (Line (L).all));
|
|
Append (Xref, Xref_Count, "");
|
|
end if;
|
|
|
|
pragma Assert (L = DB_Fields_Count);
|
|
pragma Assert (L = Xref_Count); -- Xref_Count can be removed
|
|
end loop;
|
|
|
|
declare
|
|
procedure On_Field (F : in out Field);
|
|
procedure On_Field (F : in out Field) is
|
|
N : constant String := Quote_Keyword (F.Name);
|
|
begin
|
|
for L in DB_Fields'First .. DB_Fields_Count loop
|
|
if DB_Fields (L).all = N then
|
|
DB_Field_Mappings (L) := F.Get_Type;
|
|
return;
|
|
end if;
|
|
end loop;
|
|
|
|
-- We might not find the field, in case the data only sets
|
|
-- a subset of the fields. That doesn't matter.
|
|
end On_Field;
|
|
begin
|
|
Table.For_Each_Field
|
|
(On_Field'Access, Include_Inherited => True);
|
|
end;
|
|
|
|
-- Set Select_Values and DB_Values according to DB_Field_Mappings
|
|
for L in Line'First .. Fields_Count loop
|
|
exit when Line (L).all = "";
|
|
|
|
declare
|
|
Param : constant SQL_Parameter_Type'Class :=
|
|
DB_Field_Mappings (L).Parameter_Type;
|
|
P : constant String :=
|
|
Param.Type_String (Index => L, Format => DB.all);
|
|
begin
|
|
if Is_Xref (Tmp_DB_Fields_Count + 1) then
|
|
Select_Values (L) :=
|
|
new String'("t" & Image (L, 0) & "." & FK.Name);
|
|
Where (L) := new String'
|
|
("t" & Image (L, 0) & "." & Xref (L).all
|
|
& "=" & Xref_Type (L).Parameter_Type.Type_String
|
|
(Index => L, Format => DB.all));
|
|
else
|
|
Select_Values (L) := new String'(P);
|
|
end if;
|
|
|
|
DB_Values (L) := new String'(P);
|
|
|
|
Tmp_DB_Fields_Count := Tmp_DB_Fields_Count + 1;
|
|
end;
|
|
end loop;
|
|
|
|
Q_Values := Prepare
|
|
("INSERT INTO """ & Table.Name & """("
|
|
& Join (",", DB_Fields (DB_Fields'First .. DB_Fields_Count))
|
|
& ") VALUES ("
|
|
& Join (",", DB_Values (1 .. DB_Fields_Count)) & ")",
|
|
On_Server => True,
|
|
Name => "insertval");
|
|
|
|
if Has_Xref_Column then
|
|
Q_Values_With_Select := Prepare
|
|
("INSERT INTO """ & Table.Name & """("
|
|
& Join (",", DB_Fields (DB_Fields'First .. DB_Fields_Count))
|
|
& ") SELECT "
|
|
& Join (",", Select_Values (1 .. DB_Fields_Count))
|
|
& " FROM " & Join (",", Tables (1 .. DB_Fields_Count))
|
|
& " WHERE " & Join (" and ", Where (1 .. DB_Fields_Count)),
|
|
On_Server => True,
|
|
Name => "insertv");
|
|
end if;
|
|
|
|
elsif Fields_Count /= 0
|
|
and then Line (1).all = "QUERIES"
|
|
then
|
|
while First <= Data'Last and then Data (First) = '|' loop
|
|
Parse_Line;
|
|
Execute (DB, Line (1).all);
|
|
end loop;
|
|
|
|
elsif Fields_Count /= 0 then
|
|
declare
|
|
Values : SQL_Parameters (1 .. DB_Fields_Count);
|
|
Vals : String_List (1 .. DB_Fields_Count);
|
|
Has_Xref : Boolean := False;
|
|
Use_Custom : Boolean := False;
|
|
Custom_Tables : String_List (1 .. DB_Fields_Count);
|
|
Custom_Where : String_List (1 .. DB_Fields_Count);
|
|
begin
|
|
-- Check the xref in the columns
|
|
|
|
for L in Line'First
|
|
.. Integer'Min (Fields_Count, DB_Fields_Count)
|
|
loop
|
|
if Starts_With (Line (L).all, "&") then
|
|
Has_Xref := True;
|
|
|
|
if not Is_Xref (L) then
|
|
-- The column was not prepared as an xref
|
|
raise Invalid_File
|
|
with Location & ":" & Image (Line_Number, 0)
|
|
& ": column title must indicate referenced field";
|
|
end if;
|
|
|
|
Vals (L) := new String'
|
|
(Line (L) (Line (L)'First + 1 .. Line (L)'Last));
|
|
Values (L) := +Vals (L);
|
|
|
|
else
|
|
if Is_Xref (L) then
|
|
-- The prepared query expects an xref, but we do not
|
|
-- have. So we'll use a custom query
|
|
Use_Custom := True;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
|
|
if Use_Custom then
|
|
for L in Line'First
|
|
.. Integer'Min (Fields_Count, DB_Fields_Count)
|
|
loop
|
|
if Starts_With (Line (L).all, "&") then
|
|
Custom_Tables (L) := Tables (L);
|
|
Custom_Where (L) := new String'
|
|
("t" & Image (L, 0) & "." & Xref (L).all
|
|
& "='" -- ??? Beware of SQL injection here
|
|
& Line (L) (Line (L)'First + 1 .. Line (L)'Last)
|
|
& "'");
|
|
Free (Vals (L));
|
|
Vals (L) := new String'(Select_Values (L).all);
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
for L in Line'First
|
|
.. Integer'Min (Fields_Count, DB_Fields_Count)
|
|
loop
|
|
if Vals (L) = null then
|
|
Format_Field
|
|
(DB,
|
|
Line (L).all,
|
|
DB_Field_Mappings (L).all,
|
|
Vals (L),
|
|
Values (L),
|
|
Has_Xref => Use_Custom);
|
|
end if;
|
|
end loop;
|
|
|
|
if not Has_Xref then
|
|
Execute (DB, Q_Values, Params => Values);
|
|
elsif Use_Custom then
|
|
Execute
|
|
(DB, "INSERT INTO """ & Table.Name & """("
|
|
& Join
|
|
(",", DB_Fields (DB_Fields'First .. DB_Fields_Count))
|
|
& ") SELECT " & Join (",", Vals)
|
|
& " FROM " & Join (",", Custom_Tables)
|
|
& " WHERE " & Join (" and ", Custom_Where));
|
|
else
|
|
Execute (DB, Q_Values_With_Select, Params => Values);
|
|
end if;
|
|
|
|
Free (Custom_Where);
|
|
Free (Vals);
|
|
end;
|
|
end if;
|
|
|
|
exit when not Success (DB);
|
|
end loop;
|
|
|
|
Free (String_List (Line));
|
|
Free_Vars;
|
|
end Load_Data;
|
|
|
|
---------------
|
|
-- Load_Data --
|
|
---------------
|
|
|
|
procedure Load_Data
|
|
(DB : access Database_Connection_Record'Class;
|
|
File : GNATCOLL.VFS.Virtual_File;
|
|
Schema : DB_Schema := No_Schema;
|
|
Replace_Newline : Boolean := True)
|
|
is
|
|
Str : GNAT.Strings.String_Access;
|
|
begin
|
|
Str := Read_Whole_File (+File.Full_Name.all);
|
|
if Str /= null then
|
|
Load_Data (DB, Str.all, Schema, File.Display_Full_Name,
|
|
Replace_Newline => Replace_Newline);
|
|
Free (Str);
|
|
else
|
|
raise Invalid_File with "File not found: " & File.Display_Full_Name;
|
|
end if;
|
|
end Load_Data;
|
|
|
|
-------------------
|
|
-- New_Schema_IO --
|
|
-------------------
|
|
|
|
function New_Schema_IO
|
|
(File : GNATCOLL.VFS.Virtual_File) return File_Schema_IO'Class is
|
|
begin
|
|
return Result : File_Schema_IO do
|
|
Result.File := File;
|
|
end return;
|
|
end New_Schema_IO;
|
|
|
|
-------------------
|
|
-- New_Schema_IO --
|
|
-------------------
|
|
|
|
function New_Schema_IO
|
|
(DB : Database_Connection) return DB_Schema_IO'Class is
|
|
begin
|
|
return Result : DB_Schema_IO do
|
|
Result.DB := DB;
|
|
end return;
|
|
end New_Schema_IO;
|
|
|
|
-------------------
|
|
-- Quote_Keyword --
|
|
-------------------
|
|
|
|
function Quote_Keyword (Str : String) return String is
|
|
begin
|
|
if Keywords.Is_Empty then
|
|
-- For each keyword (from the postgreSQL documentation):
|
|
-- * reserved (in sql99 or postgreSQL) means the word cannot be
|
|
-- used for identifiers
|
|
-- * non means the word can only be used in some cases.
|
|
|
|
-- Keywords.Include ("ABORT"); -- psql:non
|
|
Keywords.Include ("ABS"); -- sql99:non
|
|
Keywords.Include ("ABSOLUTE"); -- sql99:reserved, psql:non
|
|
Keywords.Include ("ACCESS"); -- non postgres
|
|
Keywords.Include ("ACTION"); -- reserved sql99, not postgres
|
|
Keywords.Include ("ADA"); -- non
|
|
|
|
Keywords.Include ("ADD"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ADMIN"); -- psql:reserved, sql99:
|
|
Keywords.Include ("AFTER"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("AGGREGATE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ALIAS"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ALL"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ALLOCATE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ALTER"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ANALYSE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ANALYZE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("AND"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ANY"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ARE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ARRAY"); -- psql:reserved, sql99:
|
|
Keywords.Include ("AS"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ASC"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ASENSITIVE"); -- psql:non, sql99:
|
|
Keywords.Include ("ASSERTION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ASSIGNMENT"); -- psql:non, sql99:non
|
|
Keywords.Include ("ASYMMETRIC"); -- psql:non, sql99:
|
|
Keywords.Include ("AT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ATOMIC"); -- psql:non, sql99:
|
|
Keywords.Include ("AUTHORIZATION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("AVG"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("BACKWARD"); -- psql:non, sql99:
|
|
Keywords.Include ("BEFORE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("BEGIN"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("BETWEEN"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("BIGINT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("BINARY"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("BIT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("BITVAR"); -- psql:non, sql99:
|
|
Keywords.Include ("BIT_LENGTH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("BLOB"); -- psql:reserved, sql99:
|
|
Keywords.Include ("BOOLEAN"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("BOTH"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("BREADTH"); -- psql:reserved, sql99:
|
|
Keywords.Include ("BY"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("C"); -- psql:non, sql99:non
|
|
Keywords.Include ("CACHE"); -- psql:non, sql99:
|
|
Keywords.Include ("CALL"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CALLED"); -- psql:non, sql99:non
|
|
Keywords.Include ("CARDINALITY"); -- psql:non, sql99:
|
|
Keywords.Include ("CASCADE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CASCADED"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CASE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CAST"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CATALOG"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CATALOG_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("CHAIN"); -- psql:non, sql99:non
|
|
Keywords.Include ("CHAR"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("CHARACTER"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("CHARACTERISTICS"); -- psql:non, sql99:
|
|
Keywords.Include ("CHARACTER_LENGTH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CHARACTER_SET_CATALOG"); -- psql:non, sql99:non
|
|
Keywords.Include ("CHARACTER_SET_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("CHARACTER_SET_SCHEMA"); -- psql:non, sql99:non
|
|
Keywords.Include ("CHAR_LENGTH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CHECK"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CHECKED"); -- psql:non, sql99:
|
|
Keywords.Include ("CHECKPOINT"); -- psql:non, sql99:
|
|
Keywords.Include ("CLASS"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CLASS_ORIGIN"); -- psql:non, sql99:non
|
|
Keywords.Include ("CLOB"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CLOSE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CLUSTER"); -- psql:non, sql99:
|
|
Keywords.Include ("COALESCE"); -- psql:non, sql99:non
|
|
Keywords.Include ("COBOL"); -- psql:non, sql99:non
|
|
Keywords.Include ("COLLATE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("COLLATION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("COLLATION_CATALOG"); -- psql:non, sql99:non
|
|
Keywords.Include ("COLLATION_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("COLLATION_SCHEMA"); -- psql:non, sql99:non
|
|
Keywords.Include ("COLUMN"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("COLUMN_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("COMMAND_FUNCTION"); -- psql:non, sql99:non
|
|
Keywords.Include ("COMMAND_FUNCTION_CODE"); -- psql:non, sql99:
|
|
Keywords.Include ("COMMENT"); -- psql:non, sql99:
|
|
Keywords.Include ("COMMIT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("COMMITTED"); -- psql:non, sql99:non
|
|
Keywords.Include ("COMPLETION"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CONDITION_NUMBER"); -- psql:non, sql99:non
|
|
Keywords.Include ("CONNECT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CONNECTION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CONNECTION_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("CONSTRAINT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CONSTRAINTS"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CONSTRAINT_CATALOG"); -- psql:non, sql99:non
|
|
Keywords.Include ("CONSTRAINT_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("CONSTRAINT_SCHEMA"); -- psql:non, sql99:non
|
|
Keywords.Include ("CONSTRUCTOR"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CONTAINS"); -- psql:non, sql99:
|
|
Keywords.Include ("CONTINUE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CONVERSION"); -- psql:non, sql99:
|
|
Keywords.Include ("CONVERT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("COPY"); -- psql:non, sql99:
|
|
Keywords.Include ("CORRESPONDING"); -- psql:res., sql99:res.
|
|
Keywords.Include ("COUNT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CREATE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CREATEDB"); -- psql:non, sql99:
|
|
Keywords.Include ("CREATEUSER"); -- psql:non, sql99:
|
|
Keywords.Include ("CROSS"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("CUBE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CURRENT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CURRENT_DATE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CURRENT_PATH"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CURRENT_ROLE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("CURRENT_TIME"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CURRENT_TIMESTAMP"); -- psql:res., sql99:res.
|
|
Keywords.Include ("CURRENT_USER"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("CURSOR"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("CURSOR_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("CYCLE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DATA"); -- psql:reserved, sql99:non
|
|
Keywords.Include ("DATABASE"); -- psql:non, sql99:
|
|
Keywords.Include ("DATE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DATETIME_INTERVAL_CODE"); -- psql:non, sql99:non
|
|
Keywords.Include ("DATETIME_INTERVAL_PRECISION"); -- psql:n, sql99:n
|
|
Keywords.Include ("DAY"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DEALLOCATE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DEC"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("DECIMAL"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("DECLARE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DEFAULT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DEFERRABLE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DEFERRED"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DEFINED"); -- psql:non, sql99:
|
|
Keywords.Include ("DEFINER"); -- psql:non, sql99:non
|
|
Keywords.Include ("DELETE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DELIMITER"); -- psql:non, sql99:
|
|
Keywords.Include ("DELIMITERS"); -- psql:non, sql99:
|
|
Keywords.Include ("DEPTH"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DEREF"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DESC"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DESCRIBE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DESCRIPTOR"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DESTROY"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DESTRUCTOR"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DETERMINISTIC"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DIAGNOSTICS"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DICTIONARY"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DISCONNECT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DISPATCH"); -- psql:non, sql99:
|
|
Keywords.Include ("DISTINCT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("DO"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DOMAIN"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DOUBLE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DROP"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("DYNAMIC"); -- psql:reserved, sql99:
|
|
Keywords.Include ("DYNAMIC_FUNCTION"); -- psql:non, sql99:non
|
|
Keywords.Include ("DYNAMIC_FUNCTION_CODE"); -- psql:non, sql99:
|
|
Keywords.Include ("EACH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ELSE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ENCODING"); -- psql:non, sql99:
|
|
Keywords.Include ("ENCRYPTED"); -- psql:non, sql99:
|
|
Keywords.Include ("END"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("END-EXEC"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("EQUALS"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ESCAPE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("EVERY"); -- psql:reserved, sql99:
|
|
Keywords.Include ("EXCEPT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("EXCEPTION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("EXCLUSIVE"); -- psql:non, sql99:
|
|
Keywords.Include ("EXEC"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("EXECUTE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("EXISTING"); -- psql:non, sql99:
|
|
Keywords.Include ("EXISTS"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("EXPLAIN"); -- psql:non, sql99:
|
|
Keywords.Include ("EXTERNAL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("EXTRACT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("FALSE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("FETCH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("FINAL"); -- psql:non, sql99:
|
|
Keywords.Include ("FIRST"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("FLOAT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("FOR"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("FORCE"); -- psql:non, sql99:
|
|
Keywords.Include ("FOREIGN"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("FORTRAN"); -- psql:non, sql99:non
|
|
Keywords.Include ("FORWARD"); -- psql:non, sql99:
|
|
Keywords.Include ("FOUND"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("FREE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("FREEZE"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("FROM"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("FULL"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("FUNCTION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("G"); -- psql:non, sql99:
|
|
Keywords.Include ("GENERAL"); -- psql:reserved, sql99:
|
|
Keywords.Include ("GENERATED"); -- psql:non, sql99:
|
|
Keywords.Include ("GET"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("GLOBAL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("GO"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("GOTO"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("GRANT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("GRANTED"); -- psql:non, sql99:
|
|
Keywords.Include ("GROUP"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("GROUPING"); -- psql:reserved, sql99:
|
|
Keywords.Include ("HANDLER"); -- psql:non, sql99:
|
|
Keywords.Include ("HAVING"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("HIERARCHY"); -- psql:non, sql99:
|
|
Keywords.Include ("HOLD"); -- psql:non, sql99:
|
|
Keywords.Include ("HOST"); -- psql:reserved, sql99:
|
|
Keywords.Include ("HOUR"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("IDENTITY"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("IGNORE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ILIKE"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("IMMEDIATE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("IMMUTABLE"); -- psql:non, sql99:
|
|
Keywords.Include ("IMPLEMENTATION"); -- psql:non, sql99:
|
|
Keywords.Include ("IMPLICIT"); -- psql:non, sql99:
|
|
Keywords.Include ("IN"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("INCREMENT"); -- psql:non, sql99:
|
|
Keywords.Include ("INDEX"); -- psql:non, sql99:
|
|
Keywords.Include ("INDICATOR"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("INFIX"); -- psql:non, sql99:
|
|
Keywords.Include ("INHERITS"); -- psql:non, sql99:
|
|
Keywords.Include ("INITIALIZE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("INITIALLY"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("INNER"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("INOUT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("INPUT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("INSENSITIVE"); -- psql:non, sql99:non
|
|
Keywords.Include ("INSERT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("INSTANCE"); -- psql:non, sql99:
|
|
Keywords.Include ("INSTANTIABLE"); -- psql:non, sql99:
|
|
Keywords.Include ("INSTEAD"); -- psql:non, sql99:
|
|
Keywords.Include ("INT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("INTEGER"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("INTERSECT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("INTERVAL"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("INTO"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("INVOKER"); -- psql:non, sql99:non
|
|
Keywords.Include ("IS"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("ISNULL"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("ISOLATION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ITERATE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("JOIN"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("K"); -- psql:non, sql99:
|
|
Keywords.Include ("KEY"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("KEY_MEMBER"); -- psql:non, sql99:
|
|
Keywords.Include ("KEY_TYPE"); -- psql:non, sql99:
|
|
Keywords.Include ("LANCOMPILER"); -- psql:non, sql99:
|
|
Keywords.Include ("LANGUAGE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("LARGE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("LAST"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("LATERAL"); -- psql:reserved, sql99:
|
|
Keywords.Include ("LEADING"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("LEFT"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("LENGTH"); -- psql:non, sql99:non
|
|
Keywords.Include ("LESS"); -- psql:reserved, sql99:
|
|
Keywords.Include ("LEVEL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("LIKE"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("LIMIT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("LISTEN"); -- psql:non, sql99:
|
|
Keywords.Include ("LOAD"); -- psql:non, sql99:
|
|
Keywords.Include ("LOCAL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("LOCALTIME"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("LOCALTIMESTAMP"); -- psql:reserved, sql99:res.
|
|
Keywords.Include ("LOCATION"); -- psql:non, sql99:
|
|
Keywords.Include ("LOCATOR"); -- psql:reserved, sql99:
|
|
Keywords.Include ("LOCK"); -- psql:non, sql99:
|
|
Keywords.Include ("LOWER"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("M"); -- psql:non, sql99:
|
|
Keywords.Include ("MAP"); -- psql:reserved, sql99:
|
|
Keywords.Include ("MATCH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("MAX"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("MAXVALUE"); -- psql:non, sql99:
|
|
Keywords.Include ("MESSAGE_LENGTH"); -- psql:non, sql99:non
|
|
Keywords.Include ("MESSAGE_OCTET_LENGTH"); -- psql:non, sql99:non
|
|
Keywords.Include ("MESSAGE_TEXT"); -- psql:non, sql99:non
|
|
Keywords.Include ("METHOD"); -- psql:non, sql99:
|
|
Keywords.Include ("MIN"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("MINUTE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("MINVALUE"); -- psql:non, sql99:
|
|
Keywords.Include ("MOD"); -- psql:non, sql99:
|
|
Keywords.Include ("MODE"); -- psql:non, sql99:
|
|
Keywords.Include ("MODIFIES"); -- psql:reserved, sql99:
|
|
Keywords.Include ("MODIFY"); -- psql:reserved, sql99:
|
|
Keywords.Include ("MODULE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("MONTH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("MORE"); -- psql:non, sql99:non
|
|
Keywords.Include ("MOVE"); -- psql:non, sql99:
|
|
Keywords.Include ("MUMPS"); -- psql:non, sql99:non
|
|
Keywords.Include ("NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("NAMES"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("NATIONAL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("NATURAL"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("NCHAR"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("NCLOB"); -- psql:reserved, sql99:
|
|
Keywords.Include ("NEW"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("NEXT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("NO"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("NOCREATEDB"); -- psql:non, sql99:
|
|
Keywords.Include ("NOCREATEUSER"); -- psql:non, sql99:
|
|
Keywords.Include ("NONE"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("NOT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("NOTHING"); -- psql:non, sql99:
|
|
Keywords.Include ("NOTIFY"); -- psql:non, sql99:
|
|
Keywords.Include ("NOTNULL"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("NULL"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("NULLABLE"); -- psql:non, sql99:non
|
|
Keywords.Include ("NULLIF"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("NUMBER"); -- psql:non, sql99:non
|
|
Keywords.Include ("NUMERIC"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("OBJECT"); -- psql:reserved, sql99:
|
|
Keywords.Include ("OCTET_LENGTH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("OF"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("OFF"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("OFFSET"); -- psql:reserved, sql99:
|
|
Keywords.Include ("OIDS"); -- psql:non, sql99:
|
|
Keywords.Include ("OLD"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ON"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ONLY"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("OPEN"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("OPERATION"); -- psql:reserved, sql99:
|
|
Keywords.Include ("OPERATOR"); -- psql:non, sql99:
|
|
Keywords.Include ("OPTION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("OPTIONS"); -- psql:non, sql99:
|
|
Keywords.Include ("OR"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ORDER"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ORDINALITY"); -- psql:reserved, sql99:
|
|
Keywords.Include ("OUT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("OUTER"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("OUTPUT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("OVERLAPS"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("OVERLAY"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("OVERRIDING"); -- psql:non, sql99:
|
|
Keywords.Include ("OWNER"); -- psql:non, sql99:
|
|
Keywords.Include ("PAD"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("PARAMETER"); -- psql:reserved, sql99:
|
|
Keywords.Include ("PARAMETERS"); -- psql:reserved, sql99:
|
|
Keywords.Include ("PARAMETER_MODE"); -- psql:non, sql99:
|
|
Keywords.Include ("PARAMETER_NAME"); -- psql:non, sql99:
|
|
Keywords.Include ("PARAMETER_ORDINAL_POSITION"); -- sql99:non
|
|
Keywords.Include ("PARAMETER_SPECIFIC_CATALOG"); -- sql99:non
|
|
Keywords.Include ("PARAMETER_SPECIFIC_NAME"); -- psql:non, sql99:
|
|
Keywords.Include ("PARAMETER_SPECIFIC_SCHEMA"); -- psql:non, sql99:
|
|
Keywords.Include ("PARTIAL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PASCAL"); -- psql:non, sql99:non
|
|
Keywords.Include ("PASSWORD"); -- psql:non, sql99:
|
|
Keywords.Include ("PATH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PENDANT"); -- psql:non, sql99:
|
|
Keywords.Include ("PLACING"); -- psql:reserved, sql99:
|
|
Keywords.Include ("PLI"); -- psql:non, sql99:non
|
|
Keywords.Include ("POSITION"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("POSTFIX"); -- psql:reserved, sql99:
|
|
Keywords.Include ("PRECISION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PREFIX"); -- psql:reserved, sql99:
|
|
Keywords.Include ("PREORDER"); -- psql:reserved, sql99:
|
|
Keywords.Include ("PREPARE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PRESERVE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("PRIMARY"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("PRIOR"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PRIVILEGES"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PROCEDURAL"); -- psql:non, sql99:
|
|
Keywords.Include ("PROCEDURE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("PUBLIC"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("READ"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("READS"); -- psql:reserved, sql99:
|
|
Keywords.Include ("REAL"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("RECHECK"); -- psql:non, sql99:
|
|
Keywords.Include ("RECURSIVE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("REF"); -- psql:reserved, sql99:
|
|
Keywords.Include ("REFERENCES"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("REFERENCING"); -- psql:reserved, sql99:
|
|
Keywords.Include ("REINDEX"); -- psql:non, sql99:
|
|
Keywords.Include ("RELATIVE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("RENAME"); -- psql:non, sql99:
|
|
Keywords.Include ("REPEATABLE"); -- psql:non, sql99:non
|
|
Keywords.Include ("REPLACE"); -- psql:non, sql99:
|
|
Keywords.Include ("RESET"); -- psql:non, sql99:
|
|
Keywords.Include ("RESTRICT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("RESULT"); -- psql:reserved, sql99:
|
|
Keywords.Include ("RETURN"); -- psql:reserved, sql99:
|
|
Keywords.Include ("RETURNED_LENGTH"); -- psql:non, sql99:non
|
|
Keywords.Include ("RETURNED_OCTET_LENGTH"); -- psql:non, sql99:non
|
|
Keywords.Include ("RETURNED_SQLSTATE"); -- psql:non, sql99:non
|
|
Keywords.Include ("RETURNS"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("REVOKE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("RIGHT"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("ROLE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ROLLBACK"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ROLLUP"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ROUTINE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("ROUTINE_CATALOG"); -- psql:non, sql99:
|
|
Keywords.Include ("ROUTINE_NAME"); -- psql:non, sql99:
|
|
Keywords.Include ("ROUTINE_SCHEMA"); -- psql:non, sql99:
|
|
Keywords.Include ("ROW"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("ROWS"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("ROW_COUNT"); -- psql:non, sql99:non
|
|
Keywords.Include ("RULE"); -- psql:non, sql99:
|
|
Keywords.Include ("SAVEPOINT"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SCALE"); -- psql:non, sql99:non
|
|
Keywords.Include ("SCHEMA"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SCHEMA_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("SCOPE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SCROLL"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SEARCH"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SECOND"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SECTION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SECURITY"); -- psql:non, sql99:non
|
|
Keywords.Include ("SELECT"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SELF"); -- psql:non, sql99:
|
|
Keywords.Include ("SENSITIVE"); -- psql:non, sql99:
|
|
Keywords.Include ("SEQUENCE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SERIALIZABLE"); -- psql:non, sql99:non
|
|
Keywords.Include ("SERVER_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("SESSION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SESSION_USER"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SET"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SETOF"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("SETS"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SHARE"); -- psql:non, sql99:
|
|
Keywords.Include ("SHOW"); -- psql:non, sql99:
|
|
Keywords.Include ("SIMILAR"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("SIMPLE"); -- psql:non, sql99:non
|
|
Keywords.Include ("SIZE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SMALLINT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("SOME"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SOURCE"); -- psql:non, sql99:
|
|
Keywords.Include ("SPACE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SPECIFIC"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SPECIFICTYPE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SPECIFIC_NAME"); -- psql:non, sql99:
|
|
Keywords.Include ("SQL"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SQLCODE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SQLERROR"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SQLEXCEPTION"); -- psql:reserved, sql99:
|
|
Keywords.Include ("SQLSTATE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("SQLWARNING"); -- psql:reserved, sql99:
|
|
Keywords.Include ("STABLE"); -- psql:non, sql99:
|
|
Keywords.Include ("START"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("STATE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("STATEMENT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("STATIC"); -- psql:reserved, sql99:
|
|
Keywords.Include ("STATISTICS"); -- psql:non, sql99:
|
|
Keywords.Include ("STDIN"); -- psql:non, sql99:
|
|
Keywords.Include ("STDOUT"); -- psql:non, sql99:
|
|
Keywords.Include ("STORAGE"); -- psql:non, sql99:
|
|
Keywords.Include ("STRICT"); -- psql:non, sql99:
|
|
Keywords.Include ("STRUCTURE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("STYLE"); -- psql:non, sql99:
|
|
Keywords.Include ("SUBCLASS_ORIGIN"); -- psql:non, sql99:non
|
|
Keywords.Include ("SUBLIST"); -- psql:non, sql99:
|
|
Keywords.Include ("SUBSTRING"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("SUM"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("SYMMETRIC"); -- psql:non, sql99:
|
|
Keywords.Include ("SYSID"); -- psql:non, sql99:
|
|
Keywords.Include ("SYSTEM"); -- psql:non, sql99:
|
|
Keywords.Include ("SYSTEM_USER"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TABLE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TABLE_NAME"); -- psql:non, sql99:non
|
|
Keywords.Include ("TEMP"); -- psql:non, sql99:
|
|
Keywords.Include ("TEMPLATE"); -- psql:non, sql99:
|
|
Keywords.Include ("TEMPORARY"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("TERMINATE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("THAN"); -- psql:reserved, sql99:
|
|
Keywords.Include ("THEN"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TIME"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("TIMESTAMP"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("TIMEZONE_HOUR"); -- psql:reserved, sql99:res.
|
|
Keywords.Include ("TIMEZONE_MINUTE"); -- psql:reserved, sql99:res.
|
|
Keywords.Include ("TO"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TOAST"); -- psql:non, sql99:
|
|
Keywords.Include ("TRAILING"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TRANSACTION"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("TRANSACTIONS_COMMITTED"); -- psql:non, sql99:
|
|
Keywords.Include ("TRANSACTIONS_ROLLED_BACK"); -- psql:non, sql99:
|
|
Keywords.Include ("TRANSACTION_ACTIVE"); -- psql:non, sql99:
|
|
Keywords.Include ("TRANSFORM"); -- psql:non, sql99:
|
|
Keywords.Include ("TRANSFORMS"); -- psql:non, sql99:
|
|
Keywords.Include ("TRANSLATE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("TRANSLATION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TREAT"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("TRIGGER"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("TRIGGER_CATALOG"); -- psql:non, sql99:
|
|
Keywords.Include ("TRIGGER_NAME"); -- psql:non, sql99:
|
|
Keywords.Include ("TRIGGER_SCHEMA"); -- psql:non, sql99:
|
|
Keywords.Include ("TRIM"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("TRUE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("TRUNCATE"); -- psql:non, sql99:
|
|
Keywords.Include ("TRUSTED"); -- psql:non, sql99:
|
|
Keywords.Include ("TYPE"); -- psql:non, sql99:non
|
|
Keywords.Include ("UNCOMMITTED"); -- psql:non, sql99:non
|
|
Keywords.Include ("UNDER"); -- psql:reserved, sql99:
|
|
Keywords.Include ("UNENCRYPTED"); -- psql:non, sql99:
|
|
Keywords.Include ("UNION"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("UNIQUE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("UNKNOWN"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("UNLISTEN"); -- psql:non, sql99:
|
|
Keywords.Include ("UNNAMED"); -- psql:non, sql99:non
|
|
Keywords.Include ("UNNEST"); -- psql:reserved, sql99:
|
|
Keywords.Include ("UNTIL"); -- psql:non, sql99:
|
|
Keywords.Include ("UPDATE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("UPPER"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("USAGE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("USER"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("USER_DEFINED_TYPE_CATALOG"); -- psql:non, sql99:
|
|
Keywords.Include ("USER_DEFINED_TYPE_NAME"); -- psql:non, sql99:
|
|
Keywords.Include ("USER_DEFINED_TYPE_SCHEMA"); -- psql:non, sql99:
|
|
Keywords.Include ("USING"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("VACUUM"); -- psql:non, sql99:
|
|
Keywords.Include ("VALID"); -- psql:non, sql99:
|
|
Keywords.Include ("VALIDATOR"); -- psql:non, sql99:
|
|
Keywords.Include ("VALUE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("VALUES"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("VARCHAR"); -- psql:non, sql99:(cannot
|
|
Keywords.Include ("VARIABLE"); -- psql:reserved, sql99:
|
|
Keywords.Include ("VARYING"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("VERBOSE"); -- psql:reserved, sql99:(can
|
|
Keywords.Include ("VERSION"); -- psql:non, sql99:
|
|
Keywords.Include ("VIEW"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("VOLATILE"); -- psql:non, sql99:
|
|
Keywords.Include ("WHEN"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("WHENEVER"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("WHERE"); -- psql:reserved, sql99:reserved
|
|
Keywords.Include ("WITH"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("WITHOUT"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("WORK"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("WRITE"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("YEAR"); -- psql:non, sql99:reserved
|
|
Keywords.Include ("ZONE"); -- psql:non, sql99:reserved
|
|
end if;
|
|
|
|
-- With postgreSQL, there is also an issue with identifiers using
|
|
-- CamelCase (this is accepted when creating the database, but then
|
|
-- we get an error when creating an index saying that the field does
|
|
-- not exist). So we always convert to lower case.
|
|
|
|
if Keywords.Contains (Str)
|
|
or else To_Lower (Str) /= Str
|
|
then
|
|
-- Insert two '"' at start and end, since these names will be quoted
|
|
-- in the generated files.
|
|
|
|
return '"' & Str & '"';
|
|
else
|
|
return Str;
|
|
end if;
|
|
end Quote_Keyword;
|
|
|
|
-------------------
|
|
-- Free_Dispatch --
|
|
-------------------
|
|
|
|
procedure Free_Dispatch (Self : in out Abstract_Table_Description'Class) is
|
|
begin
|
|
Free (Self);
|
|
end Free_Dispatch;
|
|
|
|
begin
|
|
Register_Field_Mapping (Field_Mapping_Text'(others => <>));
|
|
Register_Field_Mapping (Field_Mapping_Integer'(null record));
|
|
Register_Field_Mapping (Field_Mapping_Autoincrement'(null record));
|
|
Register_Field_Mapping (Field_Mapping_Timestamp'(null record));
|
|
Register_Field_Mapping (Field_Mapping_Float'(null record));
|
|
Register_Field_Mapping (Field_Mapping_Money'(null record));
|
|
end GNATCOLL.SQL.Inspect;
|