Is it common to bind default values in 'CREATE TABLE' statements? - mysql

Should I exec directly (pseudo code)...
q = "CREATE TABLE `usermood` { `id` INT, `name` TEXT, `mood` VARCHAR DEFAULT 'gloomy' }";
exec(q);
...or bind to a (un)named placeholder?
q = "CREATE TABLE `usermood` { `id` INT, `name` TEXT, `mood` VARCHAR DEFAULT :mood }";
prepare(q);
bind(q, ":mood", 'gloomy');
exec(q);
I've never seen it in any example code.
It's less about the security of escaping (because I control the create statements) but rather about converting the value into a database compatible format (automatic selection of content representation by type).
I'm using MySQL as well as SQLite3.
Are there database drivers that don't support binding in create statements?
If anyone is interested: I'm using QSqlQuery with QVariant as value.

You would use parameter binding when:
You are using a value from an unknown source, and you want to protect against SQL injection.
You want to prepare a statement and execute it repeatedly using different values for the parameter.
Neither of these is likely for your CREATE TABLE example.
I have never used a parameter in any DDL statement.
P.S.: You can't set a DEFAULT for a TEXT column regardless of whether it's a bound parameter or a literal value in the DDL statement, but I'm guessing your example above is artificial.

SQLite explicitly forbids binding default values:
from the SQLite docs:
An explicit DEFAULT clause may specify that the default value is NULL, a string constant, a blob constant, a signed-number, or any constant expression enclosed in parentheses. A default value may also be one of the special case-independent keywords CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP. For the purposes of the DEFAULT clause, an expression is considered constant if it does contains no sub-queries, column or table references, bound parameters, or string literals enclosed in double-quotes instead of single-quotes.
(emphasis by me)

Related

How to make default blob an image in MySQL Workbench?

I know how to upload images to my database but how can I make a longblob's/blob's default an image from my computer instead of just being null?
The only way to set DEFAULT property to BLOB column (except NULL, of course) is to specify it as an expression.
If you want to set DEFAULT value as some image, then you may:
Load this image into BLOB column. For example
CREATE temptable (blob_column BLOB)
SELECT LOAD_FILE('image_filename') AS blob_column;
Convert loaded binary value to some textual representation which can be typed in the table definition. For example, use HEX() function
SELECT HEX(blob_column)
FROM temptable;
Long string literal will be obtained in the output.
Use obtained literal (copy-paste, avoid excess linebreaks/spaces/truncation/etc.) in the expression of the DEFAULT value of the BLOB column. Apply the expression which reverses the convertion applied at the step 2. Like
CREATE TABLE tablename ( ...
blob_column_name BLOB DEFAULT (UNHEX('long HEX literal')),
... );
Pay attention to the parenthesis which wraps the expression (UNHEX() function in shown example) - they're compulsory! Single DEFAULT UNHEX('long HEX literal') will produce syntax error.

Mysql set default value to a json type column

I heard that mysql version prior to 8.0.13 accept default value for json type column, so I using the cmd:
ALTER TABLE templates CHANGE COLUMN values JSON NOT NULL DEFAULT '{}' ;
but receive error:
Error Code: 1101. BLOB, TEXT, GEOMETRY or JSON column 'values' can't have a default value
So how do I fix it?
I'm using mysql version 8.0.19 and client tool Workbench
From version 8.0.13 onwards, the documentation says (emphasis is mine):
The BLOB, TEXT, GEOMETRY, and JSON data types can be assigned a default value only if the value is written as an expression, even if the expression value is a literal.
You can make your default an expression by surrounding the literal value with parentheses:
ALTER TABLE templates CHANGE COLUMN values JSON NOT NULL DEFAULT ('{}') ;
Or:
ALTER TABLE templates CHANGE COLUMN values JSON NOT NULL DEFAULT (JSON_OBJECT()) ;
Prior to version 8.0.13 of MySQL, it was not possible to set a default value on a JSON column, as the 8.0 documentation points out a few paragraphs later :
The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default value.
According to the laravel docs:
$table->json('movies')->default(new Expression('(JSON_ARRAY())'));
The default modifier accepts a value or an Illuminate\Database\Query\Expression instance. Using an Expression instance will prevent Laravel from wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns
https://laravel.com/docs/8.x/migrations#default-expressions
MySql syntax is a bit different than Oracle/Postgres, hence to make say JSON_Array as default, the query would be -
ALTER TABLE table_name ALTER column_name SET DEFAULT (JSON_ARRAY());
Further reference here

Entering default value of string in database

I am trying to enter a default value of a string row in my database, using ASP.NET Visual Studio. I am simply trying to have "NotSet" as default but I get this error when trying to update the database:
The name "NotSet" is not permitted in this context. Valid expressions
are constants, constant expressions, and (in some contexts) variables.
Column names are not permitted.
I still have trouble understanding what kind of values are permitted, though. The datatype is "nchar(10)" and nulls are allowed. There's nothing else to it.
Make sure NotSet is in quotes in your sql statement
'NotSet'

Convert MySQL to postgres and retain default value

I have a MySQL database that I wish to convert into Postgres. One issue I encountered is to convert tinyint(1) (synonym to boolean) columns into "true" boolean and retain the default value of the MySQL column which can be either 0 or 1 but in Postgres the respective values are true or false. The SQL I'm trying:
ALTER TABLE "payments" ALTER COLUMN "is_automatic" TYPE boolean USING CAST("is_automatic" as boolean);
The error message:
ERROR: default for column "is_automatic" cannot be cast automatically to type boolean
I would think it would be possible to cast this value somehow. Is this possible to do or do I have to manually add this to the migration script?
Edit: I realise I might have explained the issue a bit vaguely, sorry about that. I am using this script (https://github.com/lanyrd/mysql-postgresql-converter) to convert the MySQL database. The values are converted into "true" postgres boolean using this script just fine but the columns themselves that where originally booleans in MySQL (represented by tinyint(1)) gets their default value dropped. This happens on row 157 in the script and removing the "DROP DEFAULT" part of the command generates the error above, because it can't be casted (I guess). My question is better asked this way: In the process of converting a tinyint(1) column, can the default value be "remembered" and later applied again with a "SET DEFAULT" command?
The postgresql ALTER TABLE reference page has an example exactly covering this scenario:
.. when the column has a default expression that won't automatically
cast to the new data type:
ALTER TABLE foo
ALTER COLUMN foo_timestamp DROP DEFAULT,
ALTER COLUMN foo_timestamp TYPE timestamp with time zone
USING
timestamp with time zone 'epoch' + foo_timestamp * interval '1 second',
ALTER COLUMN foo_timestamp SET DEFAULT now();
So, you need to drop the old default, alter the type, then add the new default.
Note that the USING expression does not have any bearing on the default. It is purely used to convert existing values in the table. But in any case, there is no direct cast between integer and boolean, so you need a slightly more advanced USING expression.
Your statement might look like this:
ALTER TABLE payments
ALTER COLUMN is_automatic DROP DEFAULT,
ALTER COLUMN is_automatic TYPE BOOLEAN
USING is_automatic!=0,
ALTER COLUMN is_automatic SET DEFAULT TRUE;
The using expression might need a little tweaking, I am assuming here that your existing data has a value of 0 for false and something else for true.

phpmyadmin mySQL create column with value "AS" not happening

need:
to enter integer IP value in ip_n and use internal INET_NTOA() function of mysql to fill in ip_a column. but using phpMyAdmin, i am not even able to create the table correctly.
I did this:
but it is wrong: for IP: 192.168.1.1
the second column is:
inspiration comes from here: http://databaseblog.myname.nl/2011/07/working-with-ips-in-mysql-and-mariadb.html
You're trying to declare a default value for a column based on the contents of another column.
Unfortunately, You Can't Do That.®
It appears that the column is being given a DEFAULT value of a string literal. Perfectly valid to assign a string literal, in this case, 'INET_NTOA(ip_n)'. That may look like a reference to a MySQL function, but MySQL is seeing that as a literal.
If we ran an INSERT statement and allowed MySQL to assign the default value, we'd expect that exact literal to be stored in the column; and that's exactly what we'd expect to be returned in a SELECT.
(The behavior described as observed by OP.)
Note that MySQL only allows a literal value to be specified as the DEFAULT value for a VARCHAR column. Actually, this is true for all datatypes, with the exception of TIMESTAMP, which allows for CURRENT_TIMESTAMP as a default. (More recent releases support a similar type of default for DATETIME as well.) But for a VARCHAR column, default is either NULL or a literal.
To get a value automatically assigned to the column, you could use MySQL BEFORE INSERT trigger (and a BEFORE UPDATE trigger)
Something like this perhaps:
DELIMITER $$
CREATE TRIGGER mytable_bi
BEFORE INSERT ON mytable
FOR EACH ROW
BEGIN
SET NEW.ip_a = INET_NTOA(NEW.ip_n);
END$$
DELIMITER ;
If you want to keep the values "in sync", you'd want a corresponding BEFORE UPDATE trigger, in the event that the value of ip_n is changed by an UPDATE statement.