Cannot insert NULL into table - oracle

I'm using EF Core to connect to an Oracle11g database (using the Oracle.EntityFrameworkCore v2.19.90 provider). It's a code first scenario, the tables are created successfully and everything is as expected.
The problem is, when I try to insert something into the database, for example:
_context.Roles.Add(new ApplicationRole()
{
Name = "FOO",
DisplayName = "Foo"
});
_context.SaveChanges();
I get an error:
OracleException: ORA-01400: cannot insert NULL into ("SCHEMA"."AppRole"."Id")
The column Id is indeed non-nullable. When I use the SQL Server provider, everything is fine, the SQL Server automatically chooses an id for my entity.
Is there any way to get Oracle to set an Id for me? Or could it be done in another way?
I don't want to use Oracle triggers and the solution should be full code first.

As you're on Oracle 11g, then you have to use a trigger along with a sequence which will populate ID column in the background.
Another option is to, obviously, provide ID value during insert.
If you were on 12c or above, you could have used identity column. As you're not, your options are listed above.

One option may be usage of SEQUENCE and default value:
CREATE TABLE AppRole(
Id INT NOT NULL,
Name VARCHAR2(100),
DisplayName VARCHAR2(100)
);
CREATE SEQUENCE seq;
ALTER TABLE AppRole MODIFY Id DEFAULT seq.NEXTVAL;
INSERT INTO AppRole(Name, DisplayName) VALUES ('Foo','Foo');
db<>fiddle demo
Default with sequence is supported from Oracle 12c.
There should exist syntax in EntityFramework core that allow to do the following without relying on triggers(raw SQL query as last resort):
INSERT INTO AppRole(Id, Name, DisplayName) VALUES (seq.NextVal, 'Foo','Foo');
Sequences
Basic usage
You can set up a sequence in the model, and then use it to generate
values for properties: C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasSequence<int>("OrderNumbers");
modelBuilder.Entity<Order>()
.Property(o => o.OrderNo)
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
}
Note that the specific SQL used to generate a value from a sequence is
database-specific; the above example works on SQL Server but will fail
on other databases. Consult your specific database's documentation for
more information.
Oracle syntax is sequence_name.NEXTVAL.

Related

FireDAC "table or view does not exist" when insert into ORACLE TABLE Delphi Belin 10.1 upd 2

We are migrating our codebase from Delphi XE3 with FireDAC 8.0.5 to Delphi Berlin 10.1 Upd 2 with FireDAC 15.0.1 (Build 86746). Everything is working smoothly using MS Sql Server, but using ORACLE it has been another history.
Throughout the application source code we use lots of TAdQuery with sql instructions like
AdQuery1.Sql.Text := 'SELECT FIELD1, FIELD2 FROM TABLE1';
In order to insert a record, we use Append or Insert methods, like this
AdQuery1.Insert;
//or
AdQuery1.Append;
Just after invoking its Post method, the component internally creates an INSERT sql statement, that goes like this
INSERT INTO TABLE1 (FIELD1, FIELD2) VALUES(:FIELD1, :FIELD2)
So the record gets inserted successfully.
Now, using TFdQuery in Delphi Berlin, the component internally creates an INSERT sql statement, like this
INSERT INTO USERNAME.TABLE1 (FIELD1, FIELD2) VALUES(:FIELD1, :FIELD2)
Failing with a [FireDAC][Phys][Ora] ORA-00942: table or view does not exist
This happens because in our Oracle database, TABLE1 is created in a schema called MAIN_SCHEMA, and we access it by using a public synonym.
Trying to find a workaround, we compared FireDAC source code, finding that
in Delphi XE3, the unit uADDAptManager.pas, on its function TADDAptTableAdapter.GetUpdateRowCommand, calls oConn.CreateCommandGenerator(oCmdGen, nil);
in Delphi Berlin, the unit FireDAC.DApt.pas, on its function TFDDAptTableAdapter.GetUpdateRowCommand
calls oConn.CreateCommandGenerator(oCmdGen, GetSelectCommand);
Whenever that second parameter (called ACommand: IFDPhysCommand) is not nil, the name of the table is returned concatenating the user name (in a function called TFDPhysCommandGenerator.GetFrom).
If we add 'MetaCurSchema=MAIN_SCHEMA' to the TFdConnection params, it works with the applications that not use a pooled connection, but We have several process that use a pooled connection with the same params, even MetaCurSchema param, but it doesn't work
What can we do?
thanks for your help
What I understand is that you would do better making the connection avoid the use of any schema name, rather than specifying it. Also, keeping in mind that you already use public synonyms.
So, according to the documentation:
Full object names
FireDAC supports full object names, which include the catalog and/or schema names.
When a short object name is specified to StoredProcName, TableName, etc, they will be expanded into the full object names, using the current catalog and/or schema names. To override or avoid usage of the current catalog and/or schema names, use the MetaCurCatalog and MetaCurSchema connection definition parameters. For example:
[Oracle_Demo]
DriverID=Ora
...
MetaCurCatalog=*
MetaCurSchema=*
~ Source: Object Names (FireDAC) - docWiki
MetaCurSchema
Specifies the current schema for the application. If not specified, then its value will be received from the DBMS. When an application is asking for metadata and do not specify a schema name, then FireDAC will implicitly use the current schema.
If MetaCurSchema is '*', then schema names will be me omitted from the metadata parameters.
~ Source: Common Connection Parameters (FireDAC) - docWiki
That asterisk (*) should do the trick, let us know if that's the case.

Oracle Alter command to rename existing Column errorring

alter table tablename rename column zl_divn_nbr to div_loc_nbr;
Error while executing the above statement. Please help.
SQL Error: ORA-54032: column to be renamed is used in a virtual column expression
54032. 0000 - "column to be renamed is used in a virtual column expression"
*Cause: Attempted to rename a column that was used in a virtual column
expression.
*Action: Drop the virtual column first or change the virtual column
expression to eliminate dependency on the column to be renamed
Run the following SQL query in your database using the table name mentioned in the error message. For example, in the error message shown in this article, the table name is 'tablename'. Note that whilst the table name appears in lower case in the error message, it may be upper case in your DB. This query is case sensitive so if you receive no results, check whether the table name is upper case inside your database.
SELECT COLUMN_NAME, DATA_DEFAULT, HIDDEN_COLUMN
FROM USER_TAB_COLS
WHERE TABLE_NAME = 'tablename';
Before proceeding, make sure the Bitbucket Server process is not running. If Extended Statistics has been enabled, contact your database administrator to have them drop the Extended Statistics metadata from the table, and proceed with your upgrade. If you wish to enable Extended Statistics again after the upgrade you may do so, however be aware that you may need to repeat this process again for subsequent upgrades otherwise you risk running into this issue again.
Removing columns created by Extended Statistics requires using an in-build stored procedure,
DBMS_STATS.DROP_EXTENDED_STATS().
Usage of this stored procedure is covered further in ORA-54033 and the Hidden Virtual Column Mystery, and looks similar to the following:
EXEC DBMS_STATS.DROP_EXTENDED_STATS(ownname=>'<YOUR_DB_USERNAME>', tabname=>'tablename', extension=>'("PR_ROLE", "USER_ID", "PR_APPROVED")')
References
Database Upgrade Eror: column to be rename
Thanks.
Probably, you have such a table :
CREATE TABLE tablename(
id NUMBER,
zl_divn_nbr NUMBER,
zl_divn_percent NUMBER GENERATED ALWAYS AS (ROUND(zl_divn_nbr/100,2)) VIRTUAL
);
where zl_divn_nbr column is used for a computation for virtual(zl_divn_percent) column.
To rename zl_divn_nbr, all referenced virtual columns to this column should be removed, and may be created later.
The syntax for defining a virtual column is this :
column_name [datatype] [GENERATED ALWAYS] AS (expression) [VIRTUAL]
Since version 11 R1, we have this property.
ALTER TABLE rename column to
In the case of tables with virtual or 'group extension columns' the above
statement returns an error before Oracle 12cR2. For Oracle 12cR2 or newer versions the above statement runs fine cause 'renaming column' command is decoupled from the group extension aspect.

Which strategy would be better related to the use of NEXTVAL and CURRVAL in Oracle?

I am developing a Online Registration System using JSP and Oracle where I need to give every successful registrant a unique registration number sequentially. For this I want to adopt the NEXTVAL facility of Oracle, but I am in a dilemma for which policy I would adopt.
Policy 1: First I will store the NEXTVAL of a sequence in the following way.
select seq_form.nextval slno from dual;
Then I will store the slno into a variable say
int slno;
Now I will use the slno for in the insert query when the user finally submits the form, like
insert into members(registration_no, name,...) values(slno, name, ...);
Here the registration_no is primary key.
Policy 2: In my second policy, I will run the insert the query first
insert into members(registration_no, name,...) values(seq_form.nextval, name, ...);
fetch the last inserted ID like
select seq_form.currval slno from dual;
And then store the same in some variable say
int slno;
And use the same to show it to the registrant. Now I can't come to a conclusion which is better in terms of safety and efficiency. Here, I must make it clear that, in both the cases, my intension is to give the user a unique sequential number after successful submission of the form and by safety I meant to say that the user should get the ID respect to his/her own web session. Please help me.
I suggest you do it slightly differently:
Create a BEFORE INSERT trigger on your MEMBERS table. Set REGISTRATION_NO column to SEQ_FORM.NEXTVAL in the trigger:
CREATE OR REPLACE TRIGGER MEMBERS_BI
BEFORE INSERT ON MEMBERS
FOR EACH ROW
BEGIN
:NEW.REGISTRATION_NO := SEQ_FORM.NEXTVAL;
END MEMBERS_BI;
Do NOT put REGISTRATION_NO into the column list in your INSERT statement - it will be set by the trigger so there's no need to supply any value for it:
Use the RETURNING clause as part of the INSERT statement to get back the value put into REGISTRATION_NO by the trigger:
INSERT INTO MEMBERS (NAME, ...)
VALUES ('Fred', ...)
RETURNING REGISTRATION_NO INTO some_parameter
If you are using oracle 12, you can use identity column.
Then use RETURNING to get auto-generated value back.
Go with the policy 2. Because you cant always be sure that the insert query will be successful. If the insert fails, your oracle sequence has been rolled forward and you lose a sequence.
it is a better idea to insert and then later fetch it into a variable.

ORMLite and Oracle - case sensitive column names

I have just started using ORMLite and was using at home to experiment on MySQL. Now I have decided to try using it on Oracle, but have noticed an issue with case sensitivity of column names.
When using the TableUtils.createTableIfNotExists() it appears to generate CREATE statements that wrap the table and column names in double quotes. For example:
CREATE TABLE "T_SUBURB" ("id" NUMERIC , "description" VARCHAR2(255)
NOT NULL , "gnaf" VARCHAR2(255) , PRIMARY KEY ("id") )
This means that when I am attempting to query the database in Oracle SQL Developer I have to use the double quotes to specify the table and column names. This doesn't seem to happen when using MySQL.
I must admit I am a SQL novice, however it doesn't seem natural to wrap every table or column name in double quotes when attempting to query them. Looking at the OracleDatabaseType implementation it would seem that the entity name is intentionally double quoted in this example.
Does anybody know of a way to turn this behaviour off?
I am currently running version 4.43 from maven-central and Oracle 11g. Cheers.
When using the TableUtils.createTableIfNotExists() it appears to generate CREATE statements that wrap the table and column names in double quotes.
That's correct. The challenge for ORMLite is that it has to protect against special characters or reserved-words as field and table names. Words like "index" or "create" might make fine field names but will cause invalid SQL.
However, according to my reading of the OracleDatabaseType is should be generating uppercase field names:
#Override
public boolean isEntityNamesMustBeUpCase() {
return true;
}
If your field is created as "description" then something is wrong. Does DESCRIPTION work instead? Is ORMLite generating your schema and using an Oracle JDBC URI? Something like:
jdbc:oracle:...
If you are not using a JDBC URI like that then ORMLite may not be using the Oracle database type to create your tables. If you need to force it to use Oracle, you can create your
ConnectionSource connectionSource =
new JdbcConnectionSource(databaseUrl, new OracleDatabaseType());
Hope this helps.

ORA-00907 while trying to create a table with automatic column

I'm attempting to create a table with an automatic column, the value of which is computed using a function I've defined. However, when I try to create the table I keep getting ora-00907: Missing right parenthesis. Can anyone help?
Here is the CREATE code:
CREATE TABLE NEW_EMP2 (
SSN CHAR(9),
EMP_NUM2 CHAR(5) automatic as newemp2id(SSN),
Fname VARCHAR2(15),
Lname VARCHAR2(15),
Bdate DATE
)
Here is the code for the function newemp2id:
CREATE OR REPLACE FUNCTION newemp2id (i_ssn NCHAR) RETURN NCHAR
IS
BEGIN
RETURN 'E'||(1000+SUBSTR(i_ssn,6,4));
END
Any help on this would be greatly appreciated, thanks!
UPDATE: I'm using Oracle Express Edition on a Windows Vista machine, in case that makes any difference.
I hadn't heard of the syntax prior to this, but all I could find is this PDF for Oracle RDB. RDB was/is a separate product for Oracle databases... Confirmed - not supported on 10g
Use a BEFORE INSERT trigger instead, because I don't believe the syntax you're using is valid for Oracle Express (10g effectively) - there's no mention in the CREATE TABLE or ALTER TABLE documentation.
I'm not fond of using triggers, I'd prefer to have a single stored procedure for inserting into given table(s) & only allow anyone to use the procedure rather than direct table access...
CREATE OR REPLACE TRIGGER newemp2_before_insert
BEFORE INSERT
ON new_mep2
FOR EACH ROW
BEGIN
-- Update created_by field to the username of the person performing the INSERT
:new.emp_num2 := newemp2id(new.ssn)
END;
Though frankly, this is overcomplicated when it could be handled in a view:
CREATE VIEW vw_emp AS
SELECT t.ssn,
'E'||(1000+SUBSTR(i_ssn,6,4)) AS emp_num2
FROM NEW_EMP2 t
What's an automatic column supposed to be? Did you mean a purely computed i.e. virtual column? Then your statement should look like this:
CREATE TABLE NEW_EMP2 (
SSN CHAR(9),
EMP_NUM2 CHAR(5) GENERATED ALWAYS AS ( newemp2id(SSN) ) VIRTUAL,
Fname VARCHAR2(15),
Lname VARCHAR2(15),
Bdate DATE
)
And your functions need to declared deterministic:
CREATE OR REPLACE FUNCTION newemp2id (i_ssn NCHAR) RETURN NCHAR DETERMINISTIC
IS
BEGIN
RETURN 'E'||(1000+SUBSTR(i_ssn,6,4));
END
If I'm not mistaken, virtual columns were introduced with Oracle 11g.
Oracle Express is Oracle 10g.
According to the manual (http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_7002.htm#i2095331) there is no "automatic" keyword and Oracle 10 has never supported "computed columns"
Oracle 11g supports virtual columns, but they are created using GENERATED ALWAYS, not even Oracle 11g has an automatic keyword
Why do you think this should work in Oracle?

Resources