I have all my table classes written for mssql but now I want to test my application locally so I need sqlitedb.Is there a way through which I can Replicate my database in sqlite.
I am facing some issues like sqlite does not support Float as a Primary key.I have more than 200 tables I can not go and edit all just for testing.I can have all the tables in one metadata.
My idea is to use sqlite just for testing and for production I will still be using mssql.
Note I changed Float to Integer but still my tables are not created instead it just creates a empty db.
My code
for table in metadata.tables:
keys_to_change = []
for pkey_column in metadata.tables[table].primary_key.columns.keys():
keys_to_change.append(pkey_column)
for data in list(metadata.tables[table].foreign_keys):
keys_to_change.append(data.column.name)
for column in metadata.tables[table].columns:
if column.name in keys_to_change:
if str(column.type) == 'FLOAT':
column.type = INTEGER
engine = create_engine('sqlite:///mytest.db', echo=True, echo_pool=True)
metadata.create_all(engine)
If you are able to change the model code, I would suggest to create an alias to the Float and use it to define those primary_key and ForeignKey columns, which you could just change for your sqlite testing:
# CONFIGURATION
PKType = Float # default: MSSQL; or Float(N, M)
# PKType = Integer # uncomment this for sqlite
and your model becomes like below:
class MyParent(Base):
__tablename__ = 'my_parent'
id = Column(PKType, primary_key=True)
name = Column(String)
children = relationship('MyChild', backref='parent')
class MyChild(Base):
__tablename__ = 'my_child'
id = Column(PKType, primary_key=True)
parent_id = Column(PKType, ForeignKey('my_parent.id'))
name = Column(String)
Alternatively, if you would like to be only changing the engine and not another configuration variable, you can use dialect-specific custom type handling:
import sqlalchemy.types as types
class PKType(types.TypeDecorator):
impl = Float
def load_dialect_impl(self, dialect):
if dialect.name == 'sqlite':
return dialect.type_descriptor(Integer())
else:
return dialect.type_descriptor(Float())
Related
I am using flask-sqlalchemy and I want to create a class from a view instead of a db table. Is there an alternative to tablename? 'Car' was recently changed from a table to a view and now it's stuck sending a request.
class car(db.Model):
__tablename__ = 'car'
model = Column(Text, primary_key=True)
brand = Column(Text, primary_key=True)
condition = Column(Text, primary_key=True)
year = Column(Integer)
SQLAlchemy does not have a particular problem with ORM objects based on views. For example, this works fine with SQL Server because SQL Server allows DML (INSERT, UPDATE, DELETE) on views:
# set up test environment
with engine.begin() as conn:
conn.exec_driver_sql("DROP TABLE IF EXISTS car_table")
conn.exec_driver_sql("CREATE TABLE car_table (id integer primary key, make varchar(50))")
conn.exec_driver_sql("INSERT INTO car_table (id, make) VALUES (1, 'Audi'), (2, 'Buick')")
conn.exec_driver_sql("DROP VIEW IF EXISTS car_view")
conn.exec_driver_sql("CREATE VIEW car_view AS SELECT * FROM car_table WHERE id <> 2")
Base = sa.orm.declarative_base()
class Car(Base):
__tablename__ = "car_view"
id = Column(Integer, primary_key=True, autoincrement=False)
make = Column(String(50), nullable=False)
def __repr__(self):
return f"<Car(id={self.id}, make='{self.make}')>"
with Session(engine) as session:
print(session.execute(select(Car)).all())
# [(<Car(id=1, make='Audi')>,)]
# (note: the view excludes the row (object) where id == 2)
session.add(Car(id=3, make="Chevrolet"))
session.commit()
print(session.execute(select(Car)).all())
# [(<Car(id=1, make='Audi')>,), (<Car(id=3, make='Chevrolet')>,)]
However, if you really are using SQLite then you won't be able to add, update, or delete objects using a class based on a view because SQLite doesn't allow that:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) cannot modify car_view because it is a view
[SQL: INSERT INTO car_view (id, make) VALUES (?, ?)]
[parameters: (3, 'Chevrolet')]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
I have created a CDS views as follows:
define view YGAC_I_REQUEST_ROLE
with parameters
pm_req_id : grfn_guid,
#Consumption.defaultValue: 'ROL'
pm_item_type : grac_prov_item_type,
#Consumption.defaultValue: 'AP'
pm_approval : grac_approval_status
as select from YGAC_I_REQ_PROVISION_ITEM as provitem
association [1..1] to YGAC_I_ROLE as _Role on _Role.RoleId = provitem.ProvisionItemId
association [1..*] to YGAC_I_ROLE_RS as _Relation on _Relation.RoleId1 = provitem.ProvisionItemId
{
key ReqId,
key ReqIdItem,
Connector,
ProvisionItemId,
ActionType,
ValidFrom,
ValidTo,
_Role.RoleId,
_Role.RoleName,
_Role.RoleType,
_Role,
_Relation
}
where
ReqId = $parameters.pm_req_id
and ProvisionItemType = $parameters.pm_item_type
and ApprovalStatus = $parameters.pm_approval
Then I have consumed in ABAP:
SELECT
FROM ygac_i_request_role( pm_req_id = #lv_test,
pm_item_type = #lv_item_type,
pm_approval = #lv_approval
)
FIELDS reqid,
connector,
provisionitemid
INTO TABLE #DATA(lt_result).
How to get the list of _Relation according to selection above.
This is generally not possible like in ABAP SQL queries:
SELECT m~*, kt~*
FROM mara AS m
JOIN makt AS kt
...
This contradicts the whole idea of CDS associations, because they were created to join on-demand and to reduce redundant calls to database. Fetching all fields negates the whole idea of "lazy join".
However, there is another syntax in FROM clause which is enabled by path expressions that allows querying underlining associations both fully and by separate elements. Here is how
SELECT *
FROM ygac_i_request_role( pm_req_id = #lv_test )
\_Role AS role
INTO TABLE #DATA(lt_result).
This fetches all the fields of _Role association into internal table.
Note: remember, it is not possible to fetch all the published associations of current view simultaneously, only one path per query.
Possible workaround is to use JOIN
SELECT *
FROM ygac_i_request_role AS main
JOIN ygac_i_request_role
\_Role AS role
ON main~ProvisionItemId = role~RoleId
JOIN ygac_i_request_role
\_Relation AS relation
ON main~ProvisionItemId = relation~RoleId1
INTO TABLE #DATA(lt_table).
This creates deep-structured type with dedicated structure for every join association like this:
If you are not comfortable with such structure for you task, lt_table should be declared statically to put up all the fields in a flat way
TYPES BEGIN OF ty_table.
INCLUDE TYPE ygac_i_request_role.
INCLUDE TYPE ygac_i_role.
INCLUDE TYPE ygac_i_role_rs.
TYPES END OF ty_table.
I want to make a not-null non-auto-inc integer my primary key, but I am unable to do so in design time with Firedac. There is no parameter for the TIntegerfield that allows me to make it primary. There is also no parameter of the TFDTable where I can choose the primary field out of all available fields.
I get that it may be possible doing it in code & combining it with my design time table but that beats the whole purpose of doing it all in design time.
Earlier I did have a auto-inc ID in my table, and this was automatically set to primary key. I deleted this field now because I need another integer to be the primary.
Also I can't find information about the primary key & TFDTable on the official Embacadero website.
It's best to experiment with this using a new table in your database and a minimal new Delphi project.
Update: See below for database DDL and Form's DFM.
You need to have your ID field marked as a primary key in your database.
After you've added an FDConnection and an FDTable to your project, select the FDTable's TableName from the drop down list. Then, click in the FDTable's IndexName field and you should find an automatically-named index on the table's Primary Key. Just select it so that the IndexName takes its value. That's all there is to it.
For the table created using the DDL below, the IndexName property of the FDTable appears as sqlite_autoindex_test_1
If you then dbl-click the FDTable and use the pop-up Fields editor to set up persistent fields on the FDTable and then select your ID field, you should find that if you examine its ProviderFlags, they should include pfInKey, which is what tells FireDAC to use the field as the table's primary key when generating the SQL to update it, do inserts, etc.
You should find that the ID field's Required field is autmatically set to True, btw.
If you want to supply the ID Field's value yourself when adding a new record, use the table's OnNewRecord to generate the ID value an assign it to the field.
DDL for test Sqlite database
create table test(
id int not null primary key,
AName nchar(12)
)
Project DFM extract
object Form2: TForm2
object DBGrid1: TDBGrid
DataSource = DataSource1
end
object DBNavigator1: TDBNavigator
DataSource = DataSource1
end
object FDConnection1: TFDConnection
Params.Strings = (
'Database=D:\aaad7\sqlite\MADB1.sqlite'
'DriverID=SQLite')
Connected = True
LoginPrompt = False
end
object DataSource1: TDataSource
DataSet = FDTable1
end
object FDTable1: TFDTable
IndexName = 'sqlite_autoindex_test_1'
Connection = FDConnection1
UpdateOptions.UpdateTableName = 'test'
TableName = 'test'
object FDTable1id: TIntegerField
FieldName = 'id'
Origin = 'id'
ProviderFlags = [pfInUpdate, pfInWhere, pfInKey]
Required = True
end
object FDTable1AName: TWideStringField
FieldName = 'AName'
Origin = 'AName'
FixedChar = True
Size = 12
end
end
end
I am trying to reproduce the SQL Server default NEWID() value for a UNIQUEIDENTIFIER column in SQLite. It produces a UUID as a default for the column.
How can I do that in SQLite?
I found this page (Is there UID datatype in SQLITE if Yes then how to generate value for that) where they use a select statement but it is no good for DEFAULT.
NOTE : I am using the SQLite version implemented for browsers as I am using it through ionic and the cordova-sqlite plugin. So this does not contain all the features provided by SQLite and more importantly this does not contain the RANDOM function.
Yes, you can use create function to add a UUID function. By default you'd have to write it in C, but many SQLite drivers have a way to add functions in their native language. Here's an example in Perl using DBD::SQLite.
use strict;
use warnings;
use v5.10;
use DBI;
use Data::UUID;
# Connect to the database.
my $dbh = DBI->connect("dbi:SQLite:dbname=test.db", "", "", { RaiseError => 1});
# Create uuid() which calls Perl to generate a uuid.
$dbh->sqlite_create_function("uuid", 0, sub { Data::UUID->new->create_str });
# Create a table using uuid() as the default.
$dbh->do(q[drop table if exists foo]);
$dbh->do(q[
create table foo (
id int primary key default (uuid()),
name text
)
]);
# Insert some rows using the default id.
$dbh->do(q[
insert into foo (name) values ("you"), ("me")
]);
# Print out the rows.
my $rows = $dbh->selectall_arrayref(q[select * from foo]);
for my $row (#$rows) {
say "$row->[0], $row->[1]";
}
And the result is.
E9768488-834F-11E6-AA60-F143DC5749B0, you
E976B69C-834F-11E6-AA60-F143DC5749B0, me
I set up a SQLite db with the same schema as my existing SQL server db and noted the following...
SQLite field names (and presumably everything else) are case sensitive.
MicroLite's SqlBuilder appears to insert the prefix 'dbo.' before the table name, which SQLite doesn't like...
This query works...
query = new SqlQuery("SELECT [ClubID], [Name] FROM [Clubs] WHERE [ClubID] = #p0", 3);
clubs = session.Fetch<MicroLiteClub>(query);
This one doesn't...
query = SqlBuilder.Select("*")
.From(typeof(MicroLiteClub))
.Where("ClubID = #p0", 3)
.OrWhere("ClubID = #p1", 22)
.OrderByDescending("Name")
.ToSqlQuery();
clubs = session.Fetch<MicroLiteClub>(query);
MicroLite logged: "no such table: dbo.Clubs"
This is happening because SQLite doesn't support table schemas like MS SQL Server does.
In the hand crafted query, you are not specifying a schame for the table FROM [Clubs] however in your mapping attribute you will have specified dbo as the schema like this:
[Table(schema: "dbo", name: "Clubs")]
The SqlBuilder doesn't know what SQL Dialect is in use so if a schame is present on the table mapping, it will be used. This means that it would generate FROM [dbo].[Clubs]. To rectify this, simply remove the schema value on the TableAttribute as is optional from MicroLite 2.1 onwards.
On a side note, MicroLite 2.1 introduced support for In in the SqlBuilder fluent API so you could change:
.Where("ClubID = #p0", 3)
.OrWhere("ClubID = #p1", 22)
to
.Where("ClubID").In(3, 22)