Drupal querying mechanism is based on:
- having a query with placeholders that will be replaced by values sanitized through
db_escape_string() - tables inside {} will be processed for prefixing
- if we want to use ranges, will need to use
db_query_range() - if we want to rewrite a node query we will
use db_rewrite_sql() - the system always tries to parse the query to inject things like "limits"
Kind of queries that can be done
Dummy queries
Usually found in the form of "SELECT * FROM table", and we know what this does to the application performance.
If a guy, avoids to write fields he wants to retrieve, it is almost sure that he will *NOT* sanitize is parameters and instead of writing SELECT field1, field2 FROM table WHERE field3 = '%s' he will write SELECT * FROM table WHERE field3 = '$field3'
node.module
Writing the fields is so boring, but when we got to develop a new node type we have no way to avoid it: we really have to write the necessary SQL for SELECT, UPDATE, INSERT and DELETE; and, with sanitized fields! By the way, do you ever had to write node types with more than 10 fields?!
Before Drupal 4.7, even the node.module implemented a strange node_save() that did not obey to fields types and only used sanitized strings.
Now, yes, it uses the correct placeholders to replace with the correct data.
Did you ever take a look at the code on node_save()? Yes, we could have less code.
Probably it would be much more simple just do a:
$fields = array(... defined fields ...);
db_query_ex_fillfields($fields, $node);
db_query_ex($fields, 'node', 'insert');
db_query_ex()
This function appears to help the construction of SQL for node API (load, delete, insert and update), with the potential to increase validation and communication between form API and database.
Things that this actually do:
- Define name, type, key, required for fields
- Constructs SQL for operations: SELECT, INSERT, UPDATE, DELETE
- Uses the 'key' definition for modification operations
- Uses 'value' information for retrieving operation
- Correctly uses types: string, int, float and bool
- Helper function to transform mysql table structure in Drupal fields definition
- Reduces the code (improving readability and security) in about 50%
- Non required fields can be update to NULL in the database
What can be implemented:
- Logic for multi-joins at the $tables parameter with aliases and join types
- Parameter for limits, group, order
- Logic for expressions at 'value' field definition
- Connection with FAPI, at the validation level, where FAPI can use the fields definition to better understand how controls should behave and validate
- Add 'date' type (based on 'string') but with knowledge by the logic
- Implement non-explicit array fields definition (which would reduce much more the code size)
- Add 'default' definition (at least for required fields)
- Full database abstraction
Definition
$fields = array of $field;
$field = array of
'type' (string|int|float|bool)
'key' (is primary key)
'required' (field is required)
'value' (value to be used for filter or data modification)
$tables = table name or array of tables
$op = (select|insert|update|delete)
This would do a select ($fields array should be re-used over and over again):
$fields = array(
'nid' => array('type' => 'int', 'key' => TRUE, 'required' => TRUE),
'vid' => array('type' => 'int', 'required' => TRUE),
'title' => array('type' => 'string', 'required' => TRUE),
'type' => array('type' => 'string', 'required' => TRUE),
'uid' => array('type' => 'int', 'required' => TRUE),
'status' => array('type' => 'int', 'required' => TRUE),
'created' => array('type' => 'int', 'required' => TRUE),
'changed' => array('type' => 'int', 'required' => TRUE),
'comment' => array('type' => 'int', 'required' => TRUE),
'promote' => array('type' => 'int', 'required' => TRUE),
'moderate' => array('type' => 'int', 'required' => TRUE),
'sticky' => array('type' => 'int', 'required' => TRUE)
);
$fields['nid']['value'] = 1;
db_query_ex($fields, 'node', 'select');
How about an insert:
$fields = _get_fields_definition();
db_query_ex_fillfields($fields, $node);
db_query_ex($fields, 'my_table', 'insert');
Hope you like this technology preview!
| Comment | File | Size | Author |
|---|---|---|---|
| db_query_ex.inc_.txt | 8.68 KB | magico |
Comments
Comment #1
AmrMostafa commentedA much needed feature that I'd LOVE to see, but I'm not sure of the approach. It may be better to take the Model (M-in-MVC) approach.
IIRC a Data API, which is similar (if not the same as) to MVC Models is in the works, I'm not sure of the status.
http://lists.drupal.org/archives/development/2006-09/msg00037.html
Comment #2
magico commentedI also don't know the status. Anyway, the idea behind this is not unify form with data (because they have different needs) but help them to be connected.
The code I present here, is really simple and I'm using it without problem. The biggest win will be when it will understand tables joins and alias. Then we can do things like this:
or even this
A constant re-use of defined fields and tables is a must have!
Comment #3
BioALIEN commentedSubscribing...
Comment #4
agentrickardWe were discussing this internally last weeek, and I'd push this a little farther.
Just like FormsAPI means that developers don't have to write forms 'by hand,' query abstraction should allow queries to be written without SQL statements. I think that the db_query_ex() proposal still places too much on the developer, and I really don't like having an $op switch case. Since most SQL is simple selects, I'd define specific functions for most cases, with a clearly documented protocol for which function to use in which case.
For example:
db_select()for simple selects.db_select_join()for a two-table INNER join.db_insert()for inserts.db_update()for updates.This system would allow for more control over the SQL syntax that actually gets executed. It would also let the query-writing function handle data sanitization. Having a
db_select_join()and perhaps adb_select_multijoin()would indicate to new developers just how dangerous the use of JOINs can be. Authors would use db_select() whenever possible. And we could still reservedb_query()for complex SQL statements; it would just be discouraged.But as for constructing queries via passed arrays rather than raw SQL, I'm all for it.
Comment #5
agentrickardWe could have a
db_count(), too, which would remove all those nasty"SELECT COUNT(*) FROM {table}" queries that drive my DBA crazy.Comment #6
magico commenteddb_query_ex()already does data sanitazion.I'm apologist of an unique function, that should accommodate for both novice and experienced users. Anyway, because we need a central function to process the queries, it would be just a manner of writing the "simplified" functions as aliases for the main function.
BTW, the only way to get this feature in is to test what is already done and get some attention from one senior developer that sees this as an important feature.
db_count()is so needed yes; but we have to make it in a way that avoid a user to do:because we all know what that will implies.
If someone, wants to share his thoughts and test the source I provided, I would appreciate your time.
Thanks
Comment #7
Crell commentedWe already have a db_insert(), db_update(), and db_delete() in the helpers module. They're in the helpers module because Dries rejected them from core. I'd love to see them in core as well as better dynamic select-statement creation, but I don't see it happening any time soon unless you can convince Dries that a query builder is worth the effort.
Comment #8
agentrickardHooray for institutional memory!
Comment #9
magico commentedAnd I agree with him, as you can see from my response above.
The advantages of a built-in query builder that builds queries from logic, are enormous: easier to understand, much more re-use of code (with the consequent reduction of code size) and surely more performance than db_query without it's
preg_replace_callback.I'm sure Dries will pay some attention to this, because we all know how Drupal got a bit slower... I think I have to do some tests with
db_query_ex()againstdb_query()(and of course not using db_query to execute the query)Comment #10
magico commentedMust use the new abstract schema http://drupal.org/node/136171
Comment #11
wim leersSubscribing.
Comment #12
Crell commentedLet's continue this in the new thread: http://drupal.org/node/225450