Automatically delete not-null constraints on unused columns after migration - constraints

I have a model which is used in combination with GORM:
type User struct {
gorm.Model
Name string
Age uint
}
When I now want to exchange the Name field with FirstName and LastName using the GORM Automigrate command I get the following error on the next request:
ERROR: null value in column "name" violates not-null constraint (SQLSTATE 23502)
Obviously, the AutoMigrate does not destroy the Name column in the user table (as stated in the docs), but it also does not destroy the not-null constraint, which makes the table useless after migration.
How can I automatically destroy the not-null constraints on old columns?

It can't be done automatically as far as I can see.
As you note, gorm will not delete old columns in the table so in essence once you delete the field, gorm forgets about the column. What's clear is that at some point you had tagged your Name field with gorm:"not null" or that constraint wouldn't have been put on.
So one way you might reverse that would be to reinstate the old field, but remove the not null tag, and run the migration. That will remove the not null constraint. Then remove the field, and run the migration again. Not automatic by any means.
You might also look at using the migrator to create your own migration script that drops the constraint or even drops the column entirely.
In my humble opinion Gorm's auto-migrations is a feature that for now is only good for quick prototyping. I don't think it can replace a proper migration system in anything more than a toy app; sooner or later you run into these problems that force you to write careful migration scripts yourself. Packages to look at for that include github.com/pressly/goose or github.com/golang-migrate/migrate.

Related

Tying table records together in SQLite3

I am currently working on a database structure in SQLite Studio (not sure whether that's in itself important, but might as well mention), and error messages are making me wonder whether I'm just going at it the wrong way or there's some subtlety I'm missing.
Assume two tables, people-basics (person-ID, person-NAME, person-GENDER) and people-stats (person-ID, person-NAME, person-SIZE). What I'm looking into achieving is "Every record in people-basics corresponds to a single record in people-stats.", ideally with the added property that person-ID and person-NAME in people-stats reflect the associated person-ID and person-NAME in people-basics.
I've been assuming up to now that one would achieve this with Foreign Keys, but I've also been unable to get this to work.
When I add a person in people-basics, it works fine, but then when I go over to people-stats no corresponding record exists and if I try to create one and fill the Foreign Key column with corresponding data, I get this message: "Cannot edit this cell. Details: Error while executing SQL query on database 'People': no such column: people-basics.person" (I think the message is truncated).
The DDL I currently have for my tables (auto-generated by SQLite Studio based on my GUI operations):
CREATE TABLE [people-basics] (
[person-ID] INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE
NOT NULL,
[person-NAME] TEXT UNIQUE
NOT NULL,
[person-GENDER] TEXT
);
CREATE TABLE [people-stats] (
[person-NAME] TEXT REFERENCES [people-basics] ([person-NAME]),
[person-SIZE] NUMERIC
);
(I've removed the person-ID column from people-stats for now as it seemed like I should only have one foreign key at a time, not sure whether that's true.)
Alright, that was a little silly.
The entire problem was solved by removing hyphens from table names and column names. (So: charBasics instead of char-basics, etc.)
Ah well.

Lazarus fails to generate proper InsertSQL, how to remedy?

I'm using Lazarus and trying to insert payment records into a SQLite database, but apparently InsertSQL isn't autogenerating the correct INSERT statement, and I have discovered that I can't simply assign parameters to .InsertSQL by using .ParamByName like I can to the .SQL property.
My database has tables of Customers and Payments. The Payments table is as follows:
Pay_Key: Primary key, Integer, Unique, Not NULL. Identifies a single payment row in the table.
Pay_Customer: Integer, Not NULL. Foreign-key linked to an integer-type Not NULL primary key in my Customers table.
Pay_Sum: Integer. (Yes, I'm storing the payment sum as integer, but this isn't really important here.)
I'm using SELECT * FROM Payments WHERE Pay_Customer=:CustomerKey in my SQLQuery.SQL, and assigning :CustomerKey each time programmatically via SQLQuery.ParamByName('CustomerKey').Text. This lets me navigate existing records nicely in a DBGrid, but when I try to insert a new payment, the operation fails on the "Pay_Customer Not NULL" condition. Apparently Lazarus doesn't know what value to use in the Pay_Customer field, since I passed it programmatically.
Is there a way to remedy this? I can write my own InsertSQL if need be, I just don't understand how I can pass this customer parameter to it. I would very much like to use InsertSQL/UpdateSQL/DeleteSQL, since they would make it easy to use stock DBGrid/DBNavigator components and logic for what I'm doing.
Thanks for any suggestions, and sorry for being so verbose about my question.
-Sam
Edit: I'm using Lazarus 1.6 (FPC 3.0.0). I have enabled foreign keys in SQLite in SQLite3Connection.Params (foreign_keys=on)

Invalid Column Name : SQL / ASP.NET

I'm having a hard time debugging a particular problem and have a couple questions. First, here is what's going on:
I have a relatively simple table called Employees, which has a primary key / identity Id. There is also a Username column - which is a GUID foreign key to my aspnet_Users table used for membership. Finally, there is another foreign key Team_Id which points to another table, Teams.
All I'm really trying to do is give a selected employee's Id and pass it to a method in the DAL which then finds the employee with the following statement:
var employee = entities.Employees.Where(emp => emp.Id == employeeId);
Once the employee is retrieved, I want to use another value which is passed to the same method - the selected team's Id - to update the employee's Team_Id value (which team they are assigned to), using the following:
employee.First().Team_Id = teamId;
entities.SaveChanges();
I get the exception
Invalid column name: {Name}
which doesn't make sense to me, because Employee doesn't have a name column.
All of that said, my questions are:
Where could the mix up possibly be coming from? I've tried thinking up a way to step through the code, but it seems like the error is somewhere in the query itself so I'm not really sure how to trace the execution of the query itself.
Is it possible that it may have something to do with my generated Entities? I noticed that when I type employee.First(). Name comes up in Intellisense. I'm really confused by that, since as I've mentioned there is no Name column in the employees table.
Fixed the issue. I just removed the existing Entity Framework Model and re-added it.
As far as the query goes, you can always use SQL Profiler to watch what scripts are actually running. That's a good way to troubleshoot generated SQL anyway.
For your property, somehow that did make it to your class, so your data model thinks it's there, for whatever reason. I'd say just go to your data model (you don't mention if this this is EF or LINQ-to-SQL), and you'll see "Name" there. Just remove it, and it will remove it from the class, and from the data access stuff.

asp.net Entity Framework/ Update from database/ The table/view does not have a primary key defined and no valid primary key could be inferred

One of the database view I am trying to import using entity framework contains only two columns, one is an integer type of column and another one is an aggregate function. I am getting the following error.
The table/view does not have a primary key defined and no valid primary key could be inferred. This table/view has been excluded. To use the entity, you will need to review your schema, add the correct keys, and uncomment it.
I understand it is a known scenario and it can be fixed by either including a Key column in the view or modifying the edmx file manually.
I just wanted to know if there is some other solution other than the above two? I do not want to include an additional column in my query and making changes in edmx is not feasible as DB changes are very frequent and the edmx will be overwritten every time I update from db.
You can mark both properties as entity key directly in the designer but you must ensure that the composite value of these two properties will be always unique. If you cannot ensure that you must add another unique column anyway or you may have some other problems when working with such entity set.

parsing SQLException errors in an ASP.NET (MVC) application?

While validation can prevent most SQL errors, there are situations that simply cannot be prevented. I can think of two of them: uniqueness of some column and wrong foreign key: validation cannot be effective as the an object can be created or deleted by other parties just after validation and before db insertion. So there are (at least) two SQL errors that should lead to a message of invalid user input.
SQLException has a Number property for the error type, but I don't know how to find out which column is duplicated or which foreign key is wrong without trying to parse the actual error message text, which happens to be localized.
Is there any way to identify the offending column other than parsing the error message (which means at least to strictly choose a language for SQL Server and always use it)?
edit:
I should mention that I come from RubyOnRails, where the approach is: let's pretend that the db doesn't exist: no constraints, no db-enforced foreign keys etc. As I'm approaching ASP.NET MVC, I'd like to get rid of the rails biases, and accept the fact that the db indeed exists.
Are you sure these two situations absolutely cannot be prevented?
You can avoid unique constraint SQLexceptions on Insert, by using an Identity (database generated) primary key column. SQL Server will guarantee that the value is unique.
The same goes for inserting related rows into tables linked by a foreign key. Insert a row in each referenced table first, before inserting a row in the main table. Use IDENTITY_INSERT to get the value of each auto generated primary key and use this as the foreign key in your main table.
You should also wrap these individual statements in a transaction to ensure that either all tables are inserted successfully or none are. The transaction also isolates (hides) these changes from all other concurrent database accesses until the transaction is committed.

Resources