[TYPO3-core] RFC #15457: Add support for prepared queries
Xavier Perseguers
typo3 at perseguers.ch
Sun Aug 15 09:54:31 CEST 2010
Hi Steffen,
> fantastic, i love to see that!
Thx.
> Here are some annotations from reading:
>
> is there a reason why you used 5 and 10 for BOOL and AUTOTYPE?
5 is the internal value when using PDO, AUTOTYPE=10 is arbitrary (it
does not exist in PDO) to have "space for other types". But as it's more
or less "internal" and should only be accessed through constants, we may
have arbitrary consecutive values as well.
> i don't like the naming of stmt_execute (too cryptic abbreviation). Why
> not preparedQuery_execute, or like the others exec_PREPAREDquery
Taken from MySQLi (http://ch.php.net/manual/en/mysqli-stmt.execute.php)
but exec_PREPAREDquery is OK I guess.
> * $key = is_numeric($parameter) ? intval($parameter) - 1 : $parameter;
> why -1 ? why not use (string) $parameter always?
string is for named parameters, integer is for indexed-based parameters,
thus for '?'. This is the same in PDO.
"-1" because when using ->bindValue(), you have to give a 1-base index
as first argument. This comes from PDO and I'd like to keep it like that
for possible forward compatibility if we are able to use PDO in the
future. But as internally arrays are 0-based, I make the conversion at
setter time.
> why choosing "thaw"? I looked up the translation but can't associate
> what is meant.
:-) that was one of the point where I did not know which verb to choose.
I took the naming from Extbase code where the data mapper "thaws" the
property values onto an object. I understood it as some synonym of
"merge" if you look a translation.
> what happen ig thawValuesInQuery is called without PARAM_AUTOTYPE and
> wrong type?
Don't understand what you mean. The test for PARAM_AUTOTYPE is needed
because of bindParam() where the type as well as the value of the
parameter is determined at execution time, not at binding time. Will add
a comment for that in code. This method is internal anyway meaning the
"world" cannot use it without relying on the official API which makes
sure all is properly defined.
But you're right, I'll add constraints as InvalidArgumentException if
the values do not match authorized values (unknown type or obviously
wrong information with a string defined as INTEGER).
> $stmt - see above, better naming
OK.
> if ($parameterReferences == NULL) {
> use strict here
OK.
> protected function &getValueOrParameter(...
> why is the reference here? Could you add this to the description?
You need the & in order to have PHP return a reference to the value it
created inside, otherwise you only get its value (but have it as
reference inside parameterReferences) and you cannot rely on putting the
real value inside parameterReferences to have it updated in all parts of
the where clause where it appears.
> I also don't know the parser very well, so can you explain how
> parameterReferences are filled? Maybe related to last mentioned function?
As explained briefly before, the point of having this global array is to
have a quick access to all parameters used in query.
To make it brief while explaining how the SQL parser works, let's take a
simple example:
SELECT uid, pages.title FROM pages WHERE pid = :pid
The SQL parser (t3lib_sqlparser) will parse this into a structure
similar to this one:
$components = array(
'type' => 'SELECT'
'SELECT' => array(
array(
// ...
'field' => 'uid',
// ...
),
array(
// ...
'table' => 'pages',
'field' => 'title',
// ...
),
),
'FROM' => array(
// ...
'pages',
// ...
),
'WHERE' => array(
array(
// ...
'pid' => array(
array(
0 => ':pid',
1 => '',
),
),
// ...
),
),
);
I did not dump a real components array, but this is basically how it
looks like.
When you compile it again, SQL parser generates the query back, as a string.
Now the point is that it would be dumb to loose the information of where
the parameters where parsed and it would be counter-productive to take
the whole component tree and dig into it at runtime to replace
placeholders with real values to be used for the current execution. This
is why I added a "parameters" structure at level 0 of the components
array to reference all parameters. Previous query looks more like that
actually:
'WHERE' => array(
array(
// ...
'pid' => array(
---> pointer to parameters[':pid']
),
// ...
),
),
'parameters' => array(
':pid' => array(
0 => ':pid',
1 => '',
),
),
This way, I'm able to replace all :pid placeholders which a single
statement:
$components['parameters'][':pid'][0] = 52;
and when compiled again, query will be:
SELECT uid, pages.title FROM pages WHERE pid = 52
When using DBAL, I use an intermediate structure to prevent having to
compile the whole query again and again at each execution but still have
a way of properly know where the parameters are. For instance without
the parser, how could you know that this query only contains one
question mark parameter?
SELECT * FROM pages WHERE pid = ? AND title = 'How to?'
> For testing i would like to see unit tests using the prepared
> statements, would make it much easier ;)
Some unit tests are available but I wrote them in DBAL package as I
already did before. Have a look at DBAL patch in BT. I wrote all my unit
tests in DBAL as of today because SQL parser was only used when using
DBAL and the problems did not appear without it. As prepared queries
will start being used in Core after this RFC, I think it would be wise
to have prepared query-related unit tests in Core, as usual.
> Thanks for your great work and sry for the nitpicking.
NP. I like to get feedback because it allows improving what gets "in" :-)
I'll wait a bit for other comments before proposing another version with
your "nitpicking" taken into account.
Xavier
More information about the TYPO3-team-core
mailing list