KEA dhcp Mysql Backend - mysql

I have an issue with the new ISC DHCP, KEA, on the MySQL backend.
I want to store leases in my DB, it works but some info are not stored.
I obtain this kind of entry in my DB :
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
| address | hwaddr | client_id | valid_lifetime | expire | subnet_id | fqdn_fwd | fqdn_rev | hostname |
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
| 3232236052 | '° | NULL | 4000 | 2015-07-22 08:54:32 | 1 | 0 | 0 | │
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
The address field is the IP adress in decimal, I checked and it's the good one.
I didn't find how change the IP adress to IPv4 format and how store mac address in the hwaddr field in the KEA documentation.
If someone know how to do this I will be really grateful !
Thank you !

As per KEA documentation hwaddr field is VARBINARY. You should be able to see the value IP address and hwaddr using:
SELECT INET_NTOA(address), HEX(hwaddr), lease4.* FROM lease4;

I had a similar need to create KEA host reservations and populate the MAC and IP addresses as regular strings while still having the fields KEA uses updated automatically on an INSERT or UPDATE.
What I ended up doing is creating two new fields that would hold those string values ('hosts.dhcp_identifier_str' and 'hosts.ipv4_address_str'):
ALTER TABLE `hosts` ADD `dhcp_identifier_str` VARCHAR(12) NOT NULL AFTER `dhcp_identifier`;
ALTER TABLE `hosts` ADD `ipv4_address_str` VARCHAR(15) NULL DEFAULT NULL AFTER `ipv4_address`;
Then, I keep the corresponding fields that KEA uses ('hosts.dhcp_identifier' and 'hosts.ipv4_address') up to date by using BEFORE INSERT/UPDATE MySQL triggers:
DELIMITER //
DROP TRIGGER IF EXISTS `host_BINS`//
CREATE TRIGGER `host_BINS` BEFORE INSERT ON `hosts`
FOR EACH ROW BEGIN
IF (NEW.dhcp_identifier = '' AND NEW.dhcp_identifier_str != '') THEN
SET NEW.dhcp_identifier = UNHEX(UPPER(NEW.dhcp_identifier_str));
ELSEIF (NEW.dhcp_identifier_str = '' AND NEW.dhcp_identifier != '') THEN
SET NEW.dhcp_identifier_str = LOWER(HEX(NEW.dhcp_identifier));
END IF;
IF (NEW.ipv4_address IS NULL AND NEW.ipv4_address_str IS NOT NULL) THEN
SET NEW.ipv4_address = INET_ATON(NEW.ipv4_address_str);
ELSEIF (NEW.ipv4_address_str IS NULL AND NEW.ipv4_address IS NOT NULL) THEN
SET NEW.ipv4_address_str = CAST(INET_NTOA(NEW.ipv4_address) AS CHAR);
END IF;
END
//
DROP TRIGGER IF EXISTS `host_BUPD`//
CREATE TRIGGER `host_BUPD` BEFORE UPDATE ON `hosts`
FOR EACH ROW BEGIN
IF (NEW.dhcp_identifier_str != '' AND OLD.dhcp_identifier != UNHEX(UPPER(NEW.dhcp_identifier_str))) THEN
SET NEW.dhcp_identifier = UNHEX(UPPER(NEW.dhcp_identifier_str));
ELSEIF (NEW.dhcp_identifier != '' AND OLD.dhcp_identifier_str != LOWER(HEX(NEW.dhcp_identifier))) THEN
SET NEW.dhcp_identifier_str = LOWER(HEX(NEW.dhcp_identifier));
END IF;
IF (NEW.ipv4_address_str IS NOT NULL AND OLD.ipv4_address != INET_ATON(NEW.ipv4_address_str)) THEN
SET NEW.ipv4_address = INET_ATON(NEW.ipv4_address_str);
ELSEIF (NEW.ipv4_address IS NOT NULL AND OLD.ipv4_address_str != CAST(INET_NTOA(NEW.ipv4_address) AS CHAR)) THEN
SET NEW.ipv4_address_str = CAST(INET_NTOA(NEW.ipv4_address) AS CHAR);
END IF;
END
//
This works whether you INSERT/UPDATE an entry using the dhcp_identifier/ipv4_address or dhcp_identifier_str/ipv4_address_str pairs.
I'm sure you can use the same triggers for the 'lease4' table.
Hope that helps.

Related

Conditional table updates

Consider the following table.
myTable
+----+-----------+------------------------------------+
| Id | responseA | responseB |
+----+-----------+------------------------------------+
| 1 | | {"foo":"bar","lvl2":{"key":"val"}} |
+----+-----------+------------------------------------+
where:
Id, INT (11) PRIMARY
responseA, TEXT utf8_unicode_ci
responseB, TEXT utf8_unicode_ci
Let's say that I want to conditionally update the table with some outside data. The conditions are:
• if there's nothing in responseA, populate it with the outside data, otherwise
• if there is something in responseA, leave it as it is, and populate responseB with the outside data
I was pretty much convinced that I could just do this to get what I want:
UPDATE myTable
SET
responseA = IF(TRIM(responseA) = '','foo',TRIM(responseA)),
responseB = IF(TRIM(responseA) != '','foo',TRIM(responseB))
WHERE Id = 1
However, this updates both responseA and responseB to the same value - foo, making the table:
myTable
+----+-----------+-----------+
| Id | responseA | responseB |
+----+-----------+-----------+
| 1 | foo | foo |
+----+-----------+-----------+
I was expecting my table to look like this after the update:
myTable
+----+-----------+------------------------------------+
| Id | responseA | responseB |
+----+-----------+------------------------------------+
| 1 | foo | {"foo":"bar","lvl2":{"key":"val"}} |
+----+-----------+------------------------------------+
What am I misunderstanding, and how can I achieve this conditional update? Do the updates happen sequentially? If so, I guess that would explain why both of the fields are updated.
UPDATE TABLE
SET responseA = CASE WHEN responseA IS NULL
THEN #data
ELSE responseA
END,
responseB = CASE WHEN responseA IS NULL
THEN responseB
ELSE #data
END
;
here your changed query
UPDATE myTable
SET
responseB = IF(TRIM(responseA) != '','foo',TRIM(responseB)),
responseA = IF(TRIM(responseA) = '','foo',TRIM(responseA))
WHERE Id = 1
It seems the value of responseA is changed before the IF() for responseB is evaluated.
One possible solution is to do a simple UPDATE:
UPDATE mytable SET responseA = ? WHERE id = 1
Then adjust the columns in a trigger, where you have access to both the original and the new value of the columns:
CREATE TRIGGER t BEFORE UPDATE ON mytable
FOR EACH ROW BEGIN
IF TRIM(OLD.responseA) != '' THEN
SET NEW.responseB = NEW.responseA;
SET NEW.responseA = OLD.responseA;
END IF;
END
(I have not tested this.)
I am also assuming that your test for '' (empty string) instead of NULL is deliberate, and that you know that NULL is not the same as ''.
The key point in the UPDATE statement is that you should update first the column responseB, so that column responseA retains its original value which can be checked again when you try to update it:
UPDATE myTable
SET responseB = CASE WHEN TRIM(responseA) = '' THEN responseB ELSE 'foo' END,
responseA = CASE WHEN TRIM(responseA) = '' THEN 'foo' ELSE responseA END
WHERE Id = 1;

How to select static values on mysql select query?

I am new to mysql, here i am trying to get data from database table.
select id,txnid,amount,status from txn_details;
With above query Getting data successfully but status column getting 0 or 1 or 2, but i want 0 as failed, 1 as success and 2 as not processed.
How to change my query?
You can use a case
select id, txnid, amount,
case when status = 0 then 'failed'
when status = 1 then 'success'
else 'not processed'
end as status
from txn_details;
We can use an expression in the SELECT list. It could be a searched CASE expression e.g.
SELECT CASE t.status
WHEN 0 THEN 'failed'
WHEN 1 THEN 'success'
WHEN 2 THEN 'not processed'
ELSE 'unknown'
END AS status_name
, t.status
, t.amount
, t.txnid
FROM txn_details t
This approach is ANSI-92 standards compliant, and will work in most relational databases.
There are some other MySQL specific alternatives, such as the ELT function ...
SELECT ELT(t.status+1,'failed','success','not processed') AS status_name
, t.status
, t.amount
, t.txnid
FROM txn_details t
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_elt
If you prefer a central point of maintenance (ie you prefer not to recode all your queries when a new status comes along) you could create a status table and either use a join or sub query to get the values, alternatively you could create a function, for example
drop table if exists txn_details,txn_status;
create table txn_details(id int, txnid int, amount int , status int);
insert into txn_details values
(1,1,10,1),(2,1,10,2),(3,1,10,4);
create table txn_status (id int, statusval varchar(20));
insert into txn_status values
(1,'success'),(2,'not processed'), (3,'failed');
drop function if exists f;
delimiter $$
create function f(instatus int)
returns varchar(20)
begin
declare rval varchar(20);
return (select
case when instatus = 1 then 'success'
when instatus = 2 then 'not processed'
when instatus = 3 then 'failed'
else 'Unknown'
end
);
select t.*,coalesce(ts.statusval,'Unknown') status
from txn_details t
left join txn_status ts on ts.id = t.status;
select t.*,coalesce((select statusval from txn_status ts where ts.id = t.status),'Unknown') status
from txn_details t;
Note the use of coalesce in case a status is not found.
Both produce this result
+------+-------+--------+--------+---------------+
| id | txnid | amount | status | status |
+------+-------+--------+--------+---------------+
| 1 | 1 | 10 | 1 | success |
| 2 | 1 | 10 | 2 | not processed |
| 3 | 1 | 10 | 4 | Unknown |
+------+-------+--------+--------+---------------+
3 rows in set (0.00 sec)
Using the function like this
select t.*, f(status) as status
from txn_details t;
also produces the same result.
Of course using a status table or a function means you have to communicate their availability and enforce their use.
I would also consider the using a foreign key constraint in txn_details to cut down on the number of unknown values and put procedures in place to stop people adding new status codes at will without going through change control
The following query would work. It uses CASE ... END to determine and return values for the virtual column status.
SELECT id,txnid,amount,
CASE
WHEN status = 0 THEN 'failed'
WHEN status = 1 THEN 'success'
WHEN status= 2 THEN 'not processed'
END AS status
FROM txn_details;

How can I check BIT datatype in MySQL?

I have a column in users table named permissions. Like this:
// users
+---------+-----------------+-------------+
| id | permissions | name |
+---------+-----------------+-------------+
| int(11) | bit(15) | varchar(20) |
+---------+-----------------+-------------+
| 1 | 001100001111101 | Jack |
| 2 | 111111111111111 | Peter |
| 3 | 110000000111011 | Martin |
+---------+-----------------+-------------+
As you can see, permissions column has bit(15) datatype. Each bit of that value determines one user ability. For example, the first bit refers to voting-ability, the second one refers to commenting-ability ant etc ...
Also I have a trigger BEFORE UPDATE which investigates that permission like this:
SELECT permissions INTO #deleting_permission FROM users WHERE id = new.deleter_id;
IF old.deleted <> new.deleted THEN
IF (IFNULL((#deleting_permission & b'10000000000' > 0), 0) < 1) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "You cannot delete post";
END IF;
END IF;
And always it throws:
You cannot delete post
Even for #2 user which has 111111111111111 permission value. So what's wrong?
Noted that I update permissions column like this:
-- To give some specific accesses (permissions) to the user
UPDATE users SET permissions = b'111111000101011' WHERE id = ?
-- To give full access (permissions) to the user
UPDATE users SET permissions = -1 WHERE id = ?
You select the field active in the trigger to check permission, but according to your question, the rights are held in the permissions field.
Change your query to use the latter field:
SELECT `permissions` INTO #active_deleter FROM users WHERE id = new.deleter_id;
Also, your permissions field has 15 bits. The mask you use has only 10. You should check if you are testing the right bit in the first place.
If you want to work with the BIT type, then you'll need to use bitwise operators, not regular operators. For example, if you wanted to check if the user had the left most bit set as a permission, you could do an & operation:
SELECT permissions INTO #p FROM users WHERE id = new.deleter_id;
IF old.deleted <> new.deleted THEN
IF (b'100000000000000' & #p = 0)
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "You cannot delete post";
END IF;
END IF;
The basic idea above is that the literal b'100000000000000' serves as a mask which can be used to detect if the left most bit (and only that bit) is set to 1. If it were set to 1, then the & operation would not return 0.
The reason why a user with 111111111111111 permissions has all rights is that any mask checking a bit would return true.

Update whole table in SQL?

I have a table called "customers" which looks like this:
ID | NAME | ADDRESS | ZIP
John | Peterson | Street 2 | 45738
Jan | 74383 | Street 4 | 74383
Eric | Stone | 74383 | 74383
Julia| Carry | Street 9 | 74383
I want to replace any occurrence of "74383" with "". I have about 1.5 Million rows. Is this doable?
Okay sorry that was my mistake, the number can be in any COLUMN! I updated it.
Assuming you mean in the zip column:
update customers
set zip = ''
where zip = '74383';
Note: you might prefer to set the value to NULL rather than the empty string (''), but your question explicitly suggests an empty string.
EDIT:
I would actually suggest that you use separate update statements for each column if you have indexes on them. The updates will be much faster.
Second, if you replace the value with NULL rather than '', you can use the logic:
update customers
set zip = NULLIF(zip, '74383'),
address = NULLIF(address, '74383'),
name = NULLIF(name, '74383')
where '74383' in (name, address, zip);
(You can actually replace this with an empty string using COALESCE(NULLIF(zip, '74383'), ''), but I prefer a CASE once the logic gets at all complicated.)
Reverse the IN operator to search the existence of '74383' value in all the columns then using case statement you can update the columns. Try this.
UPDATE customers
SET NAME = CASE WHEN NAME = '74383' THEN '' ELSE NAME END,
address = CASE WHEN address = '74383' THEN '' ELSE address END,
zip = CASE WHEN zip = '74383' THEN '' ELSE zip END
WHERE '74383' IN ( NAME, address, zip )
It is strange for me the requirement like this but, anyway, you can try this:
START TRANSACTION;
UPDATE customers
SET NAME = ''
WHERE NAME = '74383';
UPDATE customers
SET ADDRESS = ''
WHERE ADDRESS = '74383';
UPDATE customers
SET ZIP = ''
WHERE ZIP = '74383';
COMMIT;

Update a field if another field has a certain value

I am trying to update a MySQL database but only if a field has the value Approved.
If status is Approved then Date approved should update with the date.
Below is the code I am currently using but cannot get it to work. How to get it to work?
UPDATE my_table
SET `FieldValue`= IF(FieldName='status' AND FieldValue='Approved','".date('m/d/Y')."','')
WHERE `SubmissionId`=".$SubmissionId."
AND FieldName='Date Approved'
Sample Data
+--------+--------------+---------------+--------------+
| FormId | SubmissionId | FieldName | FieldValue |
+--------+--------------+---------------+--------------+
| 6 | 778 | status | Not Approved |
| 6 | 778 | Date Approved | |
+--------+--------------+---------------+--------------+
Use a CASE statement like below:
UPDATE my_table
SET `FieldValue` = CASE WHEN FieldName = 'status'
AND FieldValue='Approved' THEN date('m/d/Y') ELSE `FieldValue` END
WHERE `SubmissionId` = $SubmissionId;
But your query won't make sense; your FieldValue column looks like a string type column and you are trying store a date type data.
Something like this?
$db = JFactory::getDbo();
$query = $db->getQuery(true);
// Fields to update.
$fields = array(
$db->quoteName('FieldValue') . ' = ' . $date->toSql('m/d/Y'))
);
// Conditions for which records should be updated.
$conditions = array(
$db->quoteName('SubmissionId') . ' = SubmissionId',
$db->quoteName('FieldValue') . ' = ' . $db->quote('Approved')
);
$query->update($db->quoteName('#__my_table'))->set($fields)->where($conditions);
$db->setQuery($query);
$result = $db->execute();
Superficially, you should be using the raw SQL like this:
UPDATE my_table
SET FieldValue = date('m/d/Y')
WHERE SubmissionId = 778
AND FieldName = 'Date Approved'
-- AND FieldValue IS NULL -- Optional
-- AND FormId = 6 -- Optional
AND EXISTS (SELECT * FROM my_table
WHERE FieldName = 'status'
AND FieldValue = 'Approved'
AND SubmissionId = 778
-- AND FormId = 6 -- Optional
)
You might need to tart things up a little to get values embedded into the string that forms the SQL statement.
You don't mention FormID in your query; in case of doubt, you should constrain the UPDATE with the correct FormID value, twice, like you constrain the SubmissionID value twice (as shown in the comments). You might decide you only want to update the 'Date Approved' field name when it is NULL (or perhaps blank).
I note that one of the problem with this EAV design is that you lose the type-checking that a normal design gives you. You could store a date (as intended), or a pure number, or pure text, or anything else in the FieldValue column for the 'Date Approved' FieldName and there's nothing to stop that abuse happening. If you had an orthodox typed column, you could ensure that non-dates were never stored in the 'Date Approved' column.
Your UPDATE is tied to a single submission ID; so is mine. It should be possible to enhance things so that all the uninitialized 'Date Approved' columns that are approved and have not previously had the 'Date Approved' value set do in fact have it set.