Storing a byte array into MySQL binary column with JdbcTemplate - mysql

I have a MySQL table (simplified):
CREATE TABLE `tokens` (
`token` BINARY(16) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And I try to insert a row like this:
JdbcTemplate jdbcTemplate; // org.springframework.jdbc.core.JdbcTemplate
...
String sql = "INSERT INTO `tokens` (`token`) VALUES (?)";
String token = "123e4567e89b12d3a456426655440000"; // UUID
jdbcTemplate.update(sql, new Object[]{token.getBytes()});
But I get this exception:
Data truncation: Data too long for column 'token' at row 1
What do I do wrong? Thanks.
Edit: see my answer, I missed the hexadecimal conversion (token is an UUID).

Here is the solution to store the token (which is an UUID):
jdbcTemplate.update(sql, new Object[]{DatatypeConverter.parseHexBinary(token)});
Let us know if there are other ways...

Related

Incorrect datetime value output (without fractional part) using MATLAB from a MySQL table

I have a datetime(6) column in MySQL database with fractional seconds. For example, an example entry in my database has the time value: 2022-12-22 12:29:04.602000.
CREATE TABLE `test_data` (
`sampletime_utc` datetime(6) DEFAULT NULL,
`project_uuid` int(11) DEFAULT NULL,
`item_id` int(11) DEFAULT NULL
);
When we read this time in MATLAB, the default type for the time variable is char which looses the fractional seconds part. I found the following solution to the problem:
https://www.mathworks.com/matlabcentral/answers/602656-sql-datetime-query-is-there-a-faster-way?s_tid=prof_contriblnk
The source code in the above answer is given below:
datasourceName = "mysqlJdbc"; % The name of JDBC datasource for MySQL
username = "USERNAME";
password = "PASSWORD";
conn = database(datasourceName, username, password);
opts = databaseImportOptions(conn, tablename);
columnNames = {'col1', 'col2'}; % The column names you want to change the import options
opts = setoptions(opts, columnNames, 'Type', 'datetime'); % Change from char to datetime
sqlquery = "SELECT * FROM YOUR_TABLENAME";
data = fetch(conn, sqlquery, opts, 'MaxRows', 10);
The above solution works only for a simple select statement. If I use a complicated statement like with where and and clauses, the following error is thrown:
Error using database.jdbc.connection/fetch (line 197)
Unable to use database import options with
'SELECT data_value, sampletime_utc FROM db1.tb1 dd WHERE (dd.project_uuid = 'b7d0-cf70b35e32cb' AND dd.item_id = 131 AND dd.sampletime_utc >= '2022-04-22 20:45:52.000' and dd.sampletime_utc <= '2022-04-22 21:45:52.000' ) ORDER BY dd.sampletime_utc ASC'.
Can anyone please guide how to get the time values from mysql table with fractional seconds or as a datetime variable in MATLAB.

JSONExtractInt64 null extract from string into clickhouse

I have a source table:
CREATE TABLE test.source
(
text_json String
)
engine = Memory;
I have a destination table:
CREATE TABLE test.destination
(
column Nullable(Int64)
)
engine = Memory;
I insert to the source:
INSERT INTO test.source (text_json) FORMAT JSONAsString {"column": null};
Then I try to parse json int value and insert into destination
INSERT INTO test.destination (column)
SELECT JSONExtractInt(text_json) FROM test.source;
However, it will insert 0. And it will be a non-deterministic behavior as if I have a real zero beforehand, it will be impossible to differentiate between NULL and 0.
How to parse from JSONString null value into Int64 column AS Null?
You can use JSON_VALUE to extract the string value and pass that to INSERT instead (with implicit or explicit casting):
INSERT INTO test.destination (column)
SELECT JSON_VALUE(text_json, '$.column')
FROM test.source
The result will be NULL:
SELECT * FROM test.destination
┌─column─┐
│ ᴺᵁᴸᴸ │
└────────

Python Maria DB MYSQL Type Cast Error Incorrect double value: ''237'' for column `wtx520`.`weather`.`value` at row 1

I have a Raspi Python project where I want to store values of a Weather sensor in a DB. The values come in a String. I was able to separate it, but I make a stupid mistake and can not store the values.
Code
mport mysql.connector
import re
db =mysql.connector.connect(host='localhost', port=3306, db='wtx520', password='xxx', user='xxx')
#port 3307 für synology
#example string
line ="0r1,Dn=237D,Dm=237D,Dx=237D,Sn=0.1N,Sm=0.2N,Sx=0.3NLq#"
pair = line.split(",")
for i in pair:
value = i.split("=")
print(value[0])
if len(value)>1:
decvalue =re.findall("\d*\.?\d+", value[1])
key= value[0]
#remove brackets around dec value
inserttuple=(key,str(decvalue).replace('[','').replace(']',''))
#inserttuple=(str(key), "237") it works with this static example
sql_insert_query = """INSERT INTO weather (valuekey, value) VALUES (%s,%s)"""
mycursor = db.cursor()
result = mycursor.execute(sql_insert_query,inserttuple)
db.commit()
DB Table definition:
CREATE TABLE wtx520.weather (
ts TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
value DOUBLE DEFAULT NULL,
valuekey VARCHAR(4) DEFAULT NULL
)
ENGINE = INNODB,
AVG_ROW_LENGTH = 1365,
CHARACTER SET utf8,
COLLATE utf8_general_ci;
Error:
Nachricht = 1366 (22007): Incorrect double value: ''237'' for column wtx520.weather.value at row 1
Quelle =
Stapelüberwachung:
File "C:\Users\Marcel\source\repos\wtx520-azure\wtx520-azure\wtx520_azure.py", line 22, in
result = mycursor.execute(sql_insert_query,inserttuple)
any help apprechiated
Solution is:
inserttuple=(key,str(decvalue).replace('[','').replace(']','').replace("'",''))
thanks for the hint!

Unable to insert data into MYSQL due to UTC

I am trying to use FullCalendar v4 and cannot post data to MySQL. I have narrowed the problem down to $start and $end having UTC on the end and MySQL won't take it even though my datatype is TIMESTAMP. If I manually assign standard datetime data (without UTC) to $start and $end it will post to table. I have the statements commented out in the event.php that work, by overriding the data in the $POST.
My thought is I have something askew in MySQL that is causing the TIMESTAMP datatype to actually be a DATETIME datatype. I have deleted and created the table with SQL Statement shown below.
Running -> MySQL 8.01.5, Windows Server 2016, PHP 7.2.7, using jQuery
...
CREATE TABLE calendarsystem.events (
id int(11) NOT NULL AUTO_INCREMENT,
title VARCHAR(45) NOT NULL ,
start TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
end TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
resourceId VARCHAR(45) NOT NULL ,
PRIMARY KEY (id)
);
...
The code to add_event.php:
<?php
$title = $_POST['title'];
$start = $_POST['start'];
$end = $_POST['end'];
$resourceId = $_POST['resourceId'];
//$title = 'wtf';
//$start = '2019-03-25 16:00:00';
//$end = '2019-03-25T17:00:00';
//$resourceId = 'b';
try {
require "db_config.php";
} catch(Exception $e) {
exit('Unable to connect to database.');
}
$sql = "INSERT INTO events (title, start, end, resourceId) VALUES (:title, :start, :end, :resourceId )";
$q = $bdd->prepare($sql);
q->execute(array(':title'=>$title,':start'=>$start,':end'=>$end,':resourceId'=>$resourceId));
?>
...
If I open MySQL Workbench and try to add the data with the UTC copied from the output window of chrome I get the following error when applying:
Operation failed: There was an error while applying the SQL script to the database.
Executing:
INSERT INTO calendarsystem.events (start, end, title, resourceId) VALUES ('2019-03-25T14:00:00-05:00', '2019-03-25T15:00:00-05:00', 'xxx', 'b');
ERROR 1292: 1292: Incorrect datetime value: '2019-03-25T14:00:00-05:00' for column 'start' at row 1
SQL Statement:
INSERT INTO calendarsystem.events (start, end, title, resourceId) VALUES ('2019-03-25T14:00:00-05:00', '2019-03-25T15:00:00-05:00', 'xxx', 'b')
Sorry the post formatting is crappy
I think MySQL isn't recognizing the 'T' character or the trailing time offset in the string value.
'2019-03-25T15:00:00-05:00'
^ ^^^^^^
One way to fix the problem would be to remove that T character and the offset
'2019-03-25 15:00:00'
^
We expect MySQL to recognize a string in that format.
Alternatively, we could use STR_TO_DATE function with appropriate format model so MySQL can interpret datetime/timestamp values from strings in different formats.
The relevant topic in the MySQL Referenced Manual is here:
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-literals.html

What's the best way to structure a log table which shows differently in each view?

I need to design the structure of a table that is going to store a log of events/actions for a project management website.
The problem is, these logs will be spelled differently depending on what the user is viewing
Example:
On the overview, an action could say "John F. deleted the item #2881"
On the single-item page, it would say "John F. deleted this item"
If the current user IS John F. it would spell "You deleted this item"
I'm not sure if I should store each different possibility in the table, this doesn't sound like the optimal approach.
For any kind of logs you can use the following table structure
CREATE TABLE logs (
id bigint NOT NULL AUTO_INCREMENT,
auto_id bigint NOT NULL DEFAULT '0',
table_name varchar(100) NOT NULL,
updated_at datetime DEFAULT NULL,
updated_by bigint NOT NULL DEFAULT '0',
updated_by_name varchar(100) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3870 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
And then create another table to record what columns were updated exactly.
CREATE TABLE logs_entries (
id bigint NOT NULL AUTO_INCREMENT,
log_id bigint NOT NULL DEFAULT '0',
field_name varchar(100) NOT NULL,
old_value text,
new_value text,
PRIMARY KEY (id),
KEY log_id (log_id),
CONSTRAINT logs_entries_ibfk_1 FOREIGN KEY (log_id) REFERENCES logs (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=7212 DEFAULT CHARSET=utf8mb3
Now your table data look like this
Now Create a Database-View to fetch data easily in simple query
DELIMITER $$
ALTER ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW view_logs_entries AS
SELECT
le.id AS id,
le.log_id AS log_id,
le.field_name AS field_name,
le.old_value AS old_value,
le.new_value AS new_value,
l.auto_id AS auto_id,
l.table_name AS table_name,
l.updated_at AS updated_at,
l.updated_by AS updated_by,
l.updated_by_name AS updated_by_name
FROM (logs_entries le
LEFT JOIN logs l
ON ((le.log_id = l.id)))$$
DELIMITER ;
After creating database-view your data will look like this, so now you can easily query any logs of your project
You must have noticed that I have added updated_by and updated_by_name columns in logs table, Now there are two ways to fill this updated_by_name column
You can write a query on every log entry to fetch user name and store it(not recommended)
You can make a database-trigger to fill this column (Recommended)
You can create database trigger like this, it will automatically insert user name whenever logs entry added in database.
DELIMITER $$
USE YOUR_DATABASE_NAME$$
CREATE
TRIGGER logs_before_insert BEFORE INSERT ON logs
FOR EACH ROW BEGIN
SET new.updated_by_name= (SELECT fullname FROM users WHERE user_id = new.updated_by);
END;
$$
DELIMITER ;
After doing all this, you can insert entries in logs table whenever you do changes in any database table. In my case I have PHP-CodeIgniter project, and I did it like this in my Model file, here I am updating patient table of my database
public function update($id,$data)
{
// Log this activity
$auto_id = $id;
$table_name = 'patients';
$updated_by = #$data['updated_by'];
$new_record = $data;
$old_record = $this->db->where('id',$auto_id)->get($table_name)->row();
$data['updated_at'] = date('Y-m-d H:i:s');
$this->db->where('id', $id);
$return = $this->db->update($table_name,$data);
//my_var_dump($this->db->last_query());
if($updated_by)
{
$this->log_model->set_log($auto_id,$table_name,$updated_by,$new_record,$old_record);
}
return $return;
}
set_log function code checks which fields are actually changed
public function set_log($auto_id,$table_name,$updated_by,$new_record,$old_record)
{
$entries = [];
foreach ($new_record as $key => $value)
{
if($old_record->$key != $new_record[$key])
{
$entries[$key]['old_value'] = $old_record->$key;
$entries[$key]['new_value'] = $new_record[$key];
}
}
if(count($entries))
{
$data['auto_id'] = $auto_id;
$data['table_name'] = $table_name;
$data['updated_by'] = $updated_by;
$this->insert($data,$entries);
}
}
and insert log function look like this
public function insert($data,$entries)
{
$data['updated_at'] = date('Y-m-d H:i:s');
if($this->db->insert('logs', $data))
{
$id = $this->db->insert_id();
foreach ($entries as $key => $value)
{
$entry['log_id'] = $id;
$entry['field_name'] = $key;
$entry['old_value'] = $value['old_value'];
$entry['new_value'] = $value['new_value'];
$this->db->insert('logs_entries',$entry);
}
return $id;
}
return false;
}
You need to separate data from display. In your log, store the complete information (user xxx deleted item 2881). In your log viewer, you have the luxury of substituting as needed to make it more readable.