Repository navigation
Enhanced UI for creating and editing foreign keys - #921
Conversation
|
My only thought is that the dropdown of selectable fields contains all known fields. It would be preferable (or desirable, or likeable) if the selection was limited to those that had an index. eg, its unlikely you'd select 'movie('release_date') and if you did want it, you would have created an index on it. Some tables can be quite large, so having the list limited could be useful... Just a thought. Apart from that, looks very useful. 👍 |
|
Interesting thought @chrisjlocke. It might be better to show all fields, but (if possible) have the ones with an index be shown differently to the ones without. Maybe bold/italic or something? Saying this from personal experience with creating tables in PostgreSQL recently and forgetting to put an index on a column I was using... 😉 |
|
@innermous The screenshot for this looks really good to me too. I haven't tried compiling the code and using it on anything yet, but probably will in a few hours time. 😉 |
|
@chrisjlocke |
|
Ahhhh. I'm not sure if it can be done, I was just thinking that using italics (or something) to show which columns in the foreign table had indexes. It's just a minor thought, and definitely not required. 😄 |
|
Anyway, I'll make it work my way. Two combo-boxes is better and less cluttered than one. I'll update PR and description soon. |
|
Cool, sounds good. 😄 |
|
@justinclift I won't change anything, if anyone wants to enhance this - suggest your changes. |
|
Looks good to me. 😄 |
MKleusberg
left a comment
There was a problem hiding this comment.
Thanks, this looks promising 😃 I've added some comments and will do some more testing tomorrow, but I think other than those comments this should be fine.
| : QWidget(parent) | ||
| , tablesComboBox(new QComboBox(this)) | ||
| , idsComboBox(new QComboBox(this)) | ||
| , m_btnReset(new QPushButton("&Reset", this)) |
There was a problem hiding this comment.
We'll need to add a tr(...) here.
| , m_btnReset(new QPushButton("&Reset", this)) | ||
| , m_clauseEdit(new QLineEdit(this)) | ||
| { | ||
| m_clauseEdit->setPlaceholderText("foreign key clauses(ON UPDATE, ON DELETE etc.)"); |
There was a problem hiding this comment.
Another tr(...) here and maybe start with a capital letter ("Foreign key...") but I'm not sure on the latter. What's more 'correct' in English, @justinclift? 😉
There was a problem hiding this comment.
Hmmm, I'll have to see it in context to decide.
Just added another ToDo item for tomorrow. 😉
| if (tablesComboBox->currentText().isEmpty() || idsComboBox->currentText().isEmpty()) | ||
| return QString(); | ||
| else | ||
| return QString("`%1`(`%2`) %3") |
There was a problem hiding this comment.
Better to remove the ` quotes here and call sqlb::escapeIdentifier() instead. This handles quoting of the quote char correctly and makes it easier to change the quote char in the future if need arises.
| // is that we can grab table(), columns() and constraint() only | ||
| // from foreign keys that had actually passed thru lexer. For a | ||
| // brand new foreign key those methods return empty fields. | ||
| QRegularExpression re("\\`(\\w*)\\`\\(\\`(\\w*)\\`\\)(.*)"); |
There was a problem hiding this comment.
Probably needs to handle quoting of ` quotes correctly, too 😦 Might become ugly.
There was a problem hiding this comment.
Read comment.
I believe this is temporary solution until we can avoid regex step and just touch good ol' POD fields.
Explanation: if we can make EditTableDialog transform constraint (e.g.
table(id) ON DELETE CASCADE
)
to the fk->setTable(), fk->setColumn() and fk->setConstraint() on the fly we'd avoid regex at all.
We just need a way to do it. We don't have many options tho.
There was a problem hiding this comment.
Okay, I found workaround without parsing at all. You might not like it.
|
You might not like the hack (ownership passed vice versa) but it works like clockwork and eliminates any need in parsing. |
|
UPD: renamed ForeignKeyDelegate to ForeignKeyEditorDelegate. Reserved ForeignKeyDelegate for another class related to #614 |
| editor->setGeometry(option.rect); | ||
| } | ||
|
|
||
| #include "ForeignKeyEditorDelegate.moc" |
There was a problem hiding this comment.
MOC requires that if QObject derived class is declared withing cpp file.
MKleusberg
left a comment
There was a problem hiding this comment.
Just did some more testing 😃 Here are some tricky corner cases I've found.
| QSharedPointer<sqlb::ForeignKeyClause> fk = m_table.constraint({field}, sqlb::Constraint::ForeignKeyConstraintType).dynamicCast<sqlb::ForeignKeyClause>(); | ||
| if (!fk.isNull()) { | ||
| fkEditor->tablesComboBox->setCurrentText(fk->table()); | ||
| fkEditor->idsComboBox->setCurrentText(fk->columns().first()); |
There was a problem hiding this comment.
This crashes when trying to edit this foreign key:
CREATE TABLE test(
a int,
FOREIGN KEY(a) REFERENCES table2 -- No columns set here!
);In this situation the reference is to the primary key of the other table. I think we need to support this case as well because referencing to the primary key of a table is a different thing then referencing a column which just happens to be the primary key. As far as I can tell this means:
- Add an empty row to the column combo box.
- Allow entering extra clauses even when no column was selected.
- Generate correct SQL when no column was selected (omit the '(...)' part in 'table(id) blah').
| for (auto obj : objects) { | ||
| if ("table" == obj.gettype()) { | ||
| QString tableName = obj.table.name(); | ||
| if (tableName == m_table.name()) |
There was a problem hiding this comment.
This is wrong. You can actually set a reference to the same table, e.g. for tree structures etc. Unfortunately this makes things difficult for new tables and maybe for those cases in which a table is renamed, because even in these cases I'd like to select the current table from the list. Not sure how to handle this best to be honest.
| sqlb::FieldPtr field = m_table.fields().at(column); | ||
| QSharedPointer<sqlb::ForeignKeyClause> fk = m_table.constraint({field}, sqlb::Constraint::ForeignKeyConstraintType).dynamicCast<sqlb::ForeignKeyClause>(); | ||
| if (!fk.isNull()) { | ||
| fkEditor->tablesComboBox->setCurrentText(fk->table()); |
There was a problem hiding this comment.
There is a tricky side case for this: when the referenced table is the first table in the list, the currentIndexChanged() signal isn't emitted because the current index doesn't actually change, it's the same before as after. But this means that the slot in line 101 won't be called and the columns combo box won't get filled.
There was a problem hiding this comment.
Why so? There's no selected index by default.
There was a problem hiding this comment.
Not sure about that. I just noticed that the column combo box doesn't get filled when the foreign key references the first table. A quick fix that I tried was to first call setCurrentIndex(-1) and then setCurrentText(...):
fkEditor->tablesComboBox->setCurrentIndex(-1);
fkEditor->tablesComboBox->setCurrentText(fk->table());With this extra line it works just fine, but it's sort of a hack.
There was a problem hiding this comment.
I still don't get what's wrong. Did you try to build it yet?
There was a problem hiding this comment.
Hmm sorry, I'm bad at explaining these things... But let me try with an example:
I'm using this database (you'll need to open it in DB4S, obviously). There is a table aaa and a table bbb. Table bbb has a foreign key to table aaa. Now, when you edit table bbb, you'll see the foreign key constraint like this:

But when you double click it, you will get this (or at least I do):

You can see that the columns combo box is empty and that it's grey (=disabled). I need to change the table to some other table (sqlite_sequence) and then back to aaa - only then the columns list is filled.
For me that happens whenever the referenced table is the first in the list. Hope this makes some more sense 😃
There was a problem hiding this comment.
Kk. We'll see what I can do.
| m_table.setConstraint({field}, sqlb::ConstraintPtr(fk)); | ||
| } | ||
|
|
||
| model->setData(index, sql); |
There was a problem hiding this comment.
Maybe need to check for the return value here: if the newly set foreign key won't work, you'll see an error message but afterwards the new foreign key will still be set. This makes it look like it actually did work even though it hasn't. Not sure however what to best do here if false is returned.
There was a problem hiding this comment.
@MKleusberg return value where? Can you be more specific, please.
There was a problem hiding this comment.
Ah sorry, never mind this. This was just me being stupid and confusing two things 😉 All good here!
|
New PR coming up? 😄 |
|
@justinclift nah. |
|
Updated it. Handles fk's without explicit keys now and allows to set fk constraint to same table. Renaming is not supported yet(it never was though with line edit widget) |
|
Excellent @innermous. I'll try this new code out on OSX in a bit. 😄 |
|
@justinclift I didn't do anything to it. |
|
Yeah, I thought that was the case. No worries. I should be able to figure it out. |
|
Soooooo it's quiet again... |
|
Yep, ok from my point of view, too. If you have the time, I have just two minor suggestions left:
|
|
|
No worries. Thanks for sticking to this PR! I really think it's a big improvement in comparison to the old 'editor'! Just feel free to merge this anytime 😃 |
|
Finally merged, yay! Thanks heaps for sticking with this @innermous. 😁 (Those drop downs still need fixing on OSX, but that can be done separately.) |
|
Why ... |
|
Sorry, I thought it was. 😦 |




Requires testing and thoughts.
@justinclift
@MKleusberg
@chrisjlocke
How this looks:
