Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
ec3e5107
Commit
ec3e5107
authored
Jun 01, 2017
by
Marco Pivetta
Committed by
GitHub
Jun 01, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2495 from morozov/optimize-oci8-convert
Optimize and improve parameter conversion in OCI8Statement
parents
9d696ac2
8cbf648e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
237 additions
and
16 deletions
+237
-16
OCI8Statement.php
lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php
+120
-15
OCI8StatementTest.php
tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php
+32
-1
StatementTest.php
...trine/Tests/DBAL/Functional/Driver/OCI8/StatementTest.php
+85
-0
No files found.
lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php
View file @
ec3e5107
...
...
@@ -121,30 +121,135 @@ class OCI8Statement implements \IteratorAggregate, Statement
* @param string $statement The SQL statement to convert.
*
* @return string
* @throws \Doctrine\DBAL\Driver\OCI8\OCI8Exception
*/
static
public
function
convertPositionalToNamedPlaceholders
(
$statement
)
{
$count
=
1
;
$inLiteral
=
false
;
// a valid query never starts with quotes
$stmtLen
=
strlen
(
$statement
);
$paramMap
=
array
();
for
(
$i
=
0
;
$i
<
$stmtLen
;
$i
++
)
{
if
(
$statement
[
$i
]
==
'?'
&&
!
$inLiteral
)
{
// real positional parameter detected
$paramMap
[
$count
]
=
":param
$count
"
;
$len
=
strlen
(
$paramMap
[
$count
]);
$statement
=
substr_replace
(
$statement
,
":param
$count
"
,
$i
,
1
);
$i
+=
$len
-
1
;
// jump ahead
$stmtLen
=
strlen
(
$statement
);
// adjust statement length
++
$count
;
}
elseif
(
$statement
[
$i
]
==
"'"
||
$statement
[
$i
]
==
'"'
)
{
$inLiteral
=
!
$inLiteral
;
// switch state!
$fragmentOffset
=
$tokenOffset
=
0
;
$fragments
=
$paramMap
=
array
();
$currentLiteralDelimiter
=
null
;
do
{
if
(
!
$currentLiteralDelimiter
)
{
$result
=
self
::
findPlaceholderOrOpeningQuote
(
$statement
,
$tokenOffset
,
$fragmentOffset
,
$fragments
,
$currentLiteralDelimiter
,
$paramMap
);
}
else
{
$result
=
self
::
findClosingQuote
(
$statement
,
$tokenOffset
,
$currentLiteralDelimiter
);
}
}
while
(
$result
);
if
(
$currentLiteralDelimiter
)
{
throw
new
OCI8Exception
(
sprintf
(
'The statement contains non-terminated string literal starting at offset %d'
,
$tokenOffset
-
1
));
}
$fragments
[]
=
substr
(
$statement
,
$fragmentOffset
);
$statement
=
implode
(
''
,
$fragments
);
return
array
(
$statement
,
$paramMap
);
}
/**
* Finds next placeholder or opening quote.
*
* @param string $statement The SQL statement to parse
* @param string $tokenOffset The offset to start searching from
* @param int $fragmentOffset The offset to build the next fragment from
* @param string[] $fragments Fragments of the original statement not containing placeholders
* @param string|null $currentLiteralDelimiter The delimiter of the current string literal
* or NULL if not currently in a literal
* @param array<int, string> $paramMap Mapping of the original parameter positions to their named replacements
* @return bool Whether the token was found
*/
private
static
function
findPlaceholderOrOpeningQuote
(
$statement
,
&
$tokenOffset
,
&
$fragmentOffset
,
&
$fragments
,
&
$currentLiteralDelimiter
,
&
$paramMap
)
{
$token
=
self
::
findToken
(
$statement
,
$tokenOffset
,
'/[?\'"]/'
);
if
(
!
$token
)
{
return
false
;
}
if
(
$token
===
'?'
)
{
$position
=
count
(
$paramMap
)
+
1
;
$param
=
':param'
.
$position
;
$fragments
[]
=
substr
(
$statement
,
$fragmentOffset
,
$tokenOffset
-
$fragmentOffset
);
$fragments
[]
=
$param
;
$paramMap
[
$position
]
=
$param
;
$tokenOffset
+=
1
;
$fragmentOffset
=
$tokenOffset
;
return
true
;
}
$currentLiteralDelimiter
=
$token
;
++
$tokenOffset
;
return
true
;
}
/**
* Finds closing quote
*
* @param string $statement The SQL statement to parse
* @param string $tokenOffset The offset to start searching from
* @param string|null $currentLiteralDelimiter The delimiter of the current string literal
* or NULL if not currently in a literal
* @return bool Whether the token was found
*/
private
static
function
findClosingQuote
(
$statement
,
&
$tokenOffset
,
&
$currentLiteralDelimiter
)
{
$token
=
self
::
findToken
(
$statement
,
$tokenOffset
,
'/'
.
preg_quote
(
$currentLiteralDelimiter
,
'/'
)
.
'/'
);
if
(
!
$token
)
{
return
false
;
}
$currentLiteralDelimiter
=
false
;
++
$tokenOffset
;
return
true
;
}
/**
* Finds the token described by regex starting from the given offset. Updates the offset with the position
* where the token was found.
*
* @param string $statement The SQL statement to parse
* @param string $offset The offset to start searching from
* @param string $regex The regex containing token pattern
* @return string|null Token or NULL if not found
*/
private
static
function
findToken
(
$statement
,
&
$offset
,
$regex
)
{
if
(
preg_match
(
$regex
,
$statement
,
$matches
,
PREG_OFFSET_CAPTURE
,
$offset
))
{
$offset
=
$matches
[
0
][
1
];
return
$matches
[
0
][
0
];
}
return
null
;
}
/**
* {@inheritdoc}
*/
...
...
tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php
View file @
ec3e5107
...
...
@@ -2,7 +2,11 @@
namespace
Doctrine\Tests\DBAL
;
class
OCI8StatementTest
extends
\Doctrine\Tests\DbalTestCase
use
Doctrine\DBAL\Driver\OCI8\OCI8Exception
;
use
Doctrine\DBAL\Driver\OCI8\OCI8Statement
;
use
Doctrine\Tests\DbalTestCase
;
class
OCI8StatementTest
extends
DbalTestCase
{
protected
function
setUp
()
{
...
...
@@ -81,4 +85,31 @@ class OCI8StatementTest extends \Doctrine\Tests\DbalTestCase
);
}
/**
* @dataProvider nonTerminatedLiteralProvider
*/
public
function
testConvertNonTerminatedLiteral
(
$sql
,
$message
)
{
$this
->
expectException
(
OCI8Exception
::
class
);
$this
->
expectExceptionMessageRegExp
(
$message
);
OCI8Statement
::
convertPositionalToNamedPlaceholders
(
$sql
);
}
public
static
function
nonTerminatedLiteralProvider
()
{
return
array
(
'no-matching-quote'
=>
array
(
"SELECT 'literal FROM DUAL"
,
'/offset 7/'
,
),
'no-matching-double-quote'
=>
array
(
'SELECT 1 "COL1 FROM DUAL'
,
'/offset 9/'
,
),
'incorrect-escaping-syntax'
=>
array
(
"SELECT 'quoted
\\
'string' FROM DUAL"
,
'/offset 23/'
,
),
);
}
}
tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/StatementTest.php
0 → 100644
View file @
ec3e5107
<?php
namespace
Doctrine\Tests\DBAL\Functional\Driver\OCI8
;
use
Doctrine\DBAL\Driver\OCI8\Driver
;
use
Doctrine\Tests\DbalFunctionalTestCase
;
class
StatementTest
extends
DbalFunctionalTestCase
{
protected
function
setUp
()
{
if
(
!
extension_loaded
(
'oci8'
))
{
$this
->
markTestSkipped
(
'oci8 is not installed.'
);
}
parent
::
setUp
();
if
(
!
$this
->
_conn
->
getDriver
()
instanceof
Driver
)
{
$this
->
markTestSkipped
(
'oci8 only test.'
);
}
}
/**
* @dataProvider queryConversionProvider
*/
public
function
testQueryConversion
(
$query
,
array
$params
,
array
$expected
)
{
$this
->
assertEquals
(
$expected
,
$this
->
_conn
->
executeQuery
(
$query
,
$params
)
->
fetch
()
);
}
public
static
function
queryConversionProvider
()
{
return
array
(
'simple'
=>
array
(
'SELECT ? COL1 FROM DUAL'
,
array
(
1
),
array
(
'COL1'
=>
1
,
),
),
'literal-with-placeholder'
=>
array
(
"SELECT '?' COL1, ? COL2 FROM DUAL"
,
array
(
2
),
array
(
'COL1'
=>
'?'
,
'COL2'
=>
2
,
),
),
'literal-with-quotes'
=>
array
(
"SELECT ? COL1, '?
\"
?''?'
\"
COL?
\"
FROM DUAL"
,
array
(
3
),
array
(
'COL1'
=>
3
,
'COL?'
=>
'?"?\'?'
,
),
),
'placeholder-at-the-end'
=>
array
(
'SELECT ? COL1 FROM DUAL WHERE 1 = ?'
,
array
(
4
,
1
),
array
(
'COL1'
=>
4
,
),
),
'multi-line-literal'
=>
array
(
"SELECT 'Hello,
World?!' COL1 FROM DUAL WHERE 1 = ?"
,
array
(
1
),
array
(
'COL1'
=>
'Hello,
World?!'
,
),
),
'empty-literal'
=>
array
(
"SELECT '' COL1 FROM DUAL"
,
array
(),
array
(
'COL1'
=>
''
,
),
),
);
}
}
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