Skip to content

Enhanced UI for creating and editing foreign keys - #921

Merged
justinclift merged 1 commit into
sqlitebrowser:masterfrom
vtronko:master
Jan 13, 2017
Merged

justinclift merged 1 commit into
sqlitebrowser:masterfrom
vtronko:master

Conversation

@vtronko

@vtronko vtronko commented Jan 5, 2017 •

Copy link
Copy Markdown
Member

Requires testing and thoughts.
@justinclift
@MKleusberg
@chrisjlocke

How this looks:
screenshot_20170105_041958

@chrisjlocke

Copy link
Copy Markdown
Member

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. 👍

@justinclift

justinclift commented Jan 5, 2017 •

Copy link
Copy Markdown
Member

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... 😉

@justinclift

Copy link
Copy Markdown
Member

@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. 😉

@vtronko

vtronko commented Jan 5, 2017

Copy link
Copy Markdown
Member Author

@chrisjlocke
@justinclift
Unlikely but possible.
I still don't understand what I need to change, I didn't understand suggestions with bold indices :(

@justinclift

Copy link
Copy Markdown
Member

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. 😄

@vtronko

vtronko commented Jan 5, 2017 •

Copy link
Copy Markdown
Member Author

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.

@justinclift

Copy link
Copy Markdown
Member

Cool, sounds good. 😄

@vtronko

vtronko commented Jan 5, 2017

Copy link
Copy Markdown
Member Author

@justinclift
@chrisjlocke
Looks this way now
screenshot_20170105_231534

I won't change anything, if anyone wants to enhance this - suggest your changes.

@justinclift

Copy link
Copy Markdown
Member

Looks good to me. 😄

@MKleusberg MKleusberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/ForeignKeyDelegate.cpp Outdated
: QWidget(parent)
, tablesComboBox(new QComboBox(this))
, idsComboBox(new QComboBox(this))
, m_btnReset(new QPushButton("&Reset", this))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add a tr(...) here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Comment thread src/ForeignKeyDelegate.cpp Outdated
, m_btnReset(new QPushButton("&Reset", this))
, m_clauseEdit(new QLineEdit(this))
{
m_clauseEdit->setPlaceholderText("foreign key clauses(ON UPDATE, ON DELETE etc.)");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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? 😉

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'll have to see it in context to decide.

Just added another ToDo item for tomorrow. 😉

Comment thread src/ForeignKeyDelegate.cpp Outdated
if (tablesComboBox->currentText().isEmpty() || idsComboBox->currentText().isEmpty())
return QString();
else
return QString("`%1`(`%2`) %3")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/ForeignKeyDelegate.cpp Outdated
// 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*)\\`\\)(.*)");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably needs to handle quoting of ` quotes correctly, too 😦 Might become ugly.

@vtronko vtronko Jan 6, 2017 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I found workaround without parsing at all. You might not like it.

@vtronko

vtronko commented Jan 6, 2017

Copy link
Copy Markdown
Member Author

You might not like the hack (ownership passed vice versa) but it works like clockwork and eliminates any need in parsing.

@vtronko

vtronko commented Jan 6, 2017

Copy link
Copy Markdown
Member Author

UPD: renamed ForeignKeyDelegate to ForeignKeyEditorDelegate. Reserved ForeignKeyDelegate for another class related to #614

@justinclift

Copy link
Copy Markdown
Member

The code compiles fine here, but the display isn't correct for the "Foreign Key" widget pieces:

screen shot 2017-01-06 at 04 33 55
Resizing the table or choosing different values makes no difference.

After clicking to another line, so the values chosen in the foreign key part are accepted, the chosen values then display ok:

screen shot 2017-01-06 at 04 37 25

Haven't tested any further than that though. 😄

editor->setGeometry(option.rect);
}

#include "ForeignKeyEditorDelegate.moc"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

@vtronko vtronko Jan 6, 2017 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MOC requires that if QObject derived class is declared withing cpp file.

@MKleusberg MKleusberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just did some more testing 😃 Here are some tricky corner cases I've found.

Comment thread src/ForeignKeyEditorDelegate.cpp Outdated
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());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Add an empty row to the column combo box.
  2. Allow entering extra clauses even when no column was selected.
  3. Generate correct SQL when no column was selected (omit the '(...)' part in 'table(id) blah').

Comment thread src/ForeignKeyEditorDelegate.cpp Outdated
for (auto obj : objects) {
if ("table" == obj.gettype()) {
QString tableName = obj.table.name();
if (tableName == m_table.name())

@MKleusberg MKleusberg Jan 6, 2017 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so? There's no selected index by default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't get what's wrong. Did you try to build it yet?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:
screenshot_20170109_001146

But when you double click it, you will get this (or at least I do):
screenshot_20170109_001312
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 😃

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kk. We'll see what I can do.

m_table.setConstraint({field}, sqlb::ConstraintPtr(fk));
}

model->setData(index, sql);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@vtronko vtronko Jan 7, 2017 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MKleusberg return value where? Can you be more specific, please.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, never mind this. This was just me being stupid and confusing two things 😉 All good here!

@vtronko vtronko closed this Jan 6, 2017
@justinclift

Copy link
Copy Markdown
Member

New PR coming up? 😄

@vtronko

vtronko commented Jan 7, 2017

Copy link
Copy Markdown
Member Author

@justinclift nah.

@vtronko

vtronko commented Jan 9, 2017

Copy link
Copy Markdown
Member Author

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)

@justinclift

Copy link
Copy Markdown
Member

Excellent @innermous. I'll try this new code out on OSX in a bit. 😄

@justinclift

Copy link
Copy Markdown
Member

Just tried this out on OSX. The two drop down fields still have the sizing problem. ☹️
screen shot 2017-01-10 at 22 17 06
I'll take a better look at this tomorrow, and see if I can figure out a solution for that. It's probably something simple. 😄

@vtronko

vtronko commented Jan 10, 2017

Copy link
Copy Markdown
Member Author

@justinclift I didn't do anything to it.

@justinclift

Copy link
Copy Markdown
Member

Yeah, I thought that was the case. No worries. I should be able to figure it out.

@vtronko

vtronko commented Jan 12, 2017

Copy link
Copy Markdown
Member Author

Soooooo it's quiet again...
@MKleusberg can we merge it? From my POV there're no edge cases and everything except renaming works smooth.

@MKleusberg

Copy link
Copy Markdown
Member

Yep, ok from my point of view, too. If you have the time, I have just two minor suggestions left:

  1. I think the #inlcude <QRegularExpressionMatch> can be deleted now.
  2. Can you add a TODO comment somewhere to add support for filling the table and id combo boxes with the current table scheme when creating a new table? I agree it's not super important right away, but maybe we should add that in the future. And with the TODO it won't be forgotten (hopefully) 😃

@vtronko

vtronko commented Jan 12, 2017

Copy link
Copy Markdown
Member Author

@MKleusberg

  1. Forgot to.
  2. All right.

@MKleusberg

Copy link
Copy Markdown
Member

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 😃

@justinclift
justinclift merged commit 1ae7537 into sqlitebrowser:master Jan 13, 2017
@justinclift

Copy link
Copy Markdown
Member

Finally merged, yay!

Thanks heaps for sticking with this @innermous. 😁

(Those drop downs still need fixing on OSX, but that can be done separately.)

@vtronko

vtronko commented Jan 13, 2017 •

Copy link
Copy Markdown
Member Author

Why ...
It was not done yet.

@justinclift

Copy link
Copy Markdown
Member

Sorry, I thought it was. 😦

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants