Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
D
drupal
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Custom Issue Tracker
Custom Issue Tracker
Labels
Merge Requests
229
Merge Requests
229
Requirements
Requirements
List
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Analytics
Analytics
Code Review
Insights
Issue
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
project
drupal
Commits
9de3b9cb
Commit
9de3b9cb
authored
Nov 23, 2008
by
webchick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
#67349
by chx, Damien Tournoud, and paranojik: SQLite support in core! Yeah! :D
parent
842a0fae
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1483 additions
and
0 deletions
+1483
-0
CHANGELOG.txt
CHANGELOG.txt
+1
-0
includes/database/prefetch.inc
includes/database/prefetch.inc
+503
-0
includes/database/sqlite/database.inc
includes/database/sqlite/database.inc
+263
-0
includes/database/sqlite/install.inc
includes/database/sqlite/install.inc
+15
-0
includes/database/sqlite/query.inc
includes/database/sqlite/query.inc
+131
-0
includes/database/sqlite/schema.inc
includes/database/sqlite/schema.inc
+570
-0
No files found.
CHANGELOG.txt
View file @
9de3b9cb
...
@@ -7,6 +7,7 @@ Drupal 7.0, xxxx-xx-xx (development version)
...
@@ -7,6 +7,7 @@ Drupal 7.0, xxxx-xx-xx (development version)
* Added query builders for INSERT, UPDATE, DELETE, MERGE, and SELECT queries.
* Added query builders for INSERT, UPDATE, DELETE, MERGE, and SELECT queries.
* Support for master/slave replication, transactions, multi-insert queries,
* Support for master/slave replication, transactions, multi-insert queries,
delayed inserts, and other features.
delayed inserts, and other features.
* Added support for the SQLite database engine.
- Security:
- Security:
* Protected cron.php -- cron will only run if the proper key is provided.
* Protected cron.php -- cron will only run if the proper key is provided.
* Implemented much stronger password hashes that are also compatible with the
* Implemented much stronger password hashes that are also compatible with the
...
...
includes/database/prefetch.inc
0 → 100644
View file @
9de3b9cb
<?php
// $Id $
/**
* @file
* Database interface code for engines that need complete control over their
* result sets. For example, 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.
*/
/**
* @ingroup database
* @{
*/
/**
* An implementation of DatabaseStatementInterface that prefetches all data.
*
* This class behaves very similar to a PDOStatement but as it always fetches
* every row it is possible to manipulate those results.
*/
class
DatabaseStatementPrefetch
implements
Iterator
,
DatabaseStatementInterface
{
/**
* The query string.
*
* @var string
*/
protected
$queryString
;
/**
* Driver-specific options. Can be used by child classes.
*
* @var Array
*/
protected
$driverOptions
;
/**
* Reference to the database connection object for this statement.
*
* The name $dbh is inherited from PDOStatement.
*
* @var DatabaseConnection
*/
public
$dbh
;
/**
* Main data store.
*
* @var Array
*/
protected
$data
=
array
();
/**
* Flag indicating whether $data contains valid data.
*
* @var bool
*/
protected
$isValid
=
FALSE
;
/**
* The list of column names in this result set.
*
* @var Array
*/
protected
$columnNames
=
NULL
;
/**
* The number of rows affected by the last query.
*
* @var int
*/
protected
$rowCount
=
NULL
;
/**
* The number of rows in this result set.
*
* @var int
*/
protected
$resultRowCount
=
0
;
/**
* Holds the current fetch style (which will be used by the next fetch).
* @see PDOStatement::fetch.
*
* @var int
*/
protected
$fetchStyle
=
PDO
::
FETCH_OBJ
;
/**
* Holds supplementary current fetch options (which will be used by the next fetch).
*
* @var Array
*/
protected
$fetchOptions
=
array
(
'class'
=>
'stdClass'
,
'constructor_args'
=>
array
(),
'object'
=>
NULL
,
'column'
=>
0
,
);
/**
* Holds the default fetch style.
*
* @var int
*/
protected
$defaultFetchStyle
=
PDO
::
FETCH_OBJ
;
/**
* Holds supplementary default fetch options.
*
* @var Array
*/
protected
$defaultFetchOptions
=
array
(
'class'
=>
'stdClass'
,
'constructor_args'
=>
array
(),
'object'
=>
NULL
,
'column'
=>
0
,
);
public
function
__construct
(
DatabaseConnection
$connection
,
$query
,
Array
$driver_options
=
array
())
{
$this
->
dbh
=
$connection
;
$this
->
queryString
=
$query
;
$this
->
driverOptions
=
$driver_options
;
}
/**
* Executes a prepared statement.
*
* @param $args
* An array of values with as many elements as there are bound parameters in the SQL statement being executed.
* @param $options
* An array of options for this query.
* @return
* TRUE on success, or FALSE on failure.
*/
public
function
execute
(
$args
,
$options
)
{
if
(
isset
(
$options
[
'fetch'
]))
{
if
(
is_string
(
$options
[
'fetch'
]))
{
// Default to an object. Note: db fields will be added to the object
// before the constructor is run. If you need to assign fields after
// the constructor is run, see http://drupal.org/node/315092.
$this
->
setFetchMode
(
PDO
::
FETCH_CLASS
,
$options
[
'fetch'
]);
}
else
{
$this
->
setFetchMode
(
$options
[
'fetch'
]);
}
}
$this
->
dbh
->
lastStatement
=
$this
;
$logger
=
$this
->
dbh
->
getLogger
();
if
(
!
empty
(
$logger
))
{
$query_start
=
microtime
(
TRUE
);
}
// Prepare the query.
$statement
=
$this
->
getStatement
(
$this
->
queryString
,
$args
);
if
(
!
$statement
)
{
$this
->
throwPDOException
();
}
$return
=
$statement
->
execute
(
$args
);
if
(
!
$return
)
{
$this
->
throwPDOException
();
}
// Fetch all the data from the reply, in order to release any lock
// as soon as possible.
$this
->
rowCount
=
$statement
->
rowCount
();
$this
->
data
=
$statement
->
fetchAll
(
PDO
::
FETCH_ASSOC
);
// Destroy the statement as soon as possible.
// @see DatabaseConnection_sqlite::PDOPrepare for explanation.
unset
(
$statement
);
$this
->
resultRowCount
=
count
(
$this
->
data
);
if
(
$this
->
resultRowCount
)
{
$this
->
columnNames
=
array_keys
(
$this
->
data
[
0
]);
$this
->
isValid
=
TRUE
;
}
else
{
$this
->
columnNames
=
array
();
$this
->
isValid
=
FALSE
;
}
if
(
!
empty
(
$logger
))
{
$query_end
=
microtime
(
TRUE
);
$logger
->
log
(
$this
,
$args
,
$query_end
-
$query_start
);
}
// We will iterate this array so we need to make sure the array pointer is
// at the beginning.
reset
(
$this
->
data
);
return
$return
;
}
/**
* Throw a PDO Exception based on the last PDO error.
*/
protected
function
throwPDOException
()
{
$error_info
=
$this
->
dbh
->
errorInfo
();
// We rebuild a message formatted in the same way as PDO.
$exception
=
new
PDOException
(
"SQLSTATE["
.
$error_info
[
0
]
.
"]: General error "
.
$error_info
[
1
]
.
": "
.
$error_info
[
2
]);
$exception
->
errorInfo
=
$error_info
;
throw
$exception
;
}
/**
* Grab a PDOStatement object from a given query and its arguments.
*
* Some drivers (including SQLite) will need to perform some preparation
* themselves to get the statement right.
*
* @param $query
* The query.
* @param Array $args
* An array of arguments.
* @return
* A PDOStatement object.
*/
protected
function
getStatement
(
$query
,
&
$args
=
array
())
{
return
$this
->
dbh
->
prepare
(
$query
);
}
/**
* Return the object's SQL query string.
*/
public
function
getQueryString
()
{
return
$this
->
queryString
;
}
/**
* @see PDOStatement::setFetchMode.
*/
public
function
setFetchMode
(
$fetchStyle
,
$a2
=
NULL
,
$a3
=
NULL
)
{
$this
->
defaultFetchStyle
=
$fetchStyle
;
switch
(
$fetchStyle
)
{
case
PDO
::
FETCH_CLASS
:
$this
->
defaultFetchOptions
[
'class'
]
=
$a2
;
if
(
$a3
)
{
$this
->
defaultFetchOptions
[
'constructor_args'
]
=
$a3
;
}
break
;
case
PDO
::
FETCH_COLUMN
:
$this
->
defaultFetchOptions
[
'column'
]
=
$a2
;
break
;
case
PDO
::
FETCH_INTO
:
$this
->
defaultFetchOptions
[
'object'
]
=
$a2
;
break
;
}
// Set the values for the next fetch.
$this
->
fetchStyle
=
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
}
/**
* Return the current row formatted according to the current fetch style.
*
* This is the core method of this class. It grabs the value at the current
* array position in $this->data and format it according to $this->fetchStyle
* and $this->fetchMode.
*
* @return
* The current row formatted as requested.
*/
public
function
current
()
{
$row
=
current
(
$this
->
data
);
if
(
$row
!==
FALSE
)
{
switch
(
$this
->
fetchStyle
)
{
case
PDO
::
FETCH_ASSOC
:
return
$row
;
case
PDO
::
FETCH_BOTH
:
return
$row
+
array_values
(
$row
);
case
PDO
::
FETCH_NUM
:
return
array_values
(
$row
);
case
PDO
::
FETCH_LAZY
:
// We do not do lazy as everything is fetched already. Fallback to
// PDO::FETCH_OBJ.
case
PDO
::
FETCH_OBJ
:
return
(
object
)
$row
;
case
PDO
::
FETCH_CLASS
|
PDO
::
FETCH_CLASSTYPE
:
$class_name
=
array_unshift
(
$row
);
// Deliberate no break.
case
PDO
::
FETCH_CLASS
:
if
(
!
isset
(
$class_name
))
{
$class_name
=
$this
->
fetchOptions
[
'class'
];
}
if
(
count
(
$this
->
fetchOptions
[
'constructor_args'
]))
{
$reflector
=
new
ReflectionClass
(
$class_name
);
$result
=
$reflector
->
newInstanceArgs
(
$this
->
fetchOptions
[
'constructor_args'
]);
}
else
{
$result
=
new
$class_name
();
}
foreach
(
$row
as
$k
=>
$v
)
{
$result
->
$k
=
$v
;
}
return
$result
;
case
PDO
::
FETCH_INTO
:
foreach
(
$row
as
$k
=>
$v
)
{
$this
->
fetchOptions
[
'object'
]
->
$k
=
$v
;
}
return
$this
->
fetchOptions
[
'object'
];
case
PDO
::
FETCH_COLUMN
:
if
(
isset
(
$this
->
columnNames
[
$this
->
fetchOptions
[
'column'
]]))
{
return
$row
[
$k
][
$this
->
columnNames
[
$this
->
fetchOptions
[
'column'
]]];
}
else
{
return
;
}
}
}
}
/* Implementations of Iterator. */
public
function
key
()
{
return
key
(
$this
->
data
);
}
public
function
rewind
()
{
reset
(
$this
->
data
);
if
(
count
(
$this
->
data
))
{
$this
->
isValid
=
TRUE
;
}
}
public
function
next
()
{
// We fetch rows as PDO::FETCH_ASSOC in execute(),
// so no element of the array can ever be FALSE.
if
(
next
(
$this
->
data
)
===
FALSE
)
{
$this
->
isValid
=
FALSE
;
}
}
public
function
valid
()
{
return
$this
->
isValid
;
}
/* Implementations of DatabaseStatementInterface. */
public
function
rowCount
()
{
return
$this
->
rowCount
;
}
public
function
fetch
(
$fetch_style
=
NULL
,
$cursor_orientation
=
PDO
::
FETCH_ORI_NEXT
,
$cursor_offset
=
NULL
)
{
if
(
$this
->
isValid
)
{
// Set the fetch parameter.
$this
->
fetchStyle
=
isset
(
$fetch_style
)
?
$fetch_style
:
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
// Grab the row in the format specified above.
$return
=
$this
->
current
();
// Advance the cursor.
$this
->
next
();
// Reset the fetch parameters to the value stored using setFetchMode().
$this
->
fetchStyle
=
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
return
$return
;
}
else
{
return
FALSE
;
}
}
public
function
fetchField
(
$index
=
0
)
{
if
(
$this
->
isValid
&&
isset
(
$this
->
columnNames
[
$index
]))
{
// We grab the value directly from $this->data, and format it.
$current
=
current
(
$this
->
data
);
$return
=
$current
[
$this
->
columnNames
[
$index
]];
$this
->
next
();
return
$return
;
}
else
{
return
FALSE
;
}
}
public
function
fetchObject
(
$class_name
=
NULL
,
$constructor_args
=
array
())
{
if
(
$this
->
isValid
)
{
if
(
!
isset
(
$class_name
))
{
// Directly cast to an object to avoid a function call.
$result
=
(
object
)
current
(
$this
->
data
);
}
else
{
$this
->
fetchStyle
=
PDO
::
FETCH_CLASS
;
$this
->
fetchOptions
=
array
(
'constructor_args'
=>
$constructor_args
);
// Grab the row in the format specified above.
$result
=
$this
->
current
();
// Reset the fetch parameters to the value stored using setFetchMode().
$this
->
fetchStyle
=
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
}
$this
->
next
();
return
$result
;
}
else
{
return
FALSE
;
}
}
public
function
fetchAssoc
()
{
if
(
$this
->
isValid
)
{
$result
=
current
(
$this
->
data
);
$this
->
next
();
return
$result
;
}
else
{
return
FALSE
;
}
}
public
function
fetchAll
(
$fetch_style
=
NULL
,
$fetch_column
=
NULL
,
$constructor_args
=
NULL
)
{
$this
->
fetchStyle
=
isset
(
$fetch_style
)
?
$fetch_style
:
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
if
(
isset
(
$fetch_column
))
{
$this
->
fetchOptions
[
'column'
]
=
$fetch_column
;
}
if
(
isset
(
$constructor_args
))
{
$this
->
fetchOptions
[
'constructor_args'
]
=
$constructor_args
;
}
$result
=
array
();
// Traverse the array as PHP would have done.
while
(
$this
->
isValid
)
{
// Grab the row in the format specified above.
$result
[]
=
$this
->
current
();
$this
->
next
();
}
// Reset the fetch parameters to the value stored using setFetchMode().
$this
->
fetchStyle
=
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
return
$result
;
}
public
function
fetchCol
(
$index
=
0
)
{
if
(
isset
(
$this
->
columnNames
[
$index
]))
{
$column
=
$this
->
columnNames
[
$index
];
$result
=
array
();
// Traverse the array as PHP would have done.
while
(
$this
->
isValid
)
{
$current
=
current
(
$this
->
data
);
$result
[]
=
$current
[
$this
->
columnNames
[
$index
]];
$this
->
next
();
}
return
$result
;
}
else
{
return
array
();
}
}
public
function
fetchAllKeyed
(
$key_index
=
0
,
$value_index
=
1
)
{
if
(
!
isset
(
$this
->
columnNames
[
$key_index
])
||
!
isset
(
$this
->
columnNames
[
$value_index
]))
return
array
();
$key
=
$this
->
columnNames
[
$key_index
];
$value
=
$this
->
columnNames
[
$value_index
];
$result
=
array
();
// Traverse the array as PHP would have done.
while
(
$this
->
isValid
)
{
$row
=
current
(
$this
->
data
);
$result
[
$row
[
$key
]]
=
$row
[
$value
];
$this
->
next
();
}
return
$result
;
}
public
function
fetchAllAssoc
(
$key
,
$fetch_style
=
PDO
::
FETCH_OBJ
)
{
$this
->
fetchStyle
=
$fetch_style
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
$result
=
array
();
// Traverse the array as PHP would have done.
while
(
$this
->
isValid
)
{
// Grab the row in its raw PDO::FETCH_ASSOC format.
$row
=
current
(
$this
->
data
);
// Grab the row in the format specified above.
$result_row
=
$this
->
current
();
$result
[
$row
[
$key
]]
=
$result_row
;
$this
->
next
();
}
// Reset the fetch parameters to the value stored using setFetchMode().
$this
->
fetchStyle
=
$this
->
defaultFetchStyle
;
$this
->
fetchOptions
=
$this
->
defaultFetchOptions
;
return
$result
;
}
}
/**
* @} End of "ingroup database".
*/
includes/database/sqlite/database.inc
0 → 100644
View file @
9de3b9cb
<?php
// $Id$
/**
* @file
* Database interface code for SQLite embedded database engine.
*/
/**
* @ingroup database
* @{
*/
include_once
DRUPAL_ROOT
.
'/includes/database/prefetch.inc'
;
/**
* Specific SQLite implementation of DatabaseConnection.
*/
class
DatabaseConnection_sqlite
extends
DatabaseConnection
{
/**
* Indicates that this connection supports transactions.
*
* @var bool
*/
protected
$transactionSupport
=
TRUE
;
public
function
__construct
(
Array
$connection_options
=
array
())
{
// We don't need a specific PDOStatement class here, we simulate it below.
$connection_options
[
'statement_class'
]
=
FALSE
;
$this
->
transactionSupport
=
isset
(
$connection_options
[
'transactions'
])
?
$connection_options
[
'transactions'
]
:
TRUE
;
parent
::
__construct
(
'sqlite:'
.
$connection_options
[
'database'
],
''
,
''
,
$connection_options
);
$this
->
exec
(
'PRAGMA encoding="UTF-8"'
);
// Create functions needed by SQLite.
$this
->
sqliteCreateFunction
(
'if'
,
array
(
$this
,
'sqlFunctionIf'
));
$this
->
sqliteCreateFunction
(
'greatest'
,
array
(
$this
,
'sqlFunctionGreatest'
));
$this
->
sqliteCreateFunction
(
'pow'
,
'pow'
,
2
);
$this
->
sqliteCreateFunction
(
'length'
,
'strlen'
,
1
);
$this
->
sqliteCreateFunction
(
'concat'
,
array
(
$this
,
'sqlFunctionConcat'
));
$this
->
sqliteCreateFunction
(
'substring'
,
array
(
$this
,
'sqlFunctionSubstring'
),
3
);
$this
->
sqliteCreateFunction
(
'rand'
,
array
(
$this
,
'sqlFunctionRand'
));
}
/**
* SQLite compatibility implementation for the IF() SQL function.
*/
public
function
sqlFunctionIf
(
$condition
,
$expr1
,
$expr2
=
NULL
)
{
return
$condition
?
$expr1
:
$expr2
;
}
/**
* SQLite compatibility implementation for the GREATEST() SQL function.
*/
public
function
sqlFunctionGreatest
()
{
$args
=
func_get_args
();
foreach
(
$args
as
$k
=>
$v
)
{
if
(
is_null
(
$v
))
{
unset
(
$args
);
}
}
if
(
count
(
$args
))
{
return
max
(
$args
);
}
else
{
return
NULL
;
}
}
/**
* SQLite compatibility implementation for the CONCAT() SQL function.
*/
public
function
sqlFunctionConcat
()
{
$args
=
func_get_args
();
return
implode
(
''
,
$args
);
}
/**
* SQLite compatibility implementation for the SUBSTRING() SQL function.
*/
public
function
sqlFunctionSubstring
(
$string
,
$from
,
$length
)
{
return
substr
(
$string
,
$from
-
1
,
$length
);
}
/**
* SQLite compatibility implementation for the RAND() SQL function.
*/
public
function
sqlFunctionRand
(
$seed
=
NULL
)
{
if
(
isset
(
$seed
))
{
mt_srand
(
$seed
);
}
return
mt_rand
()
/
mt_getrandmax
();
}
/**
* SQLite-specific implementation of DatabaseConnection::prepare().
*
* We don't use prepared statements at all at this stage. We just create
* a DatabaseStatement_sqlite object, that will create a PDOStatement
* using the semi-private PDOPrepare() method below.
*/
public
function
prepare
(
$query
,
Array
$options
=
array
())
{
return
new
DatabaseStatement_sqlite
(
$this
,
$query
,
$options
);
}
/**
* NEVER CALL THIS FUNCTION: YOU MIGHT DEADLOCK YOUR PHP PROCESS.
*
* This is a wrapper around the parent PDO::prepare method. However, as
* 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, you should never
* call this function and keep a PDOStatement object alive as that can lead
* to a deadlock. This really, really should be private, but as
* DatabaseStatement_sqlite needs to call it, we have no other choice but to
* expose this function to the world.
*/
public
function
PDOPrepare
(
$query
,
Array
$options
=
array
())
{
return
parent
::
prepare
(
$query
,
$options
);
}
public
function
queryRange
(
$query
,
Array
$args
,
$from
,
$count
,
Array
$options
=
array
())
{
return
$this
->
query
(
$query
.
' LIMIT '
.
$from
.
', '
.
$count
,
$args
,
$options
);
}
public
function
queryTemporary
(
$query
,
Array
$args
,
$tablename
,
Array
$options
=
array
())
{
return
$this
->
query
(
preg_replace
(
'/^SELECT/i'
,
'CREATE TEMPORARY TABLE '
.
$tablename
.
' AS SELECT'
,
$query
),
$args
,
$options
);
}
public
function
driver
()
{
return
'sqlite'
;
}
public
function
databaseType
()
{