Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Sign in
Toggle navigation
D
drupal
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Insights
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Drupal.org issue queue
Drupal.org issue queue
Security & Compliance
Security & Compliance
Dependency List
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
project
drupal
Commits
1731cd48
Commit
1731cd48
authored
Apr 20, 2015
by
alexpott
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue
#2465633
by amateescu: Bring back the custom Statement class for the SQLite driver
parent
ce498b91
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
161 additions
and
65 deletions
+161
-65
core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
+13
-63
core/lib/Drupal/Core/Database/Driver/sqlite/Statement.php
core/lib/Drupal/Core/Database/Driver/sqlite/Statement.php
+146
-0
core/lib/Drupal/Core/Database/StatementPrefetch.php
core/lib/Drupal/Core/Database/StatementPrefetch.php
+2
-2
No files found.
core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
View file @
1731cd48
...
...
@@ -52,6 +52,10 @@ class Connection extends DatabaseConnection {
* Constructs a \Drupal\Core\Database\Driver\sqlite\Connection object.
*/
public
function
__construct
(
\PDO
$connection
,
array
$connection_options
)
{
// We don't need a specific PDOStatement class here, we simulate it in
// static::prepare().
$this
->
statementClass
=
NULL
;
parent
::
__construct
(
$connection
,
$connection_options
);
// This driver defaults to transaction support, except if explicitly passed FALSE.
...
...
@@ -255,69 +259,8 @@ public static function sqlFunctionRegexp($pattern, $subject) {
/**
* {@inheritdoc}
*/
protected
function
expandArguments
(
&
$query
,
&
$args
)
{
$modified
=
parent
::
expandArguments
(
$query
,
$args
);
// The PDO SQLite driver always replaces placeholders with strings, which
// breaks numeric expressions (e.g., COUNT(*) >= :count). Replace numeric
// placeholders in the query to work around this bug.
// @see http://bugs.php.net/bug.php?id=45259
if
(
empty
(
$args
))
{
return
$modified
;
}
// Check if $args is a simple numeric array.
if
(
range
(
0
,
count
(
$args
)
-
1
)
===
array_keys
(
$args
))
{
// In that case, we have unnamed placeholders.
$count
=
0
;
$new_args
=
array
();
foreach
(
$args
as
$value
)
{
if
(
is_float
(
$value
)
||
is_int
(
$value
)
||
is_numeric
(
$value
))
{
if
(
is_float
(
$value
))
{
// Force the conversion to float so as not to loose precision
// in the automatic cast.
$value
=
sprintf
(
'%F'
,
$value
);
}
$query
=
substr_replace
(
$query
,
$value
,
strpos
(
$query
,
'?'
),
1
);
}
else
{
$placeholder
=
':db_statement_placeholder_'
.
$count
++
;
$query
=
substr_replace
(
$query
,
$placeholder
,
strpos
(
$query
,
'?'
),
1
);
$new_args
[
$placeholder
]
=
$value
;
}
}
$args
=
$new_args
;
$modified
=
TRUE
;
}
// Otherwise this is using named placeholders.
else
{
foreach
(
$args
as
$placeholder
=>
$value
)
{
if
(
is_float
(
$value
)
||
is_int
(
$value
)
||
is_numeric
(
$value
))
{
if
(
is_float
(
$value
))
{
// Force the conversion to float so as not to loose precision
// in the automatic cast.
$value
=
sprintf
(
'%F'
,
$value
);
}
// We will remove this placeholder from the query as PDO throws an
// exception if the number of placeholders in the query and the
// arguments does not match.
unset
(
$args
[
$placeholder
]);
// PDO allows placeholders to not be prefixed by a colon. See
// http://marc.info/?l=php-internals&m=111234321827149&w=2 for
// more.
if
(
$placeholder
[
0
]
!=
':'
)
{
$placeholder
=
":
$placeholder
"
;
}
// When replacing the placeholders, make sure we search for the
// exact placeholder. For example, if searching for
// ':db_placeholder_1', do not replace ':db_placeholder_11'.
$query
=
preg_replace
(
'/'
.
preg_quote
(
$placeholder
,
'/'
)
.
'\b/'
,
$value
,
$query
);
$modified
=
TRUE
;
}
}
}
return
$modified
;
public
function
prepare
(
$statement
,
array
$driver_options
=
array
())
{
return
new
Statement
(
$this
->
connection
,
$this
,
$statement
,
$driver_options
);
}
/**
...
...
@@ -386,6 +329,13 @@ public function mapConditionOperator($operator) {
return
isset
(
$specials
[
$operator
])
?
$specials
[
$operator
]
:
NULL
;
}
/**
* {@inheritdoc}
*/
public
function
prepareQuery
(
$query
)
{
return
$this
->
prepare
(
$this
->
prefixTables
(
$query
));
}
public
function
nextId
(
$existing_id
=
0
)
{
$this
->
startTransaction
();
// We can safely use literal queries here instead of the slower query
...
...
core/lib/Drupal/Core/Database/Driver/sqlite/Statement.php
0 → 100644
View file @
1731cd48
<?php
/**
* @file
* Definition of Drupal\Core\Database\Driver\sqlite\Statement
*/
namespace
Drupal\Core\Database\Driver\sqlite
;
use
Drupal\Core\Database\StatementPrefetch
;
use
Drupal\Core\Database\StatementInterface
;
/**
* Specific SQLite implementation of DatabaseConnection.
*
* The PDO SQLite driver only closes SELECT statements when the PDOStatement
* destructor is called and SQLite does not allow data change (INSERT,
* UPDATE etc) on a table which has open SELECT statements. This is a
* user-space mock of PDOStatement that buffers all the data and doesn't
* have those limitations.
*/
class
Statement
extends
StatementPrefetch
implements
StatementInterface
{
/**
* SQLite specific implementation of getStatement().
*
* The PDO SQLite layer doesn't replace numeric placeholders in queries
* correctly, and this makes numeric expressions (such as COUNT(*) >= :count)
* fail. We replace numeric placeholders in the query ourselves to work
* around this bug.
*
* See http://bugs.php.net/bug.php?id=45259 for more details.
*/
protected
function
getStatement
(
$query
,
&
$args
=
array
())
{
if
(
count
(
$args
))
{
// Check if $args is a simple numeric array.
if
(
range
(
0
,
count
(
$args
)
-
1
)
===
array_keys
(
$args
))
{
// In that case, we have unnamed placeholders.
$count
=
0
;
$new_args
=
array
();
foreach
(
$args
as
$value
)
{
if
(
is_float
(
$value
)
||
is_int
(
$value
))
{
if
(
is_float
(
$value
))
{
// Force the conversion to float so as not to loose precision
// in the automatic cast.
$value
=
sprintf
(
'%F'
,
$value
);
}
$query
=
substr_replace
(
$query
,
$value
,
strpos
(
$query
,
'?'
),
1
);
}
else
{
$placeholder
=
':db_statement_placeholder_'
.
$count
++
;
$query
=
substr_replace
(
$query
,
$placeholder
,
strpos
(
$query
,
'?'
),
1
);
$new_args
[
$placeholder
]
=
$value
;
}
}
$args
=
$new_args
;
}
else
{
// Else, this is using named placeholders.
foreach
(
$args
as
$placeholder
=>
$value
)
{
if
(
is_float
(
$value
)
||
is_int
(
$value
))
{
if
(
is_float
(
$value
))
{
// Force the conversion to float so as not to loose precision
// in the automatic cast.
$value
=
sprintf
(
'%F'
,
$value
);
}
// We will remove this placeholder from the query as PDO throws an
// exception if the number of placeholders in the query and the
// arguments does not match.
unset
(
$args
[
$placeholder
]);
// PDO allows placeholders to not be prefixed by a colon. See
// http://marc.info/?l=php-internals&m=111234321827149&w=2 for
// more.
if
(
$placeholder
[
0
]
!=
':'
)
{
$placeholder
=
":
$placeholder
"
;
}
// When replacing the placeholders, make sure we search for the
// exact placeholder. For example, if searching for
// ':db_placeholder_1', do not replace ':db_placeholder_11'.
$query
=
preg_replace
(
'/'
.
preg_quote
(
$placeholder
)
.
'\b/'
,
$value
,
$query
);
}
}
}
}
return
$this
->
dbh
->
prepare
(
$query
);
}
public
function
execute
(
$args
=
array
(),
$options
=
array
())
{
try
{
$return
=
parent
::
execute
(
$args
,
$options
);
}
catch
(
\PDOException
$e
)
{
if
(
!
empty
(
$e
->
errorInfo
[
1
])
&&
$e
->
errorInfo
[
1
]
===
17
)
{
// The schema has changed. SQLite specifies that we must resend the query.
$return
=
parent
::
execute
(
$args
,
$options
);
}
else
{
// Rethrow the exception.
throw
$e
;
}
}
// In some weird cases, SQLite will prefix some column names by the name
// of the table. We post-process the data, by renaming the column names
// using the same convention as MySQL and PostgreSQL.
$rename_columns
=
array
();
foreach
(
$this
->
columnNames
as
$k
=>
$column
)
{
// In some SQLite versions, SELECT DISTINCT(field) will return "(field)"
// instead of "field".
if
(
preg_match
(
"/^\((.*)\)$/"
,
$column
,
$matches
))
{
$rename_columns
[
$column
]
=
$matches
[
1
];
$this
->
columnNames
[
$k
]
=
$matches
[
1
];
$column
=
$matches
[
1
];
}
// Remove "table." prefixes.
if
(
preg_match
(
"/^.*\.(.*)$/"
,
$column
,
$matches
))
{
$rename_columns
[
$column
]
=
$matches
[
1
];
$this
->
columnNames
[
$k
]
=
$matches
[
1
];
}
}
if
(
$rename_columns
)
{
// DatabaseStatementPrefetch already extracted the first row,
// put it back into the result set.
if
(
isset
(
$this
->
currentRow
))
{
$this
->
data
[
0
]
=
&
$this
->
currentRow
;
}
// Then rename all the columns across the result set.
foreach
(
$this
->
data
as
$k
=>
$row
)
{
foreach
(
$rename_columns
as
$old_column
=>
$new_column
)
{
$this
->
data
[
$k
][
$new_column
]
=
$this
->
data
[
$k
][
$old_column
];
unset
(
$this
->
data
[
$k
][
$old_column
]);
}
}
// Finally, extract the first row again.
$this
->
currentRow
=
$this
->
data
[
0
];
unset
(
$this
->
data
[
0
]);
}
return
$return
;
}
}
core/lib/Drupal/Core/Database/StatementPrefetch.php
View file @
1731cd48
...
...
@@ -187,8 +187,8 @@ public function execute($args = array(), $options = array()) {
// Fetch all the data from the reply, in order to release any lock
// as soon as possible.
$this
->
data
=
$statement
->
fetchAll
(
\PDO
::
FETCH_ASSOC
);
// Destroy the statement as soon as possible. See
//
DatabaseConnection_sqlite::PDOPrepare() for
explanation.
// Destroy the statement as soon as possible. See
the documentation of
//
\Drupal\Core\Database\Driver\sqlite\Statement for an
explanation.
unset
(
$statement
);
$this
->
resultRowCount
=
count
(
$this
->
data
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment