Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add the ability to use schema names for tables in table names #572

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

unchase
Copy link

@unchase unchase commented Aug 31, 2021

Allows to define database schema name for tables used by MiniProfiler.

For example:

image

P.S. Introduced in DatabaseStorageBase, so far implemented only in SqlServerStorage

@unchase unchase changed the title Feat: Add SchemaName parameter to DatabaseStorageBase [WIP] Feat: Add SchemaName parameter to DatabaseStorageBase Aug 31, 2021
@unchase unchase force-pushed the feat/add-schema-name-to-sql-server-storage branch 3 times, most recently from 8c818fe to 7681bd7 Compare August 31, 2021 11:00
@unchase
Copy link
Author

unchase commented Aug 31, 2021

Hi, @NickCraver

Could you take a look?

And why did yoy replace storage.CreateSchema(); method from DatabaseStorageBase to DatabaseStorageExtensions (StorageBaseTest)?

@unchase unchase changed the title [WIP] Feat: Add SchemaName parameter to DatabaseStorageBase Feat: Add SchemaName parameter to DatabaseStorageBase Aug 31, 2021
@unchase unchase force-pushed the feat/add-schema-name-to-sql-server-storage branch from adc9d55 to 8758fd7 Compare August 31, 2021 11:36
@NickCraver
Copy link
Member

Hey there! First, thanks for willingness to contribute here :)

Can I zoom out a bit and figure out what problem we're trying to solve with the changes here? There are a few issues with breaking constructors and such, but I'd like to understand what's not working today. It's supposed to be handled intentionally as your table name could for example be "MyTable", or "dbo.MyTable", and it'd work schema qualified. Is that not working for some scenario? If so I'd like to understand that path :)

There is a problem always committing schema, and that'd break some use cases I've personally maintained. In some situations, you want to use "my schema's table", whatever schema that login may default to (this happens in multi-tenancy environments). In that vein, it was an intentional decision to not bake schema into the queries here. That's just for context, but let's try and solve whatever problem let you here!

@unchase
Copy link
Author

unchase commented Sep 1, 2021

Setting your own schema is a convenient option when, for example, we need access rights to data at the schema level. In addition, if there are many tables with the "dbo" schema in the database, it is visually difficult to select the tables we need.

If we specify dbo.MyTable as the name of the table, then an error occurs when creating constraints and indexes:

image

In fact, the constructor does not break, since all previous use cases remain valid.

I can correct the changes I added so that when specifying SchemaName = null, the schema is not added to the queries (ignored), so we will solve the problem that you mentioned.

I also added a check for the existence of the created tables, since an exception occurred if they were already created.

@unchase
Copy link
Author

unchase commented Sep 1, 2021

@NickCraver
And maybe the storage.CreateSchema() method should be returned to DatabaseStorageBase be able to programmatically create the necessary tables in the database using one method?

@unchase
Copy link
Author

unchase commented Sep 3, 2021

@NickCraver Could you take a look at my little fixes?

P.S. Maybe should use MiniProfilersTable.Replace(".", "_"), MiniProfilerTimingsTable.Replace(".", "_"), MiniProfilerClientTimingsTable.Replace(".", "_"), for example, to resolve the issue with constraints and indexes?

@NickCraver
Copy link
Member

Hey @unchase - busy week here. I believe the solution here remains much more complicated than is needed to solve the problem. We can add brackets on table names for example. The schema change is a breaking change, all optional parameter additions are binary breaking changes (as consumers will get a missing method exception).

Given you're the first person I'm aware of hitting this, can you expound on the use case? For example, have you considered changing the default schema on the user MiniProfiler logs in with instead?

@unchase
Copy link
Author

unchase commented Sep 3, 2021

The schema change is a breaking change, all optional parameter additions are binary breaking changes (as consumers will get a missing method exception).

Hmm. There are optional parameters, so how consumers will get a missing method exception?

image

And this is not a breaking change, since all old calls to the constructor will also be valid (since they contain either only connectionString or all parameters - connectionString, profilersTable, timingsTable, clientTimingsTable).

image

But I agree with you, more investigation needs to be done.


The database used contains several tables for different purposes (Hangfire, MiniProfiler, and some application tables with separated schemes: Identity, Accreditation ...), and the connection string along with the user for login is one.

image

And I would like to divide access to certain tables by schemas. Therefore, changing the default schema for the user is not appropriate.
In addition, many libraries allow you to customize schemas for the tables you create, which is certainly normal practice.

@NickCraver
Copy link
Member

The way optional parameters are implemented in .NET, the caller has the values. So any assembly compiled against this one is looking for exactly the current method signature. For example let's say we have:

public void Foo(string a)

A caller is looking for Foo(string).
If we have:

public void Foo(string a, string b = null)

Then a caller with the same single arg is now looking for Foo(string, null). All people compiled against the previous version are looking for Foo(string), which no longer exists and will throw a missing method exception. This is a binary breaking change, for all libraries built on top of MiniProfiler. It's for this reason we must carefully weigh changes and ensure we don't break people in this manner. So my preference is, if possible, to explore simpler routes, rather than adding a new constructor and overload to every data provider (so we're consistent).

Does that help convey why it's a breaking change? You're not the first person to hit this confusion, I see it quite often and probably should write a better blog post and example to point to.

I'll try and poke at this for alternatives, just be aware that I stepped away from Stack after 10 years just yesterday and do plan on taking it easy at least a few days before doing much again :)

@unchase
Copy link
Author

unchase commented Sep 4, 2021

Yes, now it became clear to me, thanks for the clarification.

@unchase
Copy link
Author

unchase commented Sep 4, 2021

We can add brackets on table names for example

Like this: [MiniProfiler].[Profilers]?

The same issue:

image

@unchase unchase force-pushed the feat/add-schema-name-to-sql-server-storage branch from 6669e92 to f9074fb Compare September 4, 2021 09:49
@unchase
Copy link
Author

unchase commented Sep 4, 2021

@NickCraver I added a few changes to ensure backward compatibility (no breaking changes now I think).

@unchase unchase force-pushed the feat/add-schema-name-to-sql-server-storage branch from f9074fb to 30d049e Compare September 4, 2021 10:01
@unchase unchase changed the title Feat: Add SchemaName parameter to DatabaseStorageBase Feat: Add the ability to use schema names for tables in table names Sep 4, 2021
@NickCraver
Copy link
Member

Hey @unchase, sorry I've been busy lately - hadn't been announced I was starting at Microsoft when you filed this :) I think your original approach of adding a non-breaking overload with schema name is the way to go here (rather than battling an infinite number of SQL syntaxes with Regex). However, I don't think we should create schemas - that's not really the job of a library, that's a DBA responsibility and in many environments we'll have permissions in the schema we're given but not the ability to create a schema - so there's a scoping issue of where we can operate if introducing such a request.

In the table creation bits, the fact we're not checking if the tables are created already (and failing) is very much by design - we don't want to generate tables left and right on a mis-config, it's intended to be a very intentional on-setup operation from the caller. Happy to help tweak here or can implement off base, but given the number of changes in play I think it may be much simpler to go from off main with just a few overload additions (to ensure non-breaking).

I do appreciate you exploring the simplified approach to the index conflict, but with the extent needed to make it work, it's much more obvious your first approach is far more reasonable and maintainable - that's my fault. Happy to help remedy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants