Problem with MYSQL (INSERT) - mysql

i have model question with text and token fields. Want to add data into this via scaffold.
This is my question_ controller
def create
# #question = Question.new(params[:question])
#question = Question.create(:text => params[:text], :security_token => Digest::SHA1.hexdigest(rand(1000000).to_s))
render :json => #question.to_ext_json(:success => #question.save)
end
When i press "ADD" button i get in console this:
Question Create (0.0ms) Mysql::Error: Column 'text' cannot be null: INSERT INTO `questions` (`created_at`, `updated_at`, `text`, `security_token`) VALUES('2011-04-05 09:07:37', '2011-04-05 09:07:37', NULL, 'bf44551f11ce202b88d521a1826ab6db4254ce55')
Why COlumn 'text' can't be null?

You created the text column for the questions table with a NOT NULL constraint, and params[:text] is probably nil.
Since you used the scaffolding form params[:question][:text] returns the contents for text, not params[:text]!

Because the column in the database table is defined as 'not null'?

You are passing an empty text value (NULL/nil) to a database field which has NOT NULL constraint defined. You need to either ensure, that text is never empty or release this constraint, allowing NULLable fiels in the MySQL database.

I would suggest that you add a validation in your model to verify if text is null. as such, you will be spared from this low level error.

This error is nothing to do with ruby or rails, its just because you have defined the column as not null (.. as everybody says... :D ) , You might want to check your migration to see if you defined the column as not null there..
cheers
sameera

Related

MySQL ERROR 1265: Data truncated for column

I can't figure out why I'm getting this message. I'm using MySQL Workbench and am editing the values in an ENUM field that connects to a dropdown choice in my app.
Everything seems to be fine. I've searched on this error and all I find refers to datatype mismatches but, in this instance, that's not possible with ENUM when feeding it an array of string values.
Here's the SQL
Executing:
ALTER TABLE `mydbase`.`average_monthly_expenses`
CHANGE COLUMN `expense_category` `expense_category` ENUM('Home', 'Healthcare', 'Child care', 'Groceries and supplies', 'Eating out', 'Utilities', 'Telecomms', 'Laundry and cleaning', 'Clothes', 'Education', 'Entertainment gifts vacation', 'Auto and transportation', 'Insurance', 'Savings and investments', 'Charitable contributions', 'Itemized monthly payments') NULL DEFAULT NULL ;
Operation failed: There was an error while applying the SQL script to the database.
ERROR 1265: Data truncated for column 'expense_category' at row 1
SQL Statement:
ALTER TABLE `mydbase`.`average_monthly_expenses`
CHANGE COLUMN `expense_category` `expense_category` ENUM('Home', 'Healthcare', 'Child care', 'Groceries and supplies', 'Eating out', 'Utilities', 'Telecomms', 'Laundry and cleaning', 'Clothes', 'Education', 'Entertainment gifts vacation', 'Auto and transportation', 'Insurance', 'Savings and investments', 'Charitable contributions', 'Itemized monthly payments') NULL DEFAULT NULL
Any suggestions are very welcome
The query itself is correct.
modelling fiddle
Execute
SELECT DISTINCT expense_category, HEX(expense_category)
FROM mydbase.average_monthly_expenses
and check the output for the values which are not listed in the column definition.
There may be typos, leading/trailing spaces or another non-printed symbols, double spaces in the middle of the value, or there may be some collation problems.
UPDATE
My current field definition I'm trying to change to the above is ENUM('Home', 'Living', 'Telecommunications', 'Transportation', 'Other'). When I run your suggested SQL I just get Housing and Other listed.
These values are absent in new column definition - so server cannot convert them and truncates the values.
Recommendations: alter column definition, add new values to ENUM values list but do not remove old ones; update table and replace old values with new ones; alter column definition and remove old values from the list.
#Akina
So I figured out why I was blocked from editing the values. It would not let me make any edits that changed either value "Housing" or "Other". I did as you suggested, adding my new values to the existing ones, no problem, that worked fine. I couldn't however delete "Housing" or "Other", but the other prior values deleted fine. For the moment, I kept both, using "Housing" instead of "Home" and leaving "Other" at the end.
But I wanted to know however why those two values were protected, and then it dawned on me, there were existing records using those values. I manually changed all instances using "Other" to "Telecomms" and then I could remove "Other" from the ENUM values. All good now.

Why would you set `null: false, default: ""` on a required DB column?

I'm building a Rails app with Devise for authentication, and its default DB migration sets the following columns:
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
What is the purpose of setting null: false and default: "" at the same time?
My understanding is that null: false effectively makes a value required: i.e., trying to save a record with a NULL value in that column will fail on the database level, without depending any validations on the model.
But then default: "" basically undoes that by simply converting NULL values to empty strings before saving.
I understand that for an optional column, you want to reject NULL values just to make sure that all data within that column is of the same type. However, in this case, email and password are decidedly not optional attributes on a user authentication model. I'm sure there are validations in the model to make sure you can't create a user with an empty email address, but why would you set default: "" here in the first place? Does it serve some benefit or prevent some edge case that I haven't considered?
Broadly speaking:
To make a column required, you must set null: false on it. This is true whether you are creating a new table or updating an existing one.
And in the event that you're updating an existing table, the DB engine will try to populate that new column with NULL in each row. In such cases, you must override this behavior with default: "", or else it will conflict with null: false and the migraiton will fail.
With respect to Devise:
Devise uses two separate templates for building migrations: migration.rb, for creating new tables, and migration_existing.rb, for updating existing tables (see source on GitHub). Both templates call the same migration_data method to generate the lines in question (i.e., the ones that specify null: false, default: ""), but as mentioned above, default: "" is only really relevant in the latter case (see O. Jones’ answer for more).
So the short answer to your question, specifically in the case of Devise migrations, is “because the generator uses borrowed code which doesn’t always apply, but still doesn’t break anything.”
A consideration for UNIQUE columns:
Note that in most popular SQL engines, uniquely indexed columns can still contain multiple rows of NULL values, as long as they are not required (naturally). But the effect of making a new column both required and unique (i.e., null: false, default: "", and unique: true) is that it cannot be added: the DB engine tries to populate the new column with an empty string in each row, which conflicts with the unique constraint and causes the migration to fail.
(The only scenario in which this mechanism fails is if you have exactly one row in your table — it gets a blank string value for the new column, which naturally passes the uniqueness constraint because it's the only record.)
So another way to look at it might be that these options are a safety mechanism preventing you from running migrations that you shouldn't (i.e., retroactively adding required columns to an already-populated table).
There is a difference in the insertion type. For example, let say you have a new_table table such that:
CREATE TABLE IF NOT EXISTS `new_table` (
`col1` VARCHAR(10) NOT NULL,
`col2` VARCHAR(10) NOT NULL DEFAULT '',
`col3` VARCHAR(10) NULL DEFAULT '');
When you use explicit insert of NULL you'll get the NULL:
INSERT INTO new_table(col1,col2,col3) VALUES('a','b',NULL);
'a','b',NULL
for col2 same trick will result in error:
INSERT INTO new_table(col1,col2,col3) VALUES('a',NULL,'c');
But when you use implicit insert of NULL you'll get the default value:
INSERT INTO new_table(col1,col2) VALUES('a','b');
'a','b',''
meaning that setting a default value is not preventing NULL assertion to this column, but only used when the value is not explicitly given.
Some application software gacks on NULL values but not on zero-length text strings. In Oracle, they're the same thing, but not in MySQL.
Things get interesting upon altering tables to add columns. In that case a default value is mandatory, so the DBMS can populate the new column.
I'm thinking this is here because of MySQL 'strict mode' not allowing you to disallow a null value without providing a default.
From mysql docs: https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
For data entry into a NOT NULL column that has no explicit DEFAULT clause, if an INSERT or REPLACE statement includes no value for the column, or an UPDATE statement sets the column to NULL, MySQL handles the column according to the SQL mode in effect at the time: If strict SQL mode is enabled, an error occurs for transactional tables and the statement is rolled back. For nontransactional tables, an error occurs, but if this happens for the second or subsequent row of a multiple-row statement, the preceding rows are inserted. If strict mode is not enabled, MySQL sets the column to the implicit default value for the column data type.

Update empty string to NULL in a html form

I'm building a site in Laravel.
I have foreign key constraints set up among InnoDB tables.
My problem is that if i don't select a value in a, say, select box, the framework tries to insert or update a record in a table with '' (empty string). Which causes a MySQL error as it cannot find the equivalent foreign key value in the subtables.
Is there some elegant way to force the insertion of NULL in the foreign key fields other than checking out every single field? Or to force MySQL to accept '' as a "null" foreign key reference?
In other words: I have a, say, SELECT field with first OPTION blank. I leave the blank OPTION chosen. When I submit, an empty string '' is passed. In MySQL apparently I can do UPDATE table SET foreignKey=NULL but not UPDATE table SET foreignKey=''. It does not "convert" to NULL. I could check the fields one by one but and convert '' to NULL for every foreign key, maybe specifying all of them in an array, I was wondering if there's a more streamlined way to do this.
Maybe have to change my ON UPDATE action (which is not set) in my DB schema?
Edit: the columns DO accept the NULL value, the problem is in how the framework or MySQL handle the "empty value" coming from the HTML. I'm not suggesting MySQL "does it wrong", it is also logical, the problem is that you can't set a "NULL" value in HTML, and I would like to know if there's an elegant way to manage this problem in MySQL or Laravel.
In other words, do I have to specify manually the foreign keys and construct my query accordingly or is there another robust and elegant way?
My code so far for the model MyModel:
$obj = new MyModel;
$obj->fill(Input::all())); // can be all() or a subset of the request fields
$obj->save();
At least since v4 of Laravel (and Eloquent models), you can use mutators (aka setters) to check if a value is empty and transform it to null, and that logic is nicely put in the model :
class Anything extends \Eloquent {
// ...
public function setFooBarAttribute($value) {
$this->attributes['foo_bar'] = empty($value)?null:$value;
}
}
You can check out the doc on mutators.
I've been oriented by this github issue (not exactly related but still).
Instead of using
$obj = new MyModel;
$obj->fill(Input::all())); // can be all() or a subset of the request fields
$obj->save();
Use
$obj = new MyModel;
$obj->fieldName1 = Input::get('formField1');
$obj->fieldName2 = Input::has('formField2') && Input::get('formField2') == 'someValue' ? Input::get('formField2') : null;
// ...
$obj->save();
And make sure your database field accepts null values. Also, you can set a default value as null from the database/phpmyadmin.
You must remove the "not null" attribute from the field that maps your foreign key.
In the model add below function.
public function setFooBarAttribute($value)
{
$this->attributes['foo_bar'] = $value?:null;
}

Field 'class' doesn't have a default value

In our app, I have a class describing a person (eye and hair colour, height, etc). Once the form of registering is fulfilled, I click in the submit button, and an exception is thrown:
Class: java.sql.SQLException
Message: Field 'class' doesn't have a default value
I am scratching my head about this, because none of the classes have a field named class, for obvious reasons (it's a reserved word), and in the database there is no column named class, neither.
Any idea why this happens? And how to fix it?
EDIT:
I tried this:
classThrowingExceptionInstance.class=ClassThrowingException
And now it says Cannot set readonly property: class for class ClassThrowingException
It seems that it is MySQL error 1364 - Message: Field '%s' doesn't have a default value.
Check the table which you modify. Are there any fields without default values? Also analyze INSERT and UPDATE statements if they do not post these field values.
To fix this error:
modify table - set DEFAULT values for fields you need
or
pass concrete values to these fields in INSERT/UPDATE statements.

How do I use a Rails ActiveRecord migration to insert a primary key into a MySQL database?

I need to create an AR migration for a table of image files. The images are being checked into the source tree, and should act like attachment_fu files. That being the case, I'm creating a hierarchy for them under /public/system.
Because of the way attachment_fu generates links, I need to use the directory naming convention to insert primary key values. How do I override the auto-increment in MySQL as well as any Rails magic so that I can do something like this:
image = Image.create(:id => 42, :filename => "foo.jpg")
image.id #=> 42
Yikes, not a pleasant problem to have. The least-kludgy way I can think of to do it is to have some code in your migration that actually "uploads" all the files through attachment-fu, and therefore lets the plugin create the IDs and place the files.
Something like this:
Dir.glob("/images/to/import/*.{jpg,png,gif}").each do |path|
# simulate uploading the image
tempfile = Tempfile.new(path)
tempfile.set_encoding(Encoding::BINARY) if tempfile.respond_to?(:set_encoding)
tempfile.binmode
FileUtils.copy_file(path, tempfile.path)
# create as you do in the controller - may need other metadata here
image = Image.create({:uploaded_data => tempfile})
unless image.save
logger.info "Failed to save image #{path} in migration: #{image.errors.full_messages}"
end
tempfile.close!
end
A look at attachment-fu's tests might be useful.
Unlike, say Sybase, in MySQL if you specify the id column in the insert statement's column list, you can insert any valid, non-duplicate value in the id. No need to do something special.
I suspect the rails magic is just to not let rails know the id is auto-increment. If this is the only way you'll be inserting into this table, then don't make the id auto_increment. Just make in an int not null primary key.
Though frankly, this is using a key as data, and so it makes me uneasy. If attachment_fu is just looking for a column named "id", make a column named id that's really data, and make a column named "actual_id" the actual, synthetic, auto_incremented key.
image = Image.create(:filename => "foo.jpg") { |r| r.id = 42 }
Here's my kluge:
class AddImages < ActiveRecord::Migration
def self.up
Image.destroy_all
execute("ALTER TABLE images AUTO_INCREMENT = 1")
image = Image.create(:filename => "foo.jpg")
image.id #=> 1
end
def self.down
end
end
I'm not entirely sure I understand why you need to do this, but if you only need to do this a single time, for a migration, just use execute in the migration to set the ID (assuming it's not already taken, which I can't imagine it would be):
execute "INSERT INTO images (id, filename) VALUES (42, 'foo.jpg')"
I agree with AdminMyServer although I believe you can still perform this task on the object directly:
image = Image.new :filename => "foo.jpg"
image.id = 42
image.save
You'll also need to ensure your id auto-increment is updated at the end of the process to avoid clashes in the future.
newValue = Images.find(:first, :order => 'id DESC').id + 1
execute("ALTER TABLE images AUTO_INCREMENT = #{newValue}")
Hope this helps.