I'm writing an SDL/input library for my game in Free Pascal, and I'm facing an issue.
I've got a variant record that, when I access an element of it, changes the other elements.
The record type is:
tInput = Record
case Device: TInputDevice of
ID_KeyOnce, ID_KeyCont: (Key: TSDLKey);
ID_MouseButton: (MouseButton: Byte);
ID_MouseAxis, ID_JoyAxis,
ID_JoyBall, ID_JoyHat: (Axis: Byte);
ID_JoyButton, ID_JoyButtonOnce, ID_JoyAxis,
ID_JoyHat, ID_JoyBall: (Which: Byte);
ID_JoyButton, ID_JoyButtonOnce: (Button: Byte);
end;
The code that crashes it is:
with Input do begin
Device := ID_JoyAxis;
Which := 0;
Axis := 1;
end;
When axis is set to one, all of the other variables in the record go to one two!
Is this a known bug? Or some functionality I'm not aware of? Or something I've screwed up?
This is called a union and intended behavior of this type of record declaration.
case Device : TInputDevice of
... is the "magic" here.
In a union the storage of members is "shared".
Edit: taking the record you have in terms of byte offsets (... under the assumption that sizeof(TSDLKey) = 4):
------------------------------------------------
00 | Key | MouseButton | Axis | Which | Button |
---| |-------------|------|-------|--------|
01 | | | | | |
---| |-------------|------|-------|--------|
02 | | | | | |
---| |-------------|------|-------|--------|
03 | | | | | |
------------------------------------------------
By the rules I know, TInputDevice should be an enum type, otherwise you'd have to explicitly give Integer there:
type xyz = record
case integer of
0: (foo: Byte);
1: (bar: Integer);
end;
NB: it is customary for variant types to have one member describe which of the union members should be picked and valid (nested unions).
Related
I am converting a 20-year old system from DBase IV into Access 2010, via Access 2000, in order to be more suitable for Windows 10. However, I have about 350 fields in the database as it is a parameters table and MS-Access 2000 and MS-Access 2010 are complaining about it. I have repaired the database to removed the internal count problem but am rather surprised that Windows 10 software would have such a low restriction. Does anyone know how to bypass this? Obviously I can break it into 2 tables but this seems rather archaic.
When you start to run up against limitations such as this, it reeks of poor database design.
Given that you state that the table in question is a 'parameters' table, with so many parameters, have you considered structuring the table such that each parameter occupies its own record?
For example, consider the following approach, where ParamName is the primary key for the table:
+----------------+------------+
| ParamName (PK) | ParamValue |
+----------------+------------+
| Param1 | Value1 |
| Param2 | Value2 |
| ... | |
| ParamN | ValueN |
+----------------+------------+
Alternatively, if there is the possibility that each parameter may have multiple values, you can simple add one additional field to differentiate between multiple values for the same parameter, e.g.:
+----------------+--------------+------------+
| ParamName (PK) | ParamID (PK) | ParamValue |
+----------------+--------------+------------+
| Param1 | 1 | Value1 |
| Param1 | 2 | Value2 |
| Param1 | 3 | Value3 |
| Param2 | 1 | Value2 |
| ... | ... | ... |
| ParamN | 1 | Value1 |
| ParamN | N | ValueN |
+----------------+--------------+------------+
I had similar problem - we have more than 300 fields in one Contact table on SQL sever linked to Access. You probably do not need to display 255 fields on one form - that would not be user friendly. You can split it to several sub-forms with different underlined queries for each form with less than the limitation. All sub-forms would be linked by the ID.
Sometimes splitting tables as suggested above is not the best idea because of performance.
As Lee Mac described a sample change in structure of a "parameters" table really would be your better choice. You could then define some constants for each of these to be used in code to prevent accidental misspelling later in code in case used in many places.
Then you could create a function (or functions) that take a parameter of what parameter setting you are looking for, it queries the table for that as the key and returns the value. Not being a VB/Access developer, but would think cant overload the functions to have a single function but returning different data types such as string, int, dates, etc. So you may want functions something like
below samples in C#, but principle would be the same.
public int GetAppParmInt( string whatField )
public DateTime GetAppParmDate( string whatField )
public string GetAppParmString( string whatField )
etc...
Then you could get the values by calling the function that has the sole purpose of querying the parameters table for that one key and returns the value as stored.
Hopefully a combination of offered solutions here can help you in your upgrade, even if your parameter table (expanding a bit on Lee Mac's answer) has each data type you are storing to correspond with the "GetAppParm[type]"
ParmsTable
PkID ParmDescription ParmInt ParmDate ParmString
1 CompanyName Your Company
2 StartFiscalYear 2019-06-22
3 CurrentQuarter 4
4... etc.
Then you don't have to worry about changing / data conversions all over the place. They are stored in the proper data type you expect and return that type.
The MySQL database I am working with has a column with comma separated values similar to -
mysql> select * from performance;
+----+------------------+
| id | maximums |
+----+------------------+
| 1 | 10000RPM, 60KM/h |
| 2 | 5000RPM, 30KM/h |
| 3 | 25mph, 3000RPM |
| 4 | 200KM/h, 2000RPM |
+----+------------------+
4 rows in set (0.00 sec)
I am trying to cast the numbers found in to their own INT columns.
mysql> select maximums,
CASE WHEN maximums like "%mph%" THEN CAST(SUBSTRING_INDEX(maximums, 'mph', 1) AS UNSIGNED) END AS mph_int,
CASE WHEN maximums like "%KM/h%" THEN CAST(SUBSTRING_INDEX(maximums, 'KM/h', 1) AS UNSIGNED) END AS kmh_int,
CASE WHEN maximums like "%RPM%" THEN CAST(SUBSTRING_INDEX(maximums, 'RPM', 1) AS UNSIGNED) END AS rpm_int
from performance;
+------------------+---------+---------+---------+
| maximums | mph_int | kmh_int | rpm_int |
+------------------+---------+---------+---------+
| 10000RPM, 60KM/h | NULL | 10000 | 10000 |
| 5000RPM, 30KM/h | NULL | 5000 | 5000 |
| 25mph, 3000RPM | 25 | NULL | 25 |
| 200KM/h, 2000RPM | NULL | 200 | 200 |
+------------------+---------+---------+---------+
4 rows in set, 4 warnings (0.00 sec)
I expect the output to show me the values as INTs in new columns, however am unsure how to achieve this.
Let's give this a whirl, using the good ol'-fashioned blunt instrument approach. I am guessing that you only need this to work once, to convert an old, poorly-designed schema into something more workable. Given that, I have made no effort at elegance or performance.
(If you are not using this to fix your data schema, you should, because the pain you are experiencing now is only the beginning.)
First, we need to split the maximums value into two pieces and process them separately. The first half is:
SUBSTRING_INDEX(`maximum`, ',', 1)
The second half is similar, but there is a stray space:
TRIM(SUBSTRING_INDEX(`maximum`, ',', -1))
From here on, let's just always trim, in case there is variation in the data. Now we need to see if the first section has 'mph' in it, and if so capture the value as you did in your question (this is essentially like your example but operating on only the first part of the maximum value):
IF(TRIM(SUBSTRING_INDEX(`maximum`, ',', 1)) LIKE '%mph', SUBSTRING_INDEX(TRIM(SUBSTRING_INDEX(`maximum`, ',', 1)), 'mph', 1), NULL)
Let's name that chunk of code "mph test on first half". The mph test on the second half is almost identical, just using -1 as the index. Finally, we need to put the non-null value (if either) into the column using COALESCE. Once we create all six variations of the test, we end up with the following:
SELECT
...
COALESCE([mph test on first half], [mph test on second half]) AS mph_int,
COALESCE([kph test on first half], [kph test on second half]) AS kph_int,
COALESCE([rpm test on first half], [rpm test on second half]) AS rpm_int
WHERE
...
Chances are you don't actually need to formally cast the string of digits into an integer; if you are inserting into a table with columns of those types, MySQL will cast the value for you.
I'm storing a object / data structure like this inside a MySql (actually a MariaDb) database:
{
idx: 7,
a: "content A",
b: "content B",
c: ["entry c1", "entry c2", "entry c3"]
}
And to store it I'm using 2 tables, very similar to the method described in this answer: https://stackoverflow.com/a/17371729/3958875
i.e.
Table 1:
+-----+---+---+
| idx | a | b |
+-----+---+---+
Table 2:
+------------+-------+
| owning_obj | entry |
+------------+-------+
And then made a view that joins them together, so I get this:
+-----+------------+------------+-----------+
| idx | a | b | c |
+-----+------------+------------+-----------+
| 7 | content A1 | content B1 | entry c11 |
| 7 | content A1 | content B1 | entry c21 |
| 7 | content A1 | content B1 | entry c31 |
| 8 | content A2 | content B2 | entry c12 |
| 8 | content A2 | content B2 | entry c22 |
| 8 | content A2 | content B2 | entry c32 |
+-----+------------+------------+-----------+
My question is what is the best way I can get it back to my object form? (e.g. I want an array of the object type specified above of all entries with idx between 5 and 20)
There are 2 ways I can think of, but both seem to be not very efficient.
Firstly we can just send this whole table back to the server, and it can make a hashmap with the keys being the primary key or some other unique index, and collect up the different c columns, and rebuild it that way, but that means it has to send a lot of duplicate data, and take a bit more memory and processing time to rebuild on the server. This method also won't be very pleasant to scale if we have multiple arrays, or have arrays within arrays.
Second method would be to do multiple queries, filter Table 1 and get back the list of idx's you want, and then for each idx, send a query for Table 2 where owning_obj = current idx. This would mean sending a whole lot more queries.
Neither of these options seems very good, so I'm wondering if there is a better way. Currently I'm thinking it can be something that utilizes JSON_OBJECT(), but I'm not sure how.
This seems like a common situation, but I can't seem to find the exact wording to search for to get the answer.
PS: The server interfacing with MySql/MariaDb is written in Rust, don't think this is relevant in this question though
You can use GROUP_CONCAT to combine all the c values into a comma-separated string.
SELECT t1.idx, t1.a, t1.b, GROUP_CONCAT(entry) AS c
FROM table1 AS t1
LEFT JOIN table2 AS t2 ON t1.idx = t2.owning_obj
GROUP BY t1.idx
Then explode the string in PHP:
$result_array = [];
while ($row = $result->fetch_assoc()) {
$row['c'] = explode(',', $row['c']);
$result_array[] = $row;
}
However, if the entries can be long, make sure you increase group_concat_max_len.
If you're using MySQL 8.0 you can also use JSON_ARRAYAGG(). This will create a JSON array of the entry values, which you can convert to a PHP array using json_decode(). This is a little safer, since GROUP_CONCAT() will mess up if any of the values contain comma. You can change the separator, but you need a separator that will never be in any values. Unfortunately, this isn't in MariaDB.
So I created a database table in MySQL that held permission rights for permissions and commands, the command rights started with the prefix command_ in the column permission_name and then I have an extra column called allowed_ranks, which is a list of INT rank ID's that are required, split by a , character.
The issue is, the command ones were anything higher, and I've put 1 id in allowed_ranks, is there a way I can loop through all the ones with column starting with command_ and change the allowed_ranks that are just 1 ID to every number starting from that to 9? 9 is the maximum rank ID.
I've already done part of the query, I'm just not sure how to do the updating?
UPDATE permission_rights` SET `allowed_ranks` = '?' WHERE `permission_name` LIKE 'command_%';
How would I update it to every number after the columns value up to 9? So lets say I had this record... just a quick example to ensure you know what I mean.
| permission_name | allowed_ids |
----------------------------------
| command_hello | 2
| command_junk | 5
| command_delete | 8
| command_update | 1
Would become...
| permission_name | allowed_ids |
----------------------------------
| command_hello | 2,3,4,5,6,7,8,9
| command_junk | 5,6,7,8,9
| command_delete | 8,9
| command_update | 1,2,3,4,5,6,7,8,9
The better approach would be to use a number generator (some method which will produce number from 1 to n), but general MySQL has no such capability.
If you use MariaDB you can use seq_1_to_1000 as suggested here in Answer by O.Jones.
However your use case seems to be simpler, since you said that the highest rank is 9, I would just use
update a
set a.allowed_ids = RIGHT('1,2,3,4,5,6,7,8,9',19-2*a.allowed_ids)
where a.permission_name like 'command_%'
I have put a lot of effort into my database design, but I think I am
now realizing I made a major mistake.
Background: (Skip to 'Problem' if you don't need background.)
The DB supports a custom CMS layer for a website template. Users of the
template are limited to turning pages on and off, but not creating
their own 'new' pages. Further, many elements are non editable.
Therefore, if a page has a piece of text I want them to be able to edit,
I would have 'manually' assigned a static ID to it:
<h2><%= CMS.getDataItemByID(123456) %></h2>
Note: The scripting language is not relevant to this question, but the design forces
each table to have unique column names. Hence the convention of 'TableNameSingular_id'
for the primary key etc.
The scripting language would do a lookup on these tables to find the string.
mysql> SELECT * FROM CMSData WHERE CMSData_data_id = 123456;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| 1 | 123456 | 1 |
+------------+-----------------+-----------------------------+
mysql> SELECT * FROM CMSDataTypes WHERE CMSDataType_type_id = 1;
+----------------+---------------------+-----------------------+------------------------+
| CMSDataType_id | CMSDataType_type_id | CMSDataType_type_name | CMSDataType_table_name |
+----------------+---------------------+-----------------------+------------------------+
| 1 | 1 | String | CMSStrings |
+----------------+---------------------+-----------------------+------------------------+
mysql> SELECT * FROM CMSStrings WHERE CMSString_CMSData_data_id=123456;
+--------------+---------------------------+----------------------------------+
| CMSString_id | CMSString_CMSData_data_id | CMSString_string |
+--------------+--------------------------------------------------------------+
| 1 | 123456 | The answer to the universe is 42.|
+--------------+---------------------------+----------------------------------+
The rendered text would then be:
<h2>The answer to the universe is 42.</h2>
This works great for 'static' elements, such as the example above. I used the exact same
method for other data types such as file specifications, EMail Addresses, Dates, etc.
However, it fails for when I want to allow the User to dynamically generate content.
For example, there is an 'Events' page and they will be dynamically created by the
User by clicking 'Add Event' or 'Delete Event'.
An Event table will use keys to reference other tables with the following data items:
Data Item: Table:
--------------------------------------------------
Date CMSDates
Title CMSStrings (As show above)
Description CMSTexts (MySQL TEXT data type.)
--------------------------------------------------
Problem:
That means, each time an Event is created, I need to create the
following rows in the CMSData table;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| x | y | 6 | (Event)
| x+1 | y+1 | 5 | (Date)
| x+2 | y+2 | 1 | (Title)
| x+3 | y+3 | 3 | (Description)
+------------+-----------------+-----------------------------+
But, there is the problem. In MySQL, you can have only 1 AUTO INCREMENT field.
If I query for the highest value of CMSData_data_id and just add 1 to it, there
is a chance there is a race condition, and someone else grabs it first.
How is this issue typically resolved - or avoided in the first place?
Thanks,
Eric
The id should be meaningless, except to be unique. Your design should work no matter if the block of 4 ids is contiguous or not.
Redesign your implementation to add the parts separately, not as a block of 4. Doing so should simplify things overall, and improve your scalability.
What about locking the table before writing into it? This way, when you are inserting a row in the CMSData table, you can get the last id.
Other suggestion would be to not have an incremented id, but a unique generated one, like a guid or so.
Lock Tables