35
   43
   44:-module(cql_autoschema,
   45         []).   46
   84
   85:-use_module(library(cql/cql_database)).   86:-use_module(library(dcg/basics)).   87
   90
   91schema_fact(Schema, DBMS, dbms(Schema, DBMS)).
   92
   93schema_fact(Schema, 'PostgreSQL', Fact):-
   94        odbc_connection_call(Schema, Connection, odbc_query(Connection, 'SELECT table_name, column_name, data_type, character_maximum_length, numeric_precision, numeric_scale, domain_name, is_nullable, column_default FROM INFORMATION_SCHEMA.columns WHERE table_schema = \'public\'', row(EntityName, ColumnName, NativeType, CharacterMaximumLength, NumericPrecision, NumericScale, DomainType, IsNullable, ColumnDefault))),
   95        ( IsNullable == '1' -> AllowsNulls = allows_nulls(true) ; AllowsNulls = allows_nulls(false) ),
   96        map_native_type(NativeType, CharacterMaximumLength, NumericPrecision, NumericScale, Mapped),
   97        ( DomainType \== {null} -> DomainOrNativeType = domain(DomainType) ; DomainOrNativeType = native_type(Mapped) ),
   98        IsIdentity = is_identity(false),
   99        ( Fact = database_attribute(table, Schema, EntityName, ColumnName, DomainOrNativeType, AllowsNulls, IsIdentity, ColumnDefault)
  100        ; Fact = odbc_data_type(Schema, EntityName, ColumnName, Mapped)
  101        ).
  102
  103schema_fact(Schema, 'PostgreSQL', primary_key_column_name(Schema, EntityName, ColumnName)):-
  104        odbc_connection_call(Schema, Connection, odbc_query(Connection, 'SELECT kcu.table_name, column_name FROM INFORMATION_SCHEMA.key_column_usage KCU INNER JOIN INFORMATION_SCHEMA.table_constraints tc ON(tc.table_name = kcu.table_name AND tc.constraint_type = \'PRIMARY KEY\') WHERE kcu.table_schema = \'public\'', row(EntityName, ColumnName))).
  105
  106schema_fact(Schema, 'PostgreSQL', database_domain(DomainName, Mapped)):-
  107        odbc_connection_call(Schema, Connection, odbc_query(Connection, 'SELECT domain_name, data_type, character_maximum_length, numeric_precision, numeric_scale FROM INFORMATION_SCHEMA.domains WHERE domain_schema = \'public\'', row(DomainName, NativeType, CharacterMaximumLength, NumericPrecision, NumericScale))),
  108        map_native_type(NativeType, CharacterMaximumLength, NumericPrecision, NumericScale, Mapped).
  109
  110schema_fact(Schema, 'PostgreSQL', routine_return_type(Schema, RoutineName, Mapped)):-
  111        odbc_connection_call(Schema, Connection, odbc_query(Connection, 'SELECT data_type, character_maximum_length, numeric_precision, numeric_scale FROM INFORMATION_SCHEMA.routines WHERE routine_schema = \'public\'', row(RoutineName, NativeType, CharacterMaximumLength, NumericPrecision, NumericScale))),
  112        map_native_type(NativeType, CharacterMaximumLength, NumericPrecision, NumericScale, Mapped).
  113
  114schema_fact(Schema, 'SQLite', Fact):-
  115        findall(EntityName,
  116                odbc_connection_call(Schema, Connection, odbc_query(Connection, 'SELECT name FROM sqlite_master WHERE type = \'table\'', row(EntityName))),
  117                EntityNames),
  118        member(EntityName, EntityNames),
  119        format(atom(Pragma), 'PRAGMA table_info(~w)', [EntityName]),
  120        odbc_connection_call(Schema, Connection, odbc_query(Connection, Pragma, Row)),
  121        Row = row(_CID, ColumnName, ColumnTypeAtom, NotNullable, ColumnDefault, IsPkMember),
  122        ( NotNullable == 0 ->
  123            AllowsNulls = allows_nulls(true)
  124        ; otherwise->
  125            AllowsNulls = allows_nulls(false)
  126        ),
  127        ColumnTypeAtom \== '',   128        ( sqlite_type(ColumnTypeAtom, ColumnType)->
  129            true
  130        ; otherwise->
  131            writeln(bad_type(EntityName, ColumnName, ColumnTypeAtom))
  132        ),
  133        IsIdentity = is_identity(false),   134          135        ( ColumnType == datetime ->
  136              137            OdbcDataType = timestamp
  138        ; otherwise->
  139            OdbcDataType = ColumnType
  140        ),
  141        ( Fact = database_attribute(table, Schema, EntityName, ColumnName, native_type(ColumnType), AllowsNulls, IsIdentity, ColumnDefault)
  142        ; Fact = odbc_data_type(Schema, EntityName, ColumnName, OdbcDataType)
  143        ; IsPkMember == 1, Fact = primary_key_column_name(Schema, EntityName, ColumnName)
  144        ).
  145
  146
  147map_native_type('character varying', N, _, _, varchar(N)):- !.
  148map_native_type('decimal', _, P, S, decimal(P, S)):- !.
  149map_native_type(X, _, _, _, X).
  150
  151sqlite_type(Atom, Type):-
  152        atom_codes(Atom, Codes),
  153        sqlite_type_1(Type, Codes, []).
  154sqlite_type_1(decimal(P, S))-->
  155        ( "DECIMAL" ; "decimal" ; "Decimal"),
  156        !,
  157        whitespace, "(", whitespace, number(P), whitespace, ",", whitespace, number(S), whitespace,  ")".
  158
  159sqlite_type_1(varchar(N))-->
  160        ( "VARCHAR" ; "varchar" ; "Varchar"),
  161        !,
  162        whitespace, "(", whitespace, number(N), whitespace, ")".
  163
  164sqlite_type_1(varbinary(max))-->
  165        ( "VARBINARY" ; "varbinary" ; "Varbinary"), !.
  166
  167sqlite_type_1(longvarchar)-->
  168        ( "LONGVARCHAR" ; "longvarchar" ; "Longvarchar"), !.
  169
  170sqlite_type_1(integer)-->
  171        ( "INTEGER" ; "integer" ; "Integer"), !.
  172
  173sqlite_type_1(datetime)-->
  174        ( "DATETIME" ; "datetime" ; "Datetime"), !.
  175
  176sqlite_type_1(bigint)-->
  177        ( "BIGINT" ; "bigint" ; "Bigint"), !.
  178
  179sqlite_type_1(smallint)-->
  180        ( "SMALLINT" ; "smallint" ; "Smallint"), !.
  181
  182
  183whitespace--> [C], {code_type(C, white)}, !, whitespace.
  184whitespace--> [].
  185
  186
  187user:term_expansion(:-build_schema(Schema), Facts):-
  188        odbc_connection_call(Schema, Connection, odbc_get_connection(Connection, dbms_name(DBMS))),
  189        setof(cql:Fact,
  190              schema_fact(Schema, DBMS, Fact),
  191              Facts)