FireDAC Add new field to existing SQL table - delphi

I am trying to create a new field in a FireDAC-compatible database with this PROCEDURE:
PROCEDURE CreateField(Connection : TFDConnection ; CONST TableName : STRING; F : TFieldDefinition);
VAR
Table : TFDTable;
BEGIN
Table:=TFDTable.Create(NIL);
TRY
Table.Connection:=Connection;
Table.TableName:=TableName;
Table.FieldDefs.Updated:=FALSE;
Table.FieldDefs.Update;
Table.FieldDefs.Add(F.FieldName,F.FieldType,F.MaxLen,NOT F.Nullable);
// Commit my changes to the database //
FINALLY
FreeAndNIL(Table)
END
END;
where
TYPE
TFieldDefinition = CLASS
PUBLIC
FieldName : STRING;
FieldType : TFieldType;
MaxLen : Integer;
Nullable : BOOLEAN;
END;
but I can't seem to "Commit" my changes back to the database (ie. end up executing an ALTER TABLE ADD [COLUMN] statement).
How do I commit my changes to the FieldDefs list of the table? Or is there some other way - using plain FireDAC - that I can use to create a new field in an existing table?
Note: There are already data in the table, so I can't simply "DROP" and then "CREATE" the table again.

FireDAC should understand the sytax used by the supported databases and use the appropriate syntax and decoration. See the documentation here: http://docwiki.embarcadero.com/RADStudio/Sydney/en/Preprocessing_Command_Text_(FireDAC)
TFieldDef is an internal descriptor of the fields in a TDataSet, it's not going to be used to update the table structure automatically. (Although you could write your own procedure that compares your TFieldDefs to the FireDAC MEta Data and creates the DDL (Data Definition Language) statements you need to execute in a TFDCommand ... )
To alter that table structure you will need to provide the DDL (SQL) statement that you execute with TFDCommand - the 'preprocessing' link above will explain how to write it in a dialect abstracted way.
If you use the appropriate FireDAC description it will automatically put in the appropriate SQL decoration for you so the syntax is valid. Depending on your SQL dialect and the FireDAC driver you may hit limitations. (For example using the ODBC driver FireDAC generally won't know the specific details of the underlying database - we had to implement a solution for SAP HANA which had exactly this challenge).
Bear in mind that some SQL dialects support features that others don't - so for example it's not safe to assume that you can position a column when you add it (which MySQL supports for example) as not all dialects allow that in an ALTER TABLE statement.

Related

How to create a strong typed sql statement returning multiple result sets

Currently I am using a stored procedure returning multiple result sets. I set up a test and found that the result of the stored procedure can efficiently be read by using the "ExecuteReader" method of the DataConnection class and then Executing or Querying the DataReader.
The problem with this approach is that there is no tight coupling between Code and Stored Procedure. So I would like to replace the Stored Procedure by a Linq2db expression that produces a sql statement with multiple select statements. The ExecuteReader method is expecting a string sql parameter. How can the Sql string be constructed based on a DataConnection object that has knowledge of my Database schema. Is this feature somehow implemented in linq2Db.

Creating a Snowflake warehouse in a stored procedure changes the current warehouse

I'm trying to write a procedure that creates users, roles and warhouse. This procedure is executed with a high privilege user with ACCOUNTADMIN role
USE ACCOUNT_ADMIN;
USE WAREHOUSE PROVISIONER;
CREATE or replace PROCEDURE TEST()
RETURNS VARCHAR
LANGUAGE javascript
AS
$$
snowflake.execute({ sqlText: "CREATE OR REPLACE WAREHOUSE test;"});
return "";
$$
;
Now if I query the current warehouse:
SELECT CURRENT_WAREHOUSE();
I get PROVISIONER
If I now call the procedure
CALL TEST();
The warehouse has changed
SELECT CURRENT_WAREHOUSE();
Now returns TEST
Is this the normal behaviour? This is an issue because the warehouse is changed outside of the procedure, and I cannot use the "USE WAREHOUSE" statement inside a procedure.
Is there another way to create WAREHOUSE from a procedure without changing the current warehouse?
Yes, this is expected. See the docs for background information.
One way to work around it would be to get the current warehouse at the beginning of your stored procedure, and set it again as the current one at the end of the stored procedure.

Change BOOLEAN to LOGICAL in generated sql script by FDTable

I have a FDTable with TBooleanField.
The database is dBase IV.
I want to create the table with:
<TFDTable>.CreateTable(False, [tpTable]);
This works if I don't have a TBooleanField in the table.
In the SQL script that generated on CreateTable the TBooleanField is of type BOOLEAN. Is there something in property of FDConnection or FDTable that change the BOOLEAN to LOGICAL.
SQL Script:
CREATE TABLE ACT_01 (
ISDOC BOOLEAN,
DOCTYPE VARCHAR(1))
Must change in:
CREATE TABLE ACT_01 (
ISDOC LOGICAL,
DOCTYPE VARCHAR(1))
Ok, I can execute the sql myself without the createtable but I want to know if it is possible to change the BOOLEAN to LOGICAL from the methode TFDTable.CreateTable
FDConnection:
FDConnection1.Params.Add('DriverID=ODBC');
FDConnection1.Params.Add('ODBCDriver={Microsoft dBase Driver (*.dbf)}');
FDConnection1.Params.Add('Database=C:\Projects\Test Projects\DBase table\Data');
FireDAC uses hardcoded data types for generating CREATE command for all unsupported ODBC drivers (the ODBC command generator's GetColumnType method doesn't query driver for DBMS data type names).
What's more, FireDAC does not recognize any ftLogical data type, so you cannot create a custom data type mapping even though that's just what you were supposed to do here.
So, you're out of luck at this time.

derby procedure that does not modify

I'm trying to create a trigger on derby which simply calls a procedure. The stored procedure does not change anything and gets no parameters. It simply check that the time is within an interval (for example between 08:00 and 16:00). On creation of trigger i receive the following error:
"42Z9D: Procedures that modify SQL data are not allowed in BEFORE triggers."
But the procedure makes no changes.
When defining a procedure one should specify if the procedure modifies data or not. If it executes any sql or not. As mentioned in the link provided above by Bryan I should use one the options:
{ NO SQL | MODIFIES SQL DATA | CONTAINS SQL | READS SQL DATA }
If you dont use this options the default value will be assumed that is CONTAINS SQL.

How to call storedproc using openJPA and map to existing entity

I am trying to figure out how to call a stored procedure using openJPA
How do I do that? I assume it is the same as calling a namedQuery, but I can't find anywhere online where to do this.
I can't find one tutorial.
Also, how do you map it to an existing entity? Just have a ("nameOfStoredProc", NameOfEntity.class)?
From the OpenJPA user manual.... Creating SQL Queries
In addition to SELECT statements, OpenJPA supports stored procedure
invocations as SQL queries. OpenJPA will assume any SQL that does not
begin with the SELECT keyword (ignoring case) is a stored procedure
call, and invoke it as such at the JDBC level.
EntityManager em = ...;
Query query = em.createNativeQuery("StoredProcName", Magazine.class);
processMagazines(query.getResultList());

Resources