• (disco)

    LOL. Nice video.

  • (disco)

    That looks like something that would either

    • delete 0 rows because of a copy/paste error somewhere, resulting in comparison with the wrong field
    • delete many rows because of misplaced parentheses around a OR ([something] = @Original_something)...
  • (disco)

    There's so many conditions on that query, the page scrolls almost as much as t/1000

    <!-- Emoji'd by MobileEmoji 0.2.0-->
  • (disco)

    Actually such techniques are common if you want to ensure that the row has not been changed. A common concurrency problems is that person A selects a record from the database, looks at and determines it is "old"/"obsolete" and generates a "delete"...in the meantime person B has looked at the same record, updated information and posted it to the database.

    If one just looks at the "key" to select the row, then Person A can easily delete Persons B's work.

  • (disco) in reply to TheCPUWizard

    Two words: Database transactions.

  • (disco) in reply to TheCPUWizard

    If the CRUD is properly wrapped in transactions, then it doesn't matter; one action will complete, and the other will raise an error that can be handled properly.

    Also, Hanzo'd.

    <!-- Emoji'd by MobileEmoji 0.2.0-->
  • (disco) in reply to RaceProUK

    Transactions will save you if you're reading and writing at the same time (and you have pessimistic locking, yuck). But that doesn't prevent the sorts of race conditions that (I think) @TheCPUWizard is talking about. That's usually called optimistic locking.

    For instance, you might keep a version field that you always increment when you write to the record. So you start a transaction and then make sure it hasn't changed from when you first read the data. If you don't want to "waste" the space, you can CHECK ALL THE THINGS like the article might be doing. I say might, because :wtf:...ain't nobody got time to go through and verify that shit.

  • (disco)

    Two more words: "optimistic concurrency".

    Database transactions are not designed to lock records while they're being viewed or edited in an application by users.

  • (disco)

    Let's add a column or two, and then SearchGuard is no more safe until the web form has been updated too...

  • (disco)

    I can imagine something like this being generated by an ORM.

  • (disco) in reply to Vilx

    Microsoft's LINQ-to-SQL does exactly this if the table doesn't have a timestamp column to implement optimistic concurrency. It does this for both updates and deletes. I usually add a timestamp column to frequently edited tables to improve performance for this reason.

  • (disco)

    WTF: Deleting an employee record.

    T**R**WTF: There is no "status" field on the employee record.

  • (disco) in reply to Bananafish

    Obviously if the record exists, status is active, if not status is inactive. Duh

  • (disco)

    TRWTF is that someone thinks they know enough about programming to recognise a WTF but didn't know what this is.

    It's quite obviously an auto-generated statement implementing optimistic locking.

    If the row is not found (rowcount = 0) you get an error saying "someone else has edited that record. Click OK to load the latest version" or similar. Such statements are auto-generated by VB6/ADO.Net from the database schema. You don't write them yourself.

  • (disco) in reply to benhaha

    If you're deleting a record, do you really care if someone changed it?

  • (disco)

    I gotta admit, I'm not seeing the WTF. It's kind of ugly, but it's the only safe solution in a bunch of cases. I mean, obviously, yeah, this could be auto-generated code. Or:

    (1) If you are in a shop that insists that everything has to be done through a stored procedure ("that's the real WTF?") then you end up with a stored proc like this for every table a front end developer might delete from.

    I'm betting that's the situation here. This (possibly changed and anonymized) code is out of a stored procedure where each of those column values was passed as a parameter. It may or may not be surrounded by other code that does different verifications. It might still be a WTF but it might not be, depending on context.

    (2) As TheCPUWizard suggests, this could be to make sure the row hasn't changed. Transactions won't help for a lot of types of applications where someone can pull up a record, sit there staring at it for a half an hour, then make a change and/or delete it. You can't lock the record for all that time, and this is the cheap and easy solution.

    If I'm deleting a record, I think I do care if someone changed it. There's a good chance one of us is doing something wrong. It might be them, it might be me, but either way I would want to see those changes first.

  • (disco) in reply to beeporama
    beeporama:
    This (possibly changed and anonymized) code is out of a stored procedure where each of those column values was passed as a parameter.

    Yes. I cut off the top of the code because it was long enough already, but it began ALTER PROCEDURE

    beeporama:
    It may or may not be surrounded by other code that does different verifications

    It was not. There was a list of params, then SET NOCOUNT OFF;, then the delete statement. It may have had other lines in the codebase that the submitter removed, but then, the submitter could have made up the whole thing in the first place...

    beeporama:
    If you are in a shop that insists that everything has to be done through a stored procedure ("that's the real WTF?") then you end up with a stored proc like this for every table a front end developer might delete from.

    Our shop does mandate procs, though, and we tend to either 1) set a deleted flag on the record or 2) delete from a series of normalized tables using the ID as the input. So the two do not go hand in hand.

  • (disco) in reply to Yamikuronue

    Thanks for providing some context. It certainly makes me question that this is an effective way of doing things.

    Although not quite a "WTF" so much as inexperience, dealing with something important like Employee records you probably do want to use a flag and do a "logical delete" or somehow deactivate the record instead of a real permanent delete.

    And as you say, I do wonder why this doesn't just use an ID or primary key fields, unless it is (as TheCPUWizard suggested) some way of catching changed records. Although I suppose then that a timestamp would make a lot more sense.

    So... now I'm really scratching my head, at least, wondering what the point of doing it this way was.

  • (disco)

    SearchGuard is great. It's just too bad that they found the rows to be deleted by using this select:

    SELECT {column list} FROM [dbo].[tbl_employee] WHERE 1 = 1
    

    SearchGuard carefully matched each selected row in turn before deleting it, just as expected.

  • (disco)

    Looks exactly like the code .Net datasets generate when you let them create your stored procedures through a strongly typed dataset.

    Or, someone's adaptation of that... shudder

  • (disco) in reply to boomzilla
    boomzilla:
    LOL. Nice video.

    Interesting. On the main TDWTF page, the entire article synopsis, including the hyperlinked SearchGuard, just links to the full article page. So the youtube link is hidden unless you open the full article. Bug or feature?

    Bonus DiscoWTF: no underline widget in the editor!

  • (disco) in reply to cconroy

    Technically the Youtube link isn't exactly hidden, the entire article is overlaid with the <a> element inside the <article> element.

    If you delete it with an HTML inspector, you'll find you can now click the YouTube link, but can't get into the article page proper.

  • (disco)

    Would you guys have preferred an embed?

  • (disco) in reply to Yamikuronue

    I think the complaint is that clickable elements don't work on the front page, it is likely even an embed wouldn't work. Trying it now... ;)

    Edit: Answer: No. No it does not.

  • (disco) in reply to Yamikuronue

    I don't think an embed is necessary (even if it did happen to solve the problem). It would be nice if the links worked on the front page, is all. Personally I don't see why the whole synopsis needs to be clickable. A standard "Read more..." link at the bottom of each synopsis should suffice, and additionally would indicate that there's more unseen content available (which is not obvious at all in the current design).

  • (disco) in reply to cconroy

    Sadly, I have no say over the front page design :)

  • (disco) in reply to Yamikuronue

    I figured, just rambling. Thanks for listening though. :smile:

  • (disco)

    Who actually deletes records?

    Hide records? Sure! Delete records? Nope!

  • (disco) in reply to realee
    realee:
    Who actually deletes records?

    Hide records? Sure! Delete records? Nope!

    Depends on the system; sometimes it's OK to delete records. <!-- Emoji'd by MobileEmoji 0.2.0-->
  • (disco) in reply to mtr

    Very good point bringing up isolation levels as that is sometimes overlooked. If you have full control over the app, this problem can be resolved by using them.

    In Oracle the default isolation levels maintains a copy of the data being changed in memory until all locks are removed, then the new value is returned. This allows reads to continue and get the full old row which helps you ensure you don't get a full dirty read (such as half the row is updated, the other half is not) while not blocking writes.

    SQL Server by default blocks readers and writers but you can change the isolation level per query or server wide to act more like Oracle but it adds about 14 bytes per row if I'm not mistaken.

  • (disco)

    The query would not be much WTF if the query is generated by some query generation tool but there's no identity column on the table. (Yes I see there is employee_id, but I've seen countless time where xxx_id is not unique. So maybe TRWTF is employee_id is not identity field only)

  • (disco) in reply to cconroy

    You can submit it here, I guess: https://github.com/tdwtf/WtfWebApp

  • (disco)

    Who cares if the code is generated? Anyone who doesn't delete the rest after testing the employee ID should be shot on sight. Or are there multiple employees with the same ID?

    Also:

    @IsNull_sex = 1 AND [sex] IS NULL) OR ([sex] = @Original_sex

    I can't help wondering... Which is better, null sex or original sex?

  • (disco) in reply to Jerome_Viveiros
    Jerome_Viveiros:
    Which is better, null sex or original sex?

    Well... null sex would indicate no sex whatever. Original sex would indicate not only sex, but multiple occurrences of it.

  • (disco) in reply to boomzilla

    More than ever. If the record is no longer wanted why is it being updated? What do they know about the record that I don't?

  • (disco)

    I see this kind of code quite often when dealing with slow queries (usually from a generic search page or a generic search_employees function). Depending on database this will more or less randomly pick any index on the table (since you are searching on "that" column) or it will pick no index at all since none of the indexes are good enough. Build the query with 2-3 joins on some large tables and look at the nested loop full table scans eat all your IO capacity.

  • (disco) in reply to boomzilla
    boomzilla:
    If you're deleting a record, do you really care if someone changed it?

    Yes, for the exact reason I mentioned previously.

  • (disco) in reply to MRAholeDBA
    MRAholeDBA:
    In Oracle the default isolation levels maintains a copy of the data being changed in memory until all locks are removed, then the new value is returned. This allows reads to continue and get the full old row which helps you ensure you don't get a full dirty read (such as half the row is updated, the other half is not) while not blocking writes.

    MVCC FTFW

  • (disco)

    Has anyone ever heard of read serializable? Locks whatever is read until the transaction completes

  • (disco)
    DELETE FROM [dbo].[tbl_employee]
    

    Gee, it's sure great that someone prepended "tbl_" to all our table names, otherwise I might forget that they were tables!

    With all those conditionals and parameters, I have a feeling that this is going to have wonderful parameter sniffing issues, notwithstanding whatever the developer was sniffing when he wrote this.

  • (disco) in reply to Groaner
    Groaner:
    Gee, it's sure great that someone prepended "tbl_" to all our table names, otherwise I might forget that they were tables!

    At a previous job, all table names were in the form TSOMETHING, and all views were VSOMETHINGELSE

    Then some tables were replaced with others. For backwards compatibility reasons, the old table names still had to be addressable, so they were replaced by views with INSTEAD OF triggers so inserts, deletes and updates work on the new table. So now they have tables with the T prefix, views with the V prefix and views with the T prefix that when updated actually update other tables with the T prefix

    Oh, and another job had hungarian notation on column names, so a tinyint column would be tisomething, a varchar(30) columnwould be sz30something. Lots of columns were changed to larger data types over the years so there were ti columns holding ints and sz30 columns for varchar(200). Fun times

  • (disco) in reply to Jaloopa

    When I read about that hungarian notation on column names I imediately thought: Oh my, then what about later changes to data types (extending varchars for instance) and yep, your next sentence justified my fears... :smile:

  • (disco) in reply to Yazeran

    It makes sense if it's a bit of a toy project or something that's only expected to last a few years before being replaced, but this was a major database for a large multinational, and the product was 20+ years old. I was there in 2010, and it was using C++/MFC for the GUI.

  • (disco) in reply to Jaloopa

    Sure for a toy project it's ok, but then again, for a toy project why bother?

    In the case if a small project (or toy project), in most cases a single (of few) developers willwork on it and hungarian notation will not be nescesarry as they know that column 'user' in table 'xxx' is in fact not the username but the userid istead.

    For larger projects (or projects expected to last long), you are much better of by using descriptive comumn names wich by themselves implies what data type it is (in the former case by using 'userid' instead of 'user'. then userid's can be shortints, ints or even (shudder) varchars without confusing the future developers.

  • (disco)

    The real wtf is this is for deletes, I would asumme that the shop either uses row-level locks (which have to be cleaned up when they are not released in a short time) or that the shop also has a procedure that handles updates in the case a second user has modified the record between the time the first user has fetched the record and he has tried to save it.

    I saw a much better solution (in Oracle) which also can be implemented in sql server 2012.

    Create a single sequence in your schema. For every table in your schema, add a column (call this column ROWSTAMP) that stores the next value from your schema sequence. For each table in your schema, create a row-level trigger that fires on insert/update, that gets the next value from the sequence object and sets the ROWSTAMP using that value. All Update and delete statements must include a clause that says "and ROWSTAMP = @Original_ROWSTAMP' or something similer, this prevents updates/deletes from completing unless it is still the same starting record.

  • (disco) in reply to Jaloopa

    As I recall from my Oracle days, INSTEAD OF triggers have this endearing quality that they are silently dropped whenever the view is updated.

  • (disco) in reply to Onyx
    Onyx:
    Well... null sex would indicate no sex whatever. Original sex would indicate not only sex, but multiple occurrences of it.

    No, null sex would be undefined sex. It could be interesting, but OTOH you could wind up having sex with the big hairy person of your own gender.

  • (disco) in reply to Jaloopa
    Jaloopa:
    At a previous job, all table names were in the form TSOMETHING, and all views were VSOMETHINGELSE

    Then some tables were replaced with others. For backwards compatibility reasons, the old table names still had to be addressable, so they were replaced by views with INSTEAD OF triggers so inserts, deletes and updates work on the new table. So now they have tables with the T prefix, views with the V prefix and views with the T prefix that when updated actually update other tables with the T prefix

    Thanks for providing a real-life example of the end result of what Joe Celko calls Volkswagen coders.

  • (disco) in reply to Vault_Dweller

    Definitely not! If an employee is active, (s)he might have a status of 'A' or 'F' for full-time and 'P' for part-time. The same employee might one day have a status of 'T' for TERMINATED. As in, not active.

    So, let's just say, you have an employee that leaves the company in October. If your HR database DELETES the employee because (s)he is no longer working there (meaning, of course, "Active") then how do you print W-2 information the following January? If this is what you do, please tell us where you'll be so we can visit you in prison.

  • (disco) in reply to Bananafish
    Bananafish:
    If your HR database DELETES the employee because (s)he is no longer working there (meaning, of course, "Active") then how do you print W-2 information the following January?

    Obviously, you do a diff with the backup from the last tax year, and see which employees aren't there any more

Leave a comment on “SearchGuard”

Log In or post as a guest

Replying to comment #:

« Return to Article