News for hackers. Stuff that matters.
Re:Your templating engine sucks and everything you've ever w... by pyre on Sunday December 11, 2011 @01:19AM (#3339431)

  > As soon as I'm looking at more than one programming or
  > markup language in the same file, I'm looking at
  > spaghetti code.
By this definition the following is 'spaghetti code':

  $dbh->do("delete from my_table");
Following his logic:

  1. Realize there's SQL and Perl in the same line.
  2. STOP EVERYTHING
  3. Start attempting to fully abstract SQL into Perl
     so that no one will ever need to write a line of
     SQL again.
  4. Die an old man, never having accomplished this task.
Re:Your templating engine sucks and everything you've ever w... by postfuturist on Sunday December 11, 2011 @01:43AM (#3339473)
In small doses, you can get things done quickly like this. However, it builds up over time into a ball of spaghetti, so yeah, maybe after the 5th or 6th blob of inline SQL, you might think about using an abstraction while you still can. I've worked (and work) on old projects with hundreds of SQL queries spread throughout the code, it _is_ spaghetti code and there are better ways to do it.
Re:Your templating engine sucks and everything you've ever w... by pyre on Sunday December 11, 2011 @01:48AM (#3339485)

  > I've worked (and work) on old projects with hundreds
  > of SQL queries spread throughout the code,
So have/do I. The fact that there is SQL inlined with the code is probably the least of your worries when talking about such a large/old code base.
Re:Your templating engine sucks and everything you've ever w... by postfuturist on Sunday December 11, 2011 @02:52AM (#3339576)
Actually, the bits of SQL everywhere is the #1 problem with the code. We spent weeks just auditing them all to remove SQL injection vulnerabilities. All the SQL are fragile bits of code, difficult to write meaningful unit tests for, since you can't separate database interaction from logic, etc.
Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @03:52AM (#3339664)
Been there too, which is why I like stored procedures now (testability, clean code, predictability).
Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @01:51AM (#3339492)
The issue with inline SQL queries isn't the fact that SQL is mixed in with other programming languages itself. It's the propensity to write BAD SQL that's inlined because now you can apply the full power of the programming language to generate your SQL.

Stored procedures are a great way to abstract this out and force good SQL coding practices btw.

Re:Your templating engine sucks and everything you've ever w... by ars on Sunday December 11, 2011 @02:33AM (#3339549)
> Stored procedures are a great way to abstract this out and force good SQL coding practices btw.

How exactly do stored procedures prevent you from writing "BAD SQL"? They do no such thing.

Stored procedures are a great way of adding complexity to your code, complexity equals bugs and longer development time. There are reasons for stored procedures, but using them simply to remove SQL from your front end is not one of them. And certainly not because of a fear of "BAD SQL".

Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @02:43AM (#3339563)
How exactly do stored procedures prevent you from writing "BAD SQL"?

To be clear, they prevent one specific case of bad sql, namely queries that are written like this:

SELECT a.*, $col_list

  FROM $table a

  JOIN $second_table b ON $join

 WHERE $where
$group_by

$having;

Then when you go to find out how $where is define you see a lot of statements like:

if ($http_query->{date_from}){

   $where .= " AND date_entered > " . $dbh->quote($http_query->{date_from});

}
Re:Your templating engine sucks and everything you've ever w... by pyre on Sunday December 11, 2011 @02:06PM (#3340736)
This is not always a bad thing. Consider the following:

  my $current_month = Month->new();
  my $end_month     = Month->new()->plus_months(4);
  while ($current_month <= $end_month) {
    $yyyymm = $current_month->strftme("%Y%m");
    $last_day = $current_month->last_date()->strftime("%Y%m%d");
    $first_day = $current_month->first_date()->strftime("%Y%m%d");
    
    $dbh->do("create table my_partitioned_table_$yyyymm
              () inherits from my_partitioned_table");
    $dbh->do("alter table my_partitioned_table_$yyyymm
              add constraint ck_my_partitioned_table_$yyyymm
              check (date between '$first_day'::Date and '$last_day'::Date)");

    $current_month->next_month();
  }
This makes sure that my_partitioned_table has month-based partitions 4 months into the future (so long as you keep it on cron).
Re:Your templating engine sucks and everything you've ever w... by enqk on Sunday December 11, 2011 @04:03AM (#3339676)
And if you forbid that in perl and end up having query generation inside the stored procedure, what have you gained?
Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @04:17AM (#3339695)
There are very few valid cases for stored procedures to generate queries like that. And at least with PostgreSQL, dynamic SQL can be spotted with automated searches pretty easily. It's pretty easy to simply add all filters that might be of interest:

WHERE (date_entered > in_date_from or in_date_from IS NULL) ....

Then you also gain testability. We can write test cases for the stored procedures because they have discrete entry points and make sure they are acting according to spec.

Re:Your templating engine sucks and everything you've ever w... by ars on Sunday December 11, 2011 @05:22AM (#3339785)
Why can't you use that type of code in your frontend?

Stored procedures make it much harder to understand the code since you can't just read the query - you have to find the stored procedure and check it there. (And be realistic - no one has perfect documentation.)

And heaven forbid you have to change the query - now you need to make sure no one else is using it, and you end up with tons of versions of similar stored procedures, not all in use, and many that could be combined.

It doesn't seem worth it to me.

I use stored procedures for data reduction - I want to keep the data in the database, and process it before sending it out. (Saves bandwidth vs doing it in the front end, and often it's faster too.)

But that's it. Using stored procedures just to wrap ordinary queries is a terrible (but very common) idea. (I am aware that it saves time on generating a query plan, but usually that's a case of premature optimization.)

And using it to enforce code policies? Way overkill.

Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @05:38AM (#3339808)
ars: I don't know if we have reached max depth or if it is just a waiting period. The major issues are:

1) Testability. you can write test cases against a stored proc. You cannot write test cases against a SQL query embedded in the middle of a function in another programming language.

2) Single point of control. You can have one function that runs the stored proc there and check properly for SQL injection, ensuring that everything is free of SQL injection as far as front-to-backend is concerned (and in Pg, that only leaves SQL injection issues in dynamic SQL which can be searched for and audited separately).

So that's why not to put it there.

As to changing the query.... I only use databases which come with transactional DDL so that's not an issue as long as inputs and outputs don't change. If the inputs and outputs have to change, we can handle that too with transactional DDL and the fact that we make the interfaces discoverable. In this case, we'd

BEGIN;

DROP FUNCTION IF EXISTS foo_bar(...);

CREATE OR REPLACE FUNCTION foo_bar(...)...;

COMMENT ON FUNCTION foo_bar(...) IS $$...$$;

COMMIT;

Because of the way Pg works this shouldn't really cause issues for currently running queries.

As for finding the right function, since these are all called on a declarative interface so you know what the function name is quickly, and so it is a matter of quickly finding it in a directory of SQL files.

As for testability, don't underrate this. You can insert data, run your tests, and roll back. So for example for our 1099-MISC and INT handling, we run more than 40 tests on a pre-written data set that we can run without ever committing to production and which will never conflict with production data (and therefore does not lock other users out). That's approx. 20 tests per sproc there.

Re:Your templating engine sucks and everything you've ever w... by notaddicted on Sunday December 11, 2011 @01:33AM (#3339457)
I've actually seriously considered keeping all my SQL queries in their own files, and reading them into local variables on load. It would allow them to be tracked and edited individually... but I now use an ORM.
Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @01:52AM (#3339494)
I do keep the SQL queries in their own files. The only difference is that they get loaded into the db (as stored procs) at first and then called by name. Works well.
Re:Your templating engine sucks and everything you've ever w... by draegtun on Sunday December 11, 2011 @05:39AM (#3339809)
This is called the Phrasebook pattern and was probably being pushed as best practise until ORM's took hold.

* http://ootips.org/yonat/patterns/phrasebook.html

* http://www.perl.com/pub/2002/10/22/phrasebook.html

* https://metacpan.org/module/Data::Phrasebook::SQL

Re:Your templating engine sucks and everything you've ever w... by pyre on Sunday December 11, 2011 @01:51AM (#3339491)
The point being that ORMs have limitations. Advocating that something is 'one-size-fits-all' is foolish (not claiming that that's what you're doing).
Re:Your templating engine sucks and everything you've ever w... by ExpiredLink on Sunday December 11, 2011 @07:53AM (#3339953)
> .. but I now use an ORM.

"ORM is an anti-pattern": http://seldo.com/weblog/2011/08/11/orm_is_an_antipattern

Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @09:10AM (#3340083)
Agreed ORM's are usually very sub-optimal. They make sense only because application developers aren't db people. And this is the problem.

I suspect for certain parts of some apps, ORM's might work OK. But in general for applications of any complexity they will break down hard and fast, and lead to bad db design, bad performance, or both.

Re:Your templating engine sucks and everything you've ever w... by MatthewPhillips on Sunday December 11, 2011 @09:15AM (#3340092)
ORM is just less work. If you write in OOP you are already creating objects anyways. Then writing get, save, and update procedures is just a lot of extra work.
Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @09:37AM (#3340120)
The problem is then you build databases around your ORM but if you are doing a single app database, why use an RDBMS at all?

In other words, I see an ORM as an antipattern because the subset of cases where it works really well at bridging the gap you probably don't really want an RDBMS in there anyway.

And if you do make it work (encapsulating your db behind updatable views) without running into this, it's a heck of a lot more work than hand-coding SQL.....

Re:Your templating engine sucks and everything you've ever w... by einhverfr on Sunday December 11, 2011 @01:35AM (#3339463)
I don't consider that spaghetti code, but it does have problems. The big one is the tendency to assemble queries by string interpolation....

What we've done in LedgerSMB is quite simple. All db statements in new code (outside the mapper functions) are in stored procedures. Everything is mapped in there using a Perl API so in the new code (post-1.2) otherwise all SQL is in one set of files and all Perl is in another, with minimal overlap.

This sort of thing makes for very clean development. No, spaghetti code is making me have 15 files open in VIM trying to figure out which of them has the function that is being called in it, with several candidates all bearing the same function name (oh, the horrors).

Re:Your templating engine sucks and everything you've ever w... by draegtun on Sunday December 11, 2011 @05:34AM (#3339802)
Instead of points 3 & 4 you could replace it with... 3. Load SQL module from CPAN.

Something like SQL::Abstract (https://metacpan.org/module/SQL::Abstract) or Fey (https://metacpan.org/module/Fey) would slot in easy for most tasks like this:

  use SQL::Abstract;
  my $sql = SQL::Abstract->new;

  $dbh->do( $sql->delete("my_table") );