* Replace spaces with tabs in ApiFormatJson_json.php, fix some crazy indentation
-<?php\r
-# Copyright (C) 2004 Brion Vibber <brion@pobox.com>\r
-# http://www.mediawiki.org/\r
-#\r
-# This program is free software; you can redistribute it and/or modify\r
-# it under the terms of the GNU General Public License as published by\r
-# the Free Software Foundation; either version 2 of the License, or\r
-# (at your option) any later version.\r
-#\r
-# This program is distributed in the hope that it will be useful,\r
-# but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-# GNU General Public License for more details.\r
-#\r
-# You should have received a copy of the GNU General Public License along\r
-# with this program; if not, write to the Free Software Foundation, Inc.,\r
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-# http://www.gnu.org/copyleft/gpl.html\r
-\r
-/**\r
- * @file\r
- * @ingroup Search\r
- */\r
-\r
-/**\r
- * Search engine hook base class for IBM DB2\r
- * @ingroup Search\r
- */\r
-class SearchIBM_DB2 extends SearchEngine {\r
- function __construct($db) {\r
- $this->db = $db;\r
- }\r
-\r
- /**\r
- * Perform a full text search query and return a result set.\r
- *\r
- * @param string $term - Raw search term\r
- * @return IBM_DB2SearchResultSet\r
- * @access public\r
- */\r
- function searchText( $term ) {\r
- $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true)));\r
- return new IBM_DB2SearchResultSet($resultSet, $this->searchTerms);\r
- }\r
-\r
- /**\r
- * Perform a title-only search query and return a result set.\r
- *\r
- * @param string $term - Raw search term\r
- * @return IBM_DB2SearchResultSet\r
- * @access public\r
- */\r
- function searchTitle($term) {\r
- $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false)));\r
- return new MySQLSearchResultSet($resultSet, $this->searchTerms);\r
- }\r
-\r
-\r
- /**\r
- * Return a partial WHERE clause to exclude redirects, if so set\r
- * @return string\r
- * @private\r
- */\r
- function queryRedirect() {\r
- if ($this->showRedirects) {\r
- return '';\r
- } else {\r
- return 'AND page_is_redirect=0';\r
- }\r
- }\r
-\r
- /**\r
- * Return a partial WHERE clause to limit the search to the given namespaces\r
- * @return string\r
- * @private\r
- */\r
- function queryNamespaces() {\r
- if( is_null($this->namespaces) )\r
- return '';\r
- $namespaces = implode(',', $this->namespaces);\r
- if ($namespaces == '') {\r
- $namespaces = '0';\r
- }\r
- return 'AND page_namespace IN (' . $namespaces . ')';\r
- }\r
-\r
- /**\r
- * Return a LIMIT clause to limit results on the query.\r
- * @return string\r
- * @private\r
- */\r
- function queryLimit($sql) {\r
- return $this->db->limitResult($sql, $this->limit, $this->offset);\r
- }\r
-\r
- /**\r
- * Does not do anything for generic search engine\r
- * subclasses may define this though\r
- * @return string\r
- * @private\r
- */\r
- function queryRanking($filteredTerm, $fulltext) {\r
- // requires Net Search Extender or equivalent\r
- // return ' ORDER BY score(1)';\r
- return '';\r
- }\r
-\r
- /**\r
- * Construct the full SQL query to do the search.\r
- * The guts shoulds be constructed in queryMain()\r
- * @param string $filteredTerm\r
- * @param bool $fulltext\r
- * @private\r
- */\r
- function getQuery( $filteredTerm, $fulltext ) {\r
- return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' .\r
- $this->queryRedirect() . ' ' .\r
- $this->queryNamespaces() . ' ' .\r
- $this->queryRanking( $filteredTerm, $fulltext ) . ' ');\r
- }\r
-\r
-\r
- /**\r
- * Picks which field to index on, depending on what type of query.\r
- * @param bool $fulltext\r
- * @return string\r
- */\r
- function getIndexField($fulltext) {\r
- return $fulltext ? 'si_text' : 'si_title';\r
- }\r
-\r
- /**\r
- * Get the base part of the search query.\r
- *\r
- * @param string $filteredTerm\r
- * @param bool $fulltext\r
- * @return string\r
- * @private\r
- */\r
- function queryMain( $filteredTerm, $fulltext ) {\r
- $match = $this->parseQuery($filteredTerm, $fulltext);\r
- $page = $this->db->tableName('page');\r
- $searchindex = $this->db->tableName('searchindex');\r
- return 'SELECT page_id, page_namespace, page_title ' .\r
- "FROM $page,$searchindex " .\r
- 'WHERE page_id=si_page AND ' . $match;\r
- }\r
-\r
- /** @todo document */\r
- function parseQuery($filteredText, $fulltext) {\r
- global $wgContLang;\r
- $lc = SearchEngine::legalSearchChars();\r
- $this->searchTerms = array();\r
-\r
- # FIXME: This doesn't handle parenthetical expressions.\r
- $m = array();\r
- $q = array();\r
-\r
- if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/',\r
- $filteredText, $m, PREG_SET_ORDER)) {\r
- foreach($m as $terms) {\r
- $q[] = $terms[1] . $wgContLang->stripForSearch($terms[2]);\r
-\r
- if (!empty($terms[3])) {\r
- $regexp = preg_quote( $terms[3], '/' );\r
- if ($terms[4])\r
- $regexp .= "[0-9A-Za-z_]+";\r
- } else {\r
- $regexp = preg_quote(str_replace('"', '', $terms[2]), '/');\r
- }\r
- $this->searchTerms[] = $regexp;\r
- }\r
- }\r
-\r
- $searchon = $this->db->strencode(join(',', $q));\r
- $field = $this->getIndexField($fulltext);\r
- \r
- // requires Net Search Extender or equivalent\r
- //return " CONTAINS($field, '$searchon') > 0 ";\r
- \r
- return " lcase($field) LIKE lcase('%$searchon%')";\r
- }\r
-\r
- /**\r
- * Create or update the search index record for the given page.\r
- * Title and text should be pre-processed.\r
- *\r
- * @param int $id\r
- * @param string $title\r
- * @param string $text\r
- */\r
- function update($id, $title, $text) {\r
- $dbw = wfGetDB(DB_MASTER);\r
- $dbw->replace('searchindex',\r
- array('si_page'),\r
- array(\r
- 'si_page' => $id,\r
- 'si_title' => $title,\r
- 'si_text' => $text\r
- ), 'SearchIBM_DB2::update' );\r
- // ?\r
- //$dbw->query("CALL ctx_ddl.sync_index('si_text_idx')");\r
- //$dbw->query("CALL ctx_ddl.sync_index('si_title_idx')");\r
- }\r
-\r
- /**\r
- * Update a search index record's title only.\r
- * Title should be pre-processed.\r
- *\r
- * @param int $id\r
- * @param string $title\r
- */\r
- function updateTitle($id, $title) {\r
- $dbw = wfGetDB(DB_MASTER);\r
-\r
- $dbw->update('searchindex',\r
- array('si_title' => $title),\r
- array('si_page' => $id),\r
- 'SearchIBM_DB2::updateTitle',\r
- array());\r
- }\r
-}\r
-\r
-/**\r
- * @ingroup Search\r
- */\r
-class IBM_DB2SearchResultSet extends SearchResultSet {\r
- function __construct($resultSet, $terms) {\r
- $this->mResultSet = $resultSet;\r
- $this->mTerms = $terms;\r
- }\r
-\r
- function termMatches() {\r
- return $this->mTerms;\r
- }\r
-\r
- function numRows() {\r
- return $this->mResultSet->numRows();\r
- }\r
-\r
- function next() {\r
- $row = $this->mResultSet->fetchObject();\r
- if ($row === false)\r
- return false;\r
- return new SearchResult($row);\r
- }\r
-}\r
+<?php
+# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+/**
+ * @file
+ * @ingroup Search
+ */
+
+/**
+ * Search engine hook base class for IBM DB2
+ * @ingroup Search
+ */
+class SearchIBM_DB2 extends SearchEngine {
+ function __construct($db) {
+ $this->db = $db;
+ }
+
+ /**
+ * Perform a full text search query and return a result set.
+ *
+ * @param string $term - Raw search term
+ * @return IBM_DB2SearchResultSet
+ * @access public
+ */
+ function searchText( $term ) {
+ $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true)));
+ return new IBM_DB2SearchResultSet($resultSet, $this->searchTerms);
+ }
+
+ /**
+ * Perform a title-only search query and return a result set.
+ *
+ * @param string $term - Raw search term
+ * @return IBM_DB2SearchResultSet
+ * @access public
+ */
+ function searchTitle($term) {
+ $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false)));
+ return new MySQLSearchResultSet($resultSet, $this->searchTerms);
+ }
+
+
+ /**
+ * Return a partial WHERE clause to exclude redirects, if so set
+ * @return string
+ * @private
+ */
+ function queryRedirect() {
+ if ($this->showRedirects) {
+ return '';
+ } else {
+ return 'AND page_is_redirect=0';
+ }
+ }
+
+ /**
+ * Return a partial WHERE clause to limit the search to the given namespaces
+ * @return string
+ * @private
+ */
+ function queryNamespaces() {
+ if( is_null($this->namespaces) )
+ return '';
+ $namespaces = implode(',', $this->namespaces);
+ if ($namespaces == '') {
+ $namespaces = '0';
+ }
+ return 'AND page_namespace IN (' . $namespaces . ')';
+ }
+
+ /**
+ * Return a LIMIT clause to limit results on the query.
+ * @return string
+ * @private
+ */
+ function queryLimit($sql) {
+ return $this->db->limitResult($sql, $this->limit, $this->offset);
+ }
+
+ /**
+ * Does not do anything for generic search engine
+ * subclasses may define this though
+ * @return string
+ * @private
+ */
+ function queryRanking($filteredTerm, $fulltext) {
+ // requires Net Search Extender or equivalent
+ // return ' ORDER BY score(1)';
+ return '';
+ }
+
+ /**
+ * Construct the full SQL query to do the search.
+ * The guts shoulds be constructed in queryMain()
+ * @param string $filteredTerm
+ * @param bool $fulltext
+ * @private
+ */
+ function getQuery( $filteredTerm, $fulltext ) {
+ return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' .
+ $this->queryRedirect() . ' ' .
+ $this->queryNamespaces() . ' ' .
+ $this->queryRanking( $filteredTerm, $fulltext ) . ' ');
+ }
+
+
+ /**
+ * Picks which field to index on, depending on what type of query.
+ * @param bool $fulltext
+ * @return string
+ */
+ function getIndexField($fulltext) {
+ return $fulltext ? 'si_text' : 'si_title';
+ }
+
+ /**
+ * Get the base part of the search query.
+ *
+ * @param string $filteredTerm
+ * @param bool $fulltext
+ * @return string
+ * @private
+ */
+ function queryMain( $filteredTerm, $fulltext ) {
+ $match = $this->parseQuery($filteredTerm, $fulltext);
+ $page = $this->db->tableName('page');
+ $searchindex = $this->db->tableName('searchindex');
+ return 'SELECT page_id, page_namespace, page_title ' .
+ "FROM $page,$searchindex " .
+ 'WHERE page_id=si_page AND ' . $match;
+ }
+
+ /** @todo document */
+ function parseQuery($filteredText, $fulltext) {
+ global $wgContLang;
+ $lc = SearchEngine::legalSearchChars();
+ $this->searchTerms = array();
+
+ # FIXME: This doesn't handle parenthetical expressions.
+ $m = array();
+ $q = array();
+
+ if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/',
+ $filteredText, $m, PREG_SET_ORDER)) {
+ foreach($m as $terms) {
+ $q[] = $terms[1] . $wgContLang->stripForSearch($terms[2]);
+
+ if (!empty($terms[3])) {
+ $regexp = preg_quote( $terms[3], '/' );
+ if ($terms[4])
+ $regexp .= "[0-9A-Za-z_]+";
+ } else {
+ $regexp = preg_quote(str_replace('"', '', $terms[2]), '/');
+ }
+ $this->searchTerms[] = $regexp;
+ }
+ }
+
+ $searchon = $this->db->strencode(join(',', $q));
+ $field = $this->getIndexField($fulltext);
+
+ // requires Net Search Extender or equivalent
+ //return " CONTAINS($field, '$searchon') > 0 ";
+
+ return " lcase($field) LIKE lcase('%$searchon%')";
+ }
+
+ /**
+ * Create or update the search index record for the given page.
+ * Title and text should be pre-processed.
+ *
+ * @param int $id
+ * @param string $title
+ * @param string $text
+ */
+ function update($id, $title, $text) {
+ $dbw = wfGetDB(DB_MASTER);
+ $dbw->replace('searchindex',
+ array('si_page'),
+ array(
+ 'si_page' => $id,
+ 'si_title' => $title,
+ 'si_text' => $text
+ ), 'SearchIBM_DB2::update' );
+ // ?
+ //$dbw->query("CALL ctx_ddl.sync_index('si_text_idx')");
+ //$dbw->query("CALL ctx_ddl.sync_index('si_title_idx')");
+ }
+
+ /**
+ * Update a search index record's title only.
+ * Title should be pre-processed.
+ *
+ * @param int $id
+ * @param string $title
+ */
+ function updateTitle($id, $title) {
+ $dbw = wfGetDB(DB_MASTER);
+
+ $dbw->update('searchindex',
+ array('si_title' => $title),
+ array('si_page' => $id),
+ 'SearchIBM_DB2::updateTitle',
+ array());
+ }
+}
+
+/**
+ * @ingroup Search
+ */
+class IBM_DB2SearchResultSet extends SearchResultSet {
+ function __construct($resultSet, $terms) {
+ $this->mResultSet = $resultSet;
+ $this->mTerms = $terms;
+ }
+
+ function termMatches() {
+ return $this->mTerms;
+ }
+
+ function numRows() {
+ return $this->mResultSet->numRows();
+ }
+
+ function next() {
+ $row = $this->mResultSet->fetchObject();
+ if ($row === false)
+ return false;
+ return new SearchResult($row);
+ }
+}
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
-* @ingroup API
-* @author Michal Migurski <mike-json@teczno.com>
-* @author Matt Knapp <mdknapp[at]gmail[dot]com>
-* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
-* @copyright 2005 Michal Migurski
-* @version CVS: $Id$
-* @license http://www.opensource.org/licenses/bsd-license.php
-* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+* @ingroup API
+* @author Michal Migurski <mike-json@teczno.com>
+* @author Matt Knapp <mdknapp[at]gmail[dot]com>
+* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
+* @copyright 2005 Michal Migurski
+* @version CVS: $Id$
+* @license http://www.opensource.org/licenses/bsd-license.php
+* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
/**
*/
class Services_JSON
{
- /**
- * constructs a new JSON instance
- *
- * @param int $use object behavior flags; combine with boolean-OR
- *
- * possible values:
- * - SERVICES_JSON_LOOSE_TYPE: loose typing.
- * "{...}" syntax creates associative arrays
- * instead of objects in decode().
- * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
- * Values which can't be encoded (e.g. resources)
- * appear as NULL instead of throwing errors.
- * By default, a deeply-nested resource will
- * bubble up with an error, so all return values
- * from encode() should be checked with isError()
- */
- function Services_JSON($use = 0)
- {
- $this->use = $use;
- }
-
- /**
- * convert a string from one UTF-16 char to one UTF-8 char
- *
- * Normally should be handled by mb_convert_encoding, but
- * provides a slower PHP-only method for installations
- * that lack the multibye string extension.
- *
- * @param string $utf16 UTF-16 character
- * @return string UTF-8 character
- * @access private
- */
- function utf162utf8($utf16)
- {
- // oh please oh please oh please oh please oh please
- if(function_exists('mb_convert_encoding')) {
- return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
- }
-
- $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
-
- switch(true) {
- case ((0x7F & $bytes) == $bytes):
- // this case should never be reached, because we are in ASCII range
- // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- return chr(0x7F & $bytes);
-
- case (0x07FF & $bytes) == $bytes:
- // return a 2-byte UTF-8 character
- // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- return chr(0xC0 | (($bytes >> 6) & 0x1F))
- . chr(0x80 | ($bytes & 0x3F));
-
- case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16{2})) == 0xDC:
- // return a 4-byte UTF-8 character
- $char = ((($bytes & 0x03FF) << 10)
- | ((ord($utf16{2}) & 0x03) << 8)
- | ord($utf16{3}));
- $char += 0x10000;
- return chr(0xF0 | (($char >> 18) & 0x07))
- . chr(0x80 | (($char >> 12) & 0x3F))
- . chr(0x80 | (($char >> 6) & 0x3F))
- . chr(0x80 | ($char & 0x3F));
-
- case (0xFFFF & $bytes) == $bytes:
- // return a 3-byte UTF-8 character
- // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- return chr(0xE0 | (($bytes >> 12) & 0x0F))
- . chr(0x80 | (($bytes >> 6) & 0x3F))
- . chr(0x80 | ($bytes & 0x3F));
- }
-
- // ignoring UTF-32 for now, sorry
- return '';
- }
-
- /**
- * convert a string from one UTF-8 char to one UTF-16 char
- *
- * Normally should be handled by mb_convert_encoding, but
- * provides a slower PHP-only method for installations
- * that lack the multibye string extension.
- *
- * @param string $utf8 UTF-8 character
- * @return string UTF-16 character
- * @access private
- */
- function utf82utf16($utf8)
- {
- // oh please oh please oh please oh please oh please
- if(function_exists('mb_convert_encoding')) {
- return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
- }
-
- switch(strlen($utf8)) {
- case 1:
- // this case should never be reached, because we are in ASCII range
- // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- return $utf8;
-
- case 2:
- // return a UTF-16 character from a 2-byte UTF-8 char
- // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- return chr(0x07 & (ord($utf8{0}) >> 2))
- . chr((0xC0 & (ord($utf8{0}) << 6))
- | (0x3F & ord($utf8{1})));
-
- case 3:
- // return a UTF-16 character from a 3-byte UTF-8 char
- // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- return chr((0xF0 & (ord($utf8{0}) << 4))
- | (0x0F & (ord($utf8{1}) >> 2)))
- . chr((0xC0 & (ord($utf8{1}) << 6))
- | (0x7F & ord($utf8{2})));
-
- case 4:
- // return a UTF-16 surrogate pair from a 4-byte UTF-8 char
- if(ord($utf8{0}) > 0xF4) return ''; # invalid
- $char = ((0x1C0000 & (ord($utf8{0}) << 18))
- | (0x03F000 & (ord($utf8{1}) << 12))
- | (0x000FC0 & (ord($utf8{2}) << 6))
- | (0x00003F & ord($utf8{3})));
- if($char > 0x10FFFF) return ''; # invalid
- $char -= 0x10000;
- return chr(0xD8 | (($char >> 18) & 0x03))
- . chr(($char >> 10) & 0xFF)
- . chr(0xDC | (($char >> 8) & 0x03))
- . chr($char & 0xFF);
- }
-
- // ignoring UTF-32 for now, sorry
- return '';
- }
-
- /**
- * encodes an arbitrary variable into JSON format
- *
- * @param mixed $var any number, boolean, string, array, or object to be encoded.
- * see argument 1 to Services_JSON() above for array-parsing behavior.
- * if var is a strng, note that encode() always expects it
- * to be in ASCII or UTF-8 format!
- * @param bool $pretty pretty-print output with indents and newlines
- *
- * @return mixed JSON string representation of input var or an error if a problem occurs
- * @access public
- */
- function encode($var, $pretty=false)
- {
- $this->indent = 0;
- $this->pretty = $pretty;
- $this->nameValSeparator = $pretty ? ': ' : ':';
- return $this->encode2($var);
- }
-
- /**
- * encodes an arbitrary variable into JSON format
- *
- * @param mixed $var any number, boolean, string, array, or object to be encoded.
- * see argument 1 to Services_JSON() above for array-parsing behavior.
- * if var is a strng, note that encode() always expects it
- * to be in ASCII or UTF-8 format!
- *
- * @return mixed JSON string representation of input var or an error if a problem occurs
- * @access private
- */
- function encode2($var)
- {
- if ($this->pretty) {
- $close = "\n" . str_repeat("\t", $this->indent);
- $open = $close . "\t";
- $mid = ',' . $open;
- }
- else {
- $open = $close = '';
- $mid = ',';
- }
-
- switch (gettype($var)) {
- case 'boolean':
- return $var ? 'true' : 'false';
-
- case 'NULL':
- return 'null';
-
- case 'integer':
- return (int) $var;
-
- case 'double':
- case 'float':
- return (float) $var;
-
- case 'string':
- // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
- $ascii = '';
- $strlen_var = strlen($var);
-
- /*
- * Iterate over every character in the string,
- * escaping with a slash or encoding to UTF-8 where necessary
- */
- for ($c = 0; $c < $strlen_var; ++$c) {
-
- $ord_var_c = ord($var{$c});
-
- switch (true) {
- case $ord_var_c == 0x08:
- $ascii .= '\b';
- break;
- case $ord_var_c == 0x09:
- $ascii .= '\t';
- break;
- case $ord_var_c == 0x0A:
- $ascii .= '\n';
- break;
- case $ord_var_c == 0x0C:
- $ascii .= '\f';
- break;
- case $ord_var_c == 0x0D:
- $ascii .= '\r';
- break;
-
- case $ord_var_c == 0x22:
- case $ord_var_c == 0x2F:
- case $ord_var_c == 0x5C:
- // double quote, slash, slosh
- $ascii .= '\\'.$var{$c};
- break;
-
- case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
- // characters U-00000000 - U-0000007F (same as ASCII)
- $ascii .= $var{$c};
- break;
-
- case (($ord_var_c & 0xE0) == 0xC0):
- // characters U-00000080 - U-000007FF, mask 110XXXXX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
- $c += 1;
- $utf16 = $this->utf82utf16($char);
- $ascii .= sprintf('\u%04s', bin2hex($utf16));
- break;
-
- case (($ord_var_c & 0xF0) == 0xE0):
- // characters U-00000800 - U-0000FFFF, mask 1110XXXX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $char = pack('C*', $ord_var_c,
- ord($var{$c + 1}),
- ord($var{$c + 2}));
- $c += 2;
- $utf16 = $this->utf82utf16($char);
- $ascii .= sprintf('\u%04s', bin2hex($utf16));
- break;
-
- case (($ord_var_c & 0xF8) == 0xF0):
- // characters U-00010000 - U-001FFFFF, mask 11110XXX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- // These will always return a surrogate pair
- $char = pack('C*', $ord_var_c,
- ord($var{$c + 1}),
- ord($var{$c + 2}),
- ord($var{$c + 3}));
- $c += 3;
- $utf16 = $this->utf82utf16($char);
- if($utf16 == '') {
- $ascii .= '\ufffd';
- } else {
- $utf16 = str_split($utf16, 2);
- $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1]));
- }
- break;
- }
- }
-
- return '"'.$ascii.'"';
-
- case 'array':
- /*
- * As per JSON spec if any array key is not an integer
- * we must treat the the whole array as an object. We
- * also try to catch a sparsely populated associative
- * array with numeric keys here because some JS engines
- * will create an array with empty indexes up to
- * max_index which can cause memory issues and because
- * the keys, which may be relevant, will be remapped
- * otherwise.
- *
- * As per the ECMA and JSON specification an object may
- * have any string as a property. Unfortunately due to
- * a hole in the ECMA specification if the key is a
- * ECMA reserved word or starts with a digit the
- * parameter is only accessible using ECMAScript's
- * bracket notation.
- */
-
- // treat as a JSON object
- if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
- $this->indent++;
- $properties = array_map(array($this, 'name_value'),
- array_keys($var),
- array_values($var));
- $this->indent--;
-
- foreach($properties as $property) {
- if(Services_JSON::isError($property)) {
- return $property;
- }
- }
-
- return '{' . $open . join($mid, $properties) . $close . '}';
- }
-
- // treat it like a regular array
- $this->indent++;
- $elements = array_map(array($this, 'encode2'), $var);
- $this->indent--;
-
- foreach($elements as $element) {
- if(Services_JSON::isError($element)) {
- return $element;
- }
- }
-
- return '[' . $open . join($mid, $elements) . $close . ']';
-
- case 'object':
- $vars = get_object_vars($var);
-
- $this->indent++;
- $properties = array_map(array($this, 'name_value'),
- array_keys($vars),
- array_values($vars));
- $this->indent--;
-
- foreach($properties as $property) {
- if(Services_JSON::isError($property)) {
- return $property;
- }
- }
-
- return '{' . $open . join($mid, $properties) . $close . '}';
-
- default:
- return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
- ? 'null'
- : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
- }
- }
-
- /**
- * array-walking function for use in generating JSON-formatted name-value pairs
- *
- * @param string $name name of key to use
- * @param mixed $value reference to an array element to be encoded
- *
- * @return string JSON-formatted name-value pair, like '"name":value'
- * @access private
- */
- function name_value($name, $value)
- {
- $encoded_value = $this->encode2($value);
-
- if(Services_JSON::isError($encoded_value)) {
- return $encoded_value;
- }
-
- return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value;
- }
-
- /**
- * reduce a string by removing leading and trailing comments and whitespace
- *
- * @param $str string string value to strip of comments and whitespace
- *
- * @return string string value stripped of comments and whitespace
- * @access private
- */
- function reduce_string($str)
- {
- $str = preg_replace(array(
-
- // eliminate single line comments in '// ...' form
- '#^\s*//(.+)$#m',
-
- // eliminate multi-line comments in '/* ... */' form, at start of string
- '#^\s*/\*(.+)\*/#Us',
-
- // eliminate multi-line comments in '/* ... */' form, at end of string
- '#/\*(.+)\*/\s*$#Us'
-
- ), '', $str);
-
- // eliminate extraneous space
- return trim($str);
- }
-
- /**
- * decodes a JSON string into appropriate variable
- *
- * @param string $str JSON-formatted string
- *
- * @return mixed number, boolean, string, array, or object
- * corresponding to given JSON input string.
- * See argument 1 to Services_JSON() above for object-output behavior.
- * Note that decode() always returns strings
- * in ASCII or UTF-8 format!
- * @access public
- */
- function decode($str)
- {
- $str = $this->reduce_string($str);
-
- switch (strtolower($str)) {
- case 'true':
- return true;
-
- case 'false':
- return false;
-
- case 'null':
- return null;
-
- default:
- $m = array();
-
- if (is_numeric($str)) {
- // Lookie-loo, it's a number
-
- // This would work on its own, but I'm trying to be
- // good about returning integers where appropriate:
- // return (float)$str;
-
- // Return float or int, as appropriate
- return ((float)$str == (integer)$str)
- ? (integer)$str
- : (float)$str;
-
- } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
- // STRINGS RETURNED IN UTF-8 FORMAT
- $delim = substr($str, 0, 1);
- $chrs = substr($str, 1, -1);
- $utf8 = '';
- $strlen_chrs = strlen($chrs);
-
- for ($c = 0; $c < $strlen_chrs; ++$c) {
-
- $substr_chrs_c_2 = substr($chrs, $c, 2);
- $ord_chrs_c = ord($chrs{$c});
-
- switch (true) {
- case $substr_chrs_c_2 == '\b':
- $utf8 .= chr(0x08);
- ++$c;
- break;
- case $substr_chrs_c_2 == '\t':
- $utf8 .= chr(0x09);
- ++$c;
- break;
- case $substr_chrs_c_2 == '\n':
- $utf8 .= chr(0x0A);
- ++$c;
- break;
- case $substr_chrs_c_2 == '\f':
- $utf8 .= chr(0x0C);
- ++$c;
- break;
- case $substr_chrs_c_2 == '\r':
- $utf8 .= chr(0x0D);
- ++$c;
- break;
-
- case $substr_chrs_c_2 == '\\"':
- case $substr_chrs_c_2 == '\\\'':
- case $substr_chrs_c_2 == '\\\\':
- case $substr_chrs_c_2 == '\\/':
- if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
- ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
- $utf8 .= $chrs{++$c};
- }
- break;
-
- case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)):
- // escaped unicode surrogate pair
- $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
- . chr(hexdec(substr($chrs, ($c + 4), 2)))
- . chr(hexdec(substr($chrs, ($c + 8), 2)))
- . chr(hexdec(substr($chrs, ($c + 10), 2)));
- $utf8 .= $this->utf162utf8($utf16);
- $c += 11;
- break;
-
- case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
- // single, escaped unicode character
- $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
- . chr(hexdec(substr($chrs, ($c + 4), 2)));
- $utf8 .= $this->utf162utf8($utf16);
- $c += 5;
- break;
-
- case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
- $utf8 .= $chrs{$c};
- break;
-
- case ($ord_chrs_c & 0xE0) == 0xC0:
- // characters U-00000080 - U-000007FF, mask 110XXXXX
- //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $utf8 .= substr($chrs, $c, 2);
- ++$c;
- break;
-
- case ($ord_chrs_c & 0xF0) == 0xE0:
- // characters U-00000800 - U-0000FFFF, mask 1110XXXX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $utf8 .= substr($chrs, $c, 3);
- $c += 2;
- break;
-
- case ($ord_chrs_c & 0xF8) == 0xF0:
- // characters U-00010000 - U-001FFFFF, mask 11110XXX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $utf8 .= substr($chrs, $c, 4);
- $c += 3;
- break;
-
- case ($ord_chrs_c & 0xFC) == 0xF8:
- // characters U-00200000 - U-03FFFFFF, mask 111110XX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $utf8 .= substr($chrs, $c, 5);
- $c += 4;
- break;
-
- case ($ord_chrs_c & 0xFE) == 0xFC:
- // characters U-04000000 - U-7FFFFFFF, mask 1111110X
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $utf8 .= substr($chrs, $c, 6);
- $c += 5;
- break;
-
- }
-
- }
-
- return $utf8;
-
- } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
- // array, or object notation
-
- if ($str{0} == '[') {
- $stk = array(SERVICES_JSON_IN_ARR);
- $arr = array();
- } else {
- if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
- $stk = array(SERVICES_JSON_IN_OBJ);
- $obj = array();
- } else {
- $stk = array(SERVICES_JSON_IN_OBJ);
- $obj = new stdClass();
- }
- }
-
- array_push($stk, array('what' => SERVICES_JSON_SLICE,
- 'where' => 0,
- 'delim' => false));
-
- $chrs = substr($str, 1, -1);
- $chrs = $this->reduce_string($chrs);
-
- if ($chrs == '') {
- if (reset($stk) == SERVICES_JSON_IN_ARR) {
- return $arr;
-
- } else {
- return $obj;
-
- }
- }
-
- //print("\nparsing {$chrs}\n");
-
- $strlen_chrs = strlen($chrs);
-
- for ($c = 0; $c <= $strlen_chrs; ++$c) {
-
- $top = end($stk);
- $substr_chrs_c_2 = substr($chrs, $c, 2);
-
- if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
- // found a comma that is not inside a string, array, etc.,
- // OR we've reached the end of the character list
- $slice = substr($chrs, $top['where'], ($c - $top['where']));
- array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
- //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
-
- if (reset($stk) == SERVICES_JSON_IN_ARR) {
- // we are in an array, so just push an element onto the stack
- array_push($arr, $this->decode($slice));
-
- } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
- // we are in an object, so figure
- // out the property name and set an
- // element in an associative array,
- // for now
- $parts = array();
-
- if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
- // "name":value pair
- $key = $this->decode($parts[1]);
- $val = $this->decode($parts[2]);
-
- if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
- $obj[$key] = $val;
- } else {
- $obj->$key = $val;
- }
- } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
- // name:value pair, where name is unquoted
- $key = $parts[1];
- $val = $this->decode($parts[2]);
-
- if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
- $obj[$key] = $val;
- } else {
- $obj->$key = $val;
- }
- }
-
- }
-
- } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
- // found a quote, and we are not inside a string
- array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
- //print("Found start of string at {$c}\n");
-
- } elseif (($chrs{$c} == $top['delim']) &&
- ($top['what'] == SERVICES_JSON_IN_STR) &&
- (($chrs{$c - 1} != '\\') ||
- ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) {
- // found a quote, we're in a string, and it's not escaped
- array_pop($stk);
- //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
-
- } elseif (($chrs{$c} == '[') &&
- in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
- // found a left-bracket, and we are in an array, object, or slice
- array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
- //print("Found start of array at {$c}\n");
-
- } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
- // found a right-bracket, and we're in an array
- array_pop($stk);
- //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
-
- } elseif (($chrs{$c} == '{') &&
- in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
- // found a left-brace, and we are in an array, object, or slice
- array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
- //print("Found start of object at {$c}\n");
-
- } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
- // found a right-brace, and we're in an object
- array_pop($stk);
- //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
-
- } elseif (($substr_chrs_c_2 == '/*') &&
- in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
- // found a comment start, and we are in an array, object, or slice
- array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
- $c++;
- //print("Found start of comment at {$c}\n");
-
- } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
- // found a comment end, and we're in one now
- array_pop($stk);
- $c++;
-
- for ($i = $top['where']; $i <= $c; ++$i)
- $chrs = substr_replace($chrs, ' ', $i, 1);
-
- //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
-
- }
-
- }
-
- if (reset($stk) == SERVICES_JSON_IN_ARR) {
- return $arr;
-
- } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
- return $obj;
-
- }
-
- }
- }
- }
-
- /**
- * @todo Ultimately, this should just call PEAR::isError()
- */
- function isError($data, $code = null)
- {
- if (class_exists('pear')) {
- return PEAR::isError($data, $code);
- } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
- is_subclass_of($data, 'services_json_error'))) {
- return true;
- }
-
- return false;
- }
+ /**
+ * constructs a new JSON instance
+ *
+ * @param int $use object behavior flags; combine with boolean-OR
+ *
+ * possible values:
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
+ * "{...}" syntax creates associative arrays
+ * instead of objects in decode().
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
+ * Values which can't be encoded (e.g. resources)
+ * appear as NULL instead of throwing errors.
+ * By default, a deeply-nested resource will
+ * bubble up with an error, so all return values
+ * from encode() should be checked with isError()
+ */
+ function Services_JSON($use = 0)
+ {
+ $this->use = $use;
+ }
+
+ /**
+ * convert a string from one UTF-16 char to one UTF-8 char
+ *
+ * Normally should be handled by mb_convert_encoding, but
+ * provides a slower PHP-only method for installations
+ * that lack the multibye string extension.
+ *
+ * @param string $utf16 UTF-16 character
+ * @return string UTF-8 character
+ * @access private
+ */
+ function utf162utf8($utf16)
+ {
+ // oh please oh please oh please oh please oh please
+ if(function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
+ }
+
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
+
+ switch(true) {
+ case ((0x7F & $bytes) == $bytes):
+ // this case should never be reached, because we are in ASCII range
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0x7F & $bytes);
+
+ case (0x07FF & $bytes) == $bytes:
+ // return a 2-byte UTF-8 character
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
+ . chr(0x80 | ($bytes & 0x3F));
+
+ case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16{2})) == 0xDC:
+ // return a 4-byte UTF-8 character
+ $char = ((($bytes & 0x03FF) << 10)
+ | ((ord($utf16{2}) & 0x03) << 8)
+ | ord($utf16{3}));
+ $char += 0x10000;
+ return chr(0xF0 | (($char >> 18) & 0x07))
+ . chr(0x80 | (($char >> 12) & 0x3F))
+ . chr(0x80 | (($char >> 6) & 0x3F))
+ . chr(0x80 | ($char & 0x3F));
+
+ case (0xFFFF & $bytes) == $bytes:
+ // return a 3-byte UTF-8 character
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
+ . chr(0x80 | ($bytes & 0x3F));
+ }
+
+ // ignoring UTF-32 for now, sorry
+ return '';
+ }
+
+ /**
+ * convert a string from one UTF-8 char to one UTF-16 char
+ *
+ * Normally should be handled by mb_convert_encoding, but
+ * provides a slower PHP-only method for installations
+ * that lack the multibye string extension.
+ *
+ * @param string $utf8 UTF-8 character
+ * @return string UTF-16 character
+ * @access private
+ */
+ function utf82utf16($utf8)
+ {
+ // oh please oh please oh please oh please oh please
+ if(function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+ }
+
+ switch(strlen($utf8)) {
+ case 1:
+ // this case should never be reached, because we are in ASCII range
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return $utf8;
+
+ case 2:
+ // return a UTF-16 character from a 2-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0x07 & (ord($utf8{0}) >> 2))
+ . chr((0xC0 & (ord($utf8{0}) << 6))
+ | (0x3F & ord($utf8{1})));
+
+ case 3:
+ // return a UTF-16 character from a 3-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr((0xF0 & (ord($utf8{0}) << 4))
+ | (0x0F & (ord($utf8{1}) >> 2)))
+ . chr((0xC0 & (ord($utf8{1}) << 6))
+ | (0x7F & ord($utf8{2})));
+
+ case 4:
+ // return a UTF-16 surrogate pair from a 4-byte UTF-8 char
+ if(ord($utf8{0}) > 0xF4) return ''; # invalid
+ $char = ((0x1C0000 & (ord($utf8{0}) << 18))
+ | (0x03F000 & (ord($utf8{1}) << 12))
+ | (0x000FC0 & (ord($utf8{2}) << 6))
+ | (0x00003F & ord($utf8{3})));
+ if($char > 0x10FFFF) return ''; # invalid
+ $char -= 0x10000;
+ return chr(0xD8 | (($char >> 18) & 0x03))
+ . chr(($char >> 10) & 0xFF)
+ . chr(0xDC | (($char >> 8) & 0x03))
+ . chr($char & 0xFF);
+ }
+
+ // ignoring UTF-32 for now, sorry
+ return '';
+ }
+
+ /**
+ * encodes an arbitrary variable into JSON format
+ *
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
+ * if var is a strng, note that encode() always expects it
+ * to be in ASCII or UTF-8 format!
+ * @param bool $pretty pretty-print output with indents and newlines
+ *
+ * @return mixed JSON string representation of input var or an error if a problem occurs
+ * @access public
+ */
+ function encode($var, $pretty=false)
+ {
+ $this->indent = 0;
+ $this->pretty = $pretty;
+ $this->nameValSeparator = $pretty ? ': ' : ':';
+ return $this->encode2($var);
+ }
+
+ /**
+ * encodes an arbitrary variable into JSON format
+ *
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
+ * if var is a strng, note that encode() always expects it
+ * to be in ASCII or UTF-8 format!
+ *
+ * @return mixed JSON string representation of input var or an error if a problem occurs
+ * @access private
+ */
+ function encode2($var)
+ {
+ if ($this->pretty) {
+ $close = "\n" . str_repeat("\t", $this->indent);
+ $open = $close . "\t";
+ $mid = ',' . $open;
+ }
+ else {
+ $open = $close = '';
+ $mid = ',';
+ }
+
+ switch (gettype($var)) {
+ case 'boolean':
+ return $var ? 'true' : 'false';
+
+ case 'NULL':
+ return 'null';
+
+ case 'integer':
+ return (int) $var;
+
+ case 'double':
+ case 'float':
+ return (float) $var;
+
+ case 'string':
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+ $ascii = '';
+ $strlen_var = strlen($var);
+
+ /*
+ * Iterate over every character in the string,
+ * escaping with a slash or encoding to UTF-8 where necessary
+ */
+ for ($c = 0; $c < $strlen_var; ++$c) {
+
+ $ord_var_c = ord($var{$c});
+
+ switch (true) {
+ case $ord_var_c == 0x08:
+ $ascii .= '\b';
+ break;
+ case $ord_var_c == 0x09:
+ $ascii .= '\t';
+ break;
+ case $ord_var_c == 0x0A:
+ $ascii .= '\n';
+ break;
+ case $ord_var_c == 0x0C:
+ $ascii .= '\f';
+ break;
+ case $ord_var_c == 0x0D:
+ $ascii .= '\r';
+ break;
+
+ case $ord_var_c == 0x22:
+ case $ord_var_c == 0x2F:
+ case $ord_var_c == 0x5C:
+ // double quote, slash, slosh
+ $ascii .= '\\'.$var{$c};
+ break;
+
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+ // characters U-00000000 - U-0000007F (same as ASCII)
+ $ascii .= $var{$c};
+ break;
+
+ case (($ord_var_c & 0xE0) == 0xC0):
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+ $c += 1;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF0) == 0xE0):
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}));
+ $c += 2;
+ $utf16 = $this->utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF8) == 0xF0):
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ // These will always return a surrogate pair
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}));
+ $c += 3;
+ $utf16 = $this->utf82utf16($char);
+ if($utf16 == '') {
+ $ascii .= '\ufffd';
+ } else {
+ $utf16 = str_split($utf16, 2);
+ $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1]));
+ }
+ break;
+ }
+ }
+
+ return '"'.$ascii.'"';
+
+ case 'array':
+ /*
+ * As per JSON spec if any array key is not an integer
+ * we must treat the the whole array as an object. We
+ * also try to catch a sparsely populated associative
+ * array with numeric keys here because some JS engines
+ * will create an array with empty indexes up to
+ * max_index which can cause memory issues and because
+ * the keys, which may be relevant, will be remapped
+ * otherwise.
+ *
+ * As per the ECMA and JSON specification an object may
+ * have any string as a property. Unfortunately due to
+ * a hole in the ECMA specification if the key is a
+ * ECMA reserved word or starts with a digit the
+ * parameter is only accessible using ECMAScript's
+ * bracket notation.
+ */
+
+ // treat as a JSON object
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+ $this->indent++;
+ $properties = array_map(array($this, 'name_value'),
+ array_keys($var),
+ array_values($var));
+ $this->indent--;
+
+ foreach($properties as $property) {
+ if(Services_JSON::isError($property)) {
+ return $property;
+ }
+ }
+
+ return '{' . $open . join($mid, $properties) . $close . '}';
+ }
+
+ // treat it like a regular array
+ $this->indent++;
+ $elements = array_map(array($this, 'encode2'), $var);
+ $this->indent--;
+
+ foreach($elements as $element) {
+ if(Services_JSON::isError($element)) {
+ return $element;
+ }
+ }
+
+ return '[' . $open . join($mid, $elements) . $close . ']';
+
+ case 'object':
+ $vars = get_object_vars($var);
+
+ $this->indent++;
+ $properties = array_map(array($this, 'name_value'),
+ array_keys($vars),
+ array_values($vars));
+ $this->indent--;
+
+ foreach($properties as $property) {
+ if(Services_JSON::isError($property)) {
+ return $property;
+ }
+ }
+
+ return '{' . $open . join($mid, $properties) . $close . '}';
+
+ default:
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
+ ? 'null'
+ : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
+ }
+ }
+
+ /**
+ * array-walking function for use in generating JSON-formatted name-value pairs
+ *
+ * @param string $name name of key to use
+ * @param mixed $value reference to an array element to be encoded
+ *
+ * @return string JSON-formatted name-value pair, like '"name":value'
+ * @access private
+ */
+ function name_value($name, $value)
+ {
+ $encoded_value = $this->encode2($value);
+
+ if(Services_JSON::isError($encoded_value)) {
+ return $encoded_value;
+ }
+
+ return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value;
+ }
+
+ /**
+ * reduce a string by removing leading and trailing comments and whitespace
+ *
+ * @param $str string string value to strip of comments and whitespace
+ *
+ * @return string string value stripped of comments and whitespace
+ * @access private
+ */
+ function reduce_string($str)
+ {
+ $str = preg_replace(array(
+
+ // eliminate single line comments in '// ...' form
+ '#^\s*//(.+)$#m',
+
+ // eliminate multi-line comments in '/* ... */' form, at start of string
+ '#^\s*/\*(.+)\*/#Us',
+
+ // eliminate multi-line comments in '/* ... */' form, at end of string
+ '#/\*(.+)\*/\s*$#Us'
+
+ ), '', $str);
+
+ // eliminate extraneous space
+ return trim($str);
+ }
+
+ /**
+ * decodes a JSON string into appropriate variable
+ *
+ * @param string $str JSON-formatted string
+ *
+ * @return mixed number, boolean, string, array, or object
+ * corresponding to given JSON input string.
+ * See argument 1 to Services_JSON() above for object-output behavior.
+ * Note that decode() always returns strings
+ * in ASCII or UTF-8 format!
+ * @access public
+ */
+ function decode($str)
+ {
+ $str = $this->reduce_string($str);
+
+ switch (strtolower($str)) {
+ case 'true':
+ return true;
+
+ case 'false':
+ return false;
+
+ case 'null':
+ return null;
+
+ default:
+ $m = array();
+
+ if (is_numeric($str)) {
+ // Lookie-loo, it's a number
+
+ // This would work on its own, but I'm trying to be
+ // good about returning integers where appropriate:
+ // return (float)$str;
+
+ // Return float or int, as appropriate
+ return ((float)$str == (integer)$str)
+ ? (integer)$str
+ : (float)$str;
+
+ } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
+ // STRINGS RETURNED IN UTF-8 FORMAT
+ $delim = substr($str, 0, 1);
+ $chrs = substr($str, 1, -1);
+ $utf8 = '';
+ $strlen_chrs = strlen($chrs);
+
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
+
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
+ $ord_chrs_c = ord($chrs{$c});
+
+ switch (true) {
+ case $substr_chrs_c_2 == '\b':
+ $utf8 .= chr(0x08);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\t':
+ $utf8 .= chr(0x09);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\n':
+ $utf8 .= chr(0x0A);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\f':
+ $utf8 .= chr(0x0C);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\r':
+ $utf8 .= chr(0x0D);
+ ++$c;
+ break;
+
+ case $substr_chrs_c_2 == '\\"':
+ case $substr_chrs_c_2 == '\\\'':
+ case $substr_chrs_c_2 == '\\\\':
+ case $substr_chrs_c_2 == '\\/':
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
+ $utf8 .= $chrs{++$c};
+ }
+ break;
+
+ case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)):
+ // escaped unicode surrogate pair
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
+ . chr(hexdec(substr($chrs, ($c + 4), 2)))
+ . chr(hexdec(substr($chrs, ($c + 8), 2)))
+ . chr(hexdec(substr($chrs, ($c + 10), 2)));
+ $utf8 .= $this->utf162utf8($utf16);
+ $c += 11;
+ break;
+
+ case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
+ // single, escaped unicode character
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
+ . chr(hexdec(substr($chrs, ($c + 4), 2)));
+ $utf8 .= $this->utf162utf8($utf16);
+ $c += 5;
+ break;
+
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
+ $utf8 .= $chrs{$c};
+ break;
+
+ case ($ord_chrs_c & 0xE0) == 0xC0:
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 2);
+ ++$c;
+ break;
+
+ case ($ord_chrs_c & 0xF0) == 0xE0:
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 3);
+ $c += 2;
+ break;
+
+ case ($ord_chrs_c & 0xF8) == 0xF0:
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 4);
+ $c += 3;
+ break;
+
+ case ($ord_chrs_c & 0xFC) == 0xF8:
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 5);
+ $c += 4;
+ break;
+
+ case ($ord_chrs_c & 0xFE) == 0xFC:
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 6);
+ $c += 5;
+ break;
+
+ }
+
+ }
+
+ return $utf8;
+
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
+ // array, or object notation
+
+ if ($str{0} == '[') {
+ $stk = array(SERVICES_JSON_IN_ARR);
+ $arr = array();
+ } else {
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+ $stk = array(SERVICES_JSON_IN_OBJ);
+ $obj = array();
+ } else {
+ $stk = array(SERVICES_JSON_IN_OBJ);
+ $obj = new stdClass();
+ }
+ }
+
+ array_push($stk, array( 'what' => SERVICES_JSON_SLICE,
+ 'where' => 0,
+ 'delim' => false));
+
+ $chrs = substr($str, 1, -1);
+ $chrs = $this->reduce_string($chrs);
+
+ if ($chrs == '') {
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
+ return $arr;
+
+ } else {
+ return $obj;
+
+ }
+ }
+
+ //print("\nparsing {$chrs}\n");
+
+ $strlen_chrs = strlen($chrs);
+
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
+
+ $top = end($stk);
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
+
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
+ // found a comma that is not inside a string, array, etc.,
+ // OR we've reached the end of the character list
+ $slice = substr($chrs, $top['where'], ($c - $top['where']));
+ array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
+ //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
+ // we are in an array, so just push an element onto the stack
+ array_push($arr, $this->decode($slice));
+
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+ // we are in an object, so figure
+ // out the property name and set an
+ // element in an associative array,
+ // for now
+ $parts = array();
+
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+ // "name":value pair
+ $key = $this->decode($parts[1]);
+ $val = $this->decode($parts[2]);
+
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+ $obj[$key] = $val;
+ } else {
+ $obj->$key = $val;
+ }
+ } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+ // name:value pair, where name is unquoted
+ $key = $parts[1];
+ $val = $this->decode($parts[2]);
+
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+ $obj[$key] = $val;
+ } else {
+ $obj->$key = $val;
+ }
+ }
+
+ }
+
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
+ // found a quote, and we are not inside a string
+ array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
+ //print("Found start of string at {$c}\n");
+
+ } elseif (($chrs{$c} == $top['delim']) &&
+ ($top['what'] == SERVICES_JSON_IN_STR) &&
+ (($chrs{$c - 1} != '\\') ||
+ ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) {
+ // found a quote, we're in a string, and it's not escaped
+ array_pop($stk);
+ //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
+
+ } elseif (($chrs{$c} == '[') &&
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+ // found a left-bracket, and we are in an array, object, or slice
+ array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
+ //print("Found start of array at {$c}\n");
+
+ } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
+ // found a right-bracket, and we're in an array
+ array_pop($stk);
+ //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ } elseif (($chrs{$c} == '{') &&
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+ // found a left-brace, and we are in an array, object, or slice
+ array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
+ //print("Found start of object at {$c}\n");
+
+ } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
+ // found a right-brace, and we're in an object
+ array_pop($stk);
+ //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ } elseif (($substr_chrs_c_2 == '/*') &&
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+ // found a comment start, and we are in an array, object, or slice
+ array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
+ $c++;
+ //print("Found start of comment at {$c}\n");
+
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
+ // found a comment end, and we're in one now
+ array_pop($stk);
+ $c++;
+
+ for ($i = $top['where']; $i <= $c; ++$i)
+ $chrs = substr_replace($chrs, ' ', $i, 1);
+
+ //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ }
+
+ }
+
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
+ return $arr;
+
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+ return $obj;
+
+ }
+
+ }
+ }
+ }
+
+ /**
+ * @todo Ultimately, this should just call PEAR::isError()
+ */
+ function isError($data, $code = null)
+ {
+ if (class_exists('pear')) {
+ return PEAR::isError($data, $code);
+ } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
+ is_subclass_of($data, 'services_json_error'))) {
+ return true;
+ }
+
+ return false;
+ }
}
/// @cond
if (class_exists('PEAR_Error')) {
- /**
- * @ingroup API
- */
- class Services_JSON_Error extends PEAR_Error
- {
- function Services_JSON_Error($message = 'unknown error', $code = null,
- $mode = null, $options = null, $userinfo = null)
- {
- parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
- }
- }
+ /**
+ * @ingroup API
+ */
+ class Services_JSON_Error extends PEAR_Error
+ {
+ function Services_JSON_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+ parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
+ }
+ }
} else {
/// @endcond
- /**
- * @todo Ultimately, this class shall be descended from PEAR_Error
- * @ingroup API
- */
- class Services_JSON_Error
- {
- function Services_JSON_Error($message = 'unknown error', $code = null,
- $mode = null, $options = null, $userinfo = null)
- {
-
- }
- }
+ /**
+ * @todo Ultimately, this class shall be descended from PEAR_Error
+ * @ingroup API
+ */
+ class Services_JSON_Error
+ {
+ function Services_JSON_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+
+ }
+ }
}
throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
}
return $row;
- }\r
- \r
- /**\r
- * Override if introduced to base Database class\r
- */\r
- public function initial_setup() {\r
- // do nothing\r
+ }
+
+ /**
+ * Override if introduced to base Database class
+ */
+ public function initial_setup() {
+ // do nothing
}
/**
print "<br><pre>$mwe</pre><br>";
}
}
-\r
- /**\r
+
+ /**
* Escapes strings
- * Doesn't escape numbers\r
- * @param string s string to escape\r
- * @return escaped string\r
+ * Doesn't escape numbers
+ * @param string s string to escape
+ * @return escaped string
*/
public function addQuotes( $s ) {
//wfDebug("DB2::addQuotes($s)\n");
$s = str_replace($from, $to, $s); // DB2 expects '', not \' escaping
return $s;
}
- \r
- /**\r
- * Switch into the database schema\r
+
+ /**
+ * Switch into the database schema
*/
protected function applySchema() {
- if ( !($this->mSchemaSet) ) {\r
+ if ( !($this->mSchemaSet) ) {
$this->mSchemaSet = true;
$this->begin();
- $this->doQuery("SET SCHEMA = $this->mSchema");\r
+ $this->doQuery("SET SCHEMA = $this->mSchema");
$this->commit();
}
- }\r
- \r
- /**\r
- * Start a transaction (mandatory)\r
- */\r
- public function begin() {\r
+ }
+
+ /**
+ * Start a transaction (mandatory)
+ */
+ public function begin() {
// turn off auto-commit
db2_autocommit($this->mConn, DB2_AUTOCOMMIT_OFF);
- $this->mTrxLevel = 1;\r
- }\r
- \r
- /**\r
+ $this->mTrxLevel = 1;
+ }
+
+ /**
* End a transaction
- * Must have a preceding begin()\r
- */\r
- public function commit() {\r
+ * Must have a preceding begin()
+ */
+ public function commit() {
db2_commit($this->mConn);
// turn auto-commit back on
db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
- $this->mTrxLevel = 0;\r
- }\r
- \r
- /**\r
- * Cancel a transaction\r
- */\r
- public function rollback() {\r
+ $this->mTrxLevel = 0;
+ }
+
+ /**
+ * Cancel a transaction
+ */
+ public function rollback() {
db2_rollback($this->mConn);
// turn auto-commit back on
// not sure if this is appropriate
db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
- $this->mTrxLevel = 0;\r
+ $this->mTrxLevel = 0;
}
/**
-== Syntax differences between other databases and IBM DB2 ==\r
-{| border cellspacing=0 cellpadding=4\r
-!MySQL!!IBM DB2\r
-|-\r
-\r
-|SELECT 1 FROM $table LIMIT 1\r
-|SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST\r
-WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema'\r
-|-\r
-|MySQL code tries to read one row and interprets lack of error as proof of existence.\r
-|DB2 code counts the number of TABLES of that name in the database. There ought to be 1 for it to exist.\r
-|-\r
-|BEGIN\r
-|(implicit)\r
-|-\r
-|TEXT\r
-|VARCHAR(255) or CLOB\r
-|-\r
-|TIMESTAMPTZ\r
-|TIMESTAMP\r
-|-\r
-|BYTEA\r
-|VARGRAPHIC(255)\r
-|-\r
-|DEFAULT nextval('some_kind_of_sequence'),\r
-|GENERATED ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1),\r
-|-\r
-|CIDR\r
-|VARCHAR(255)\r
-|-\r
-|LIMIT 10\r
-|FETCH FIRST 10 ROWS ONLY\r
-|-\r
-|ROLLBACK TO\r
-|ROLLBACK TO SAVEPOINT\r
-|-\r
-|RELEASE\r
-|RELEASE SAVEPOINT\r
-|}\r
-== See also ==\r
+== Syntax differences between other databases and IBM DB2 ==
+{| border cellspacing=0 cellpadding=4
+!MySQL!!IBM DB2
+|-
+
+|SELECT 1 FROM $table LIMIT 1
+|SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST
+WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema'
+|-
+|MySQL code tries to read one row and interprets lack of error as proof of existence.
+|DB2 code counts the number of TABLES of that name in the database. There ought to be 1 for it to exist.
+|-
+|BEGIN
+|(implicit)
+|-
+|TEXT
+|VARCHAR(255) or CLOB
+|-
+|TIMESTAMPTZ
+|TIMESTAMP
+|-
+|BYTEA
+|VARGRAPHIC(255)
+|-
+|DEFAULT nextval('some_kind_of_sequence'),
+|GENERATED ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1),
+|-
+|CIDR
+|VARCHAR(255)
+|-
+|LIMIT 10
+|FETCH FIRST 10 ROWS ONLY
+|-
+|ROLLBACK TO
+|ROLLBACK TO SAVEPOINT
+|-
+|RELEASE
+|RELEASE SAVEPOINT
+|}
+== See also ==
*[http://ca.php.net/manual/en/function.db2-connect.php PHP Manual for DB2 functions]
\ No newline at end of file
--- DB2\r
-\r
--- SQL to create the initial tables for the MediaWiki database.\r
--- This is read and executed by the install script; you should\r
--- not have to run it by itself unless doing a manual install.\r
--- This is the IBM DB2 version.\r
--- For information about each table, please see the notes in maintenance/tables.sql\r
--- Please make sure all dollar-quoting uses $mw$ at the start of the line\r
--- TODO: Change CHAR/SMALLINT to BOOL (still used in a non-bool fashion in PHP code)\r
-\r
-\r
-\r
-\r
-CREATE SEQUENCE user_user_id_seq AS INTEGER START WITH 0 INCREMENT BY 1;\r
-CREATE TABLE mwuser ( -- replace reserved word 'user'\r
- user_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT nextval('user_user_id_seq'),\r
- user_name VARCHAR(255) NOT NULL UNIQUE,\r
- user_real_name VARCHAR(255),\r
- user_password clob(1K),\r
- user_newpassword clob(1K),\r
- user_newpass_time TIMESTAMP,\r
- user_token VARCHAR(255),\r
- user_email VARCHAR(255),\r
- user_email_token VARCHAR(255),\r
- user_email_token_expires TIMESTAMP,\r
- user_email_authenticated TIMESTAMP,\r
- user_options CLOB(64K),\r
- user_touched TIMESTAMP,\r
- user_registration TIMESTAMP,\r
- user_editcount INTEGER\r
-);\r
-CREATE INDEX user_email_token_idx ON mwuser (user_email_token);\r
-\r
--- Create a dummy user to satisfy fk contraints especially with revisions\r
-INSERT INTO mwuser\r
- VALUES (NEXTVAL FOR user_user_id_seq,'Anonymous','', NULL,NULL,CURRENT_TIMESTAMP,NULL, NULL,NULL,NULL,NULL, NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,0);\r
-\r
-CREATE TABLE user_groups (\r
- ug_user INTEGER REFERENCES mwuser(user_id) ON DELETE CASCADE,\r
- ug_group VARCHAR(255) NOT NULL\r
-);\r
-CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group);\r
-\r
-CREATE TABLE user_newtalk (\r
- user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,\r
- user_ip VARCHAR(255),\r
- user_last_timestamp TIMESTAMP\r
-);\r
-CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id);\r
-CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip);\r
-\r
-\r
-CREATE SEQUENCE page_page_id_seq;\r
-CREATE TABLE page (\r
- page_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT NEXT VALUE FOR user_user_id_seq,\r
- page_namespace SMALLINT NOT NULL,\r
- page_title VARCHAR(255) NOT NULL,\r
- page_restrictions clob(1K),\r
- page_counter BIGINT NOT NULL DEFAULT 0,\r
- page_is_redirect SMALLINT NOT NULL DEFAULT 0,\r
- page_is_new SMALLINT NOT NULL DEFAULT 0,\r
- page_random NUMERIC(15,14) NOT NULL,\r
- page_touched TIMESTAMP,\r
- page_latest INTEGER NOT NULL, -- FK?\r
- page_len INTEGER NOT NULL\r
-);\r
-CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title);\r
---CREATE INDEX page_main_title ON page (page_title) WHERE page_namespace = 0;\r
---CREATE INDEX page_talk_title ON page (page_title) WHERE page_namespace = 1;\r
---CREATE INDEX page_user_title ON page (page_title) WHERE page_namespace = 2;\r
---CREATE INDEX page_utalk_title ON page (page_title) WHERE page_namespace = 3;\r
---CREATE INDEX page_project_title ON page (page_title) WHERE page_namespace = 4;\r
-CREATE INDEX page_random_idx ON page (page_random);\r
-CREATE INDEX page_len_idx ON page (page_len);\r
-\r
---CREATE FUNCTION page_deleted() RETURNS TRIGGER LANGUAGE plpgsql AS\r
---$mw$\r
---BEGIN\r
---DELETE FROM recentchanges WHERE rc_namespace = OLD.page_namespace AND rc_title = OLD.page_title;\r
---RETURN NULL;\r
---END;\r
---$mw$;\r
-\r
---CREATE TRIGGER page_deleted AFTER DELETE ON page\r
--- FOR EACH ROW EXECUTE PROCEDURE page_deleted();\r
-\r
-CREATE SEQUENCE rev_rev_id_val;\r
-CREATE TABLE revision (\r
- rev_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('rev_rev_id_val'),\r
- rev_page INTEGER REFERENCES page (page_id) ON DELETE CASCADE,\r
- rev_text_id INTEGER, -- FK\r
- rev_comment clob(1K), -- changed from VARCHAR(255)\r
- rev_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE RESTRICT,\r
- rev_user_text VARCHAR(255) NOT NULL,\r
- rev_timestamp TIMESTAMP NOT NULL,\r
- rev_minor_edit SMALLINT NOT NULL DEFAULT 0,\r
- rev_deleted SMALLINT NOT NULL DEFAULT 0,\r
- rev_len INTEGER,\r
- rev_parent_id INTEGER\r
-);\r
-CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id);\r
-CREATE INDEX rev_text_id_idx ON revision (rev_text_id);\r
-CREATE INDEX rev_timestamp_idx ON revision (rev_timestamp);\r
-CREATE INDEX rev_user_idx ON revision (rev_user);\r
-CREATE INDEX rev_user_text_idx ON revision (rev_user_text);\r
-\r
-\r
-CREATE SEQUENCE text_old_id_val;\r
-CREATE TABLE pagecontent ( -- replaces reserved word 'text'\r
- old_id INTEGER NOT NULL,\r
- --PRIMARY KEY DEFAULT nextval('text_old_id_val'),\r
- old_text CLOB(16M),\r
- old_flags clob(1K)\r
-);\r
-\r
-CREATE SEQUENCE pr_id_val;\r
-CREATE TABLE page_restrictions (\r
- pr_id INTEGER NOT NULL UNIQUE,\r
- --DEFAULT nextval('pr_id_val'),\r
- pr_page INTEGER NOT NULL\r
- --(used to be nullable)\r
- REFERENCES page (page_id) ON DELETE CASCADE,\r
- pr_type VARCHAR(255) NOT NULL,\r
- pr_level VARCHAR(255) NOT NULL,\r
- pr_cascade SMALLINT NOT NULL,\r
- pr_user INTEGER,\r
- pr_expiry TIMESTAMP,\r
- PRIMARY KEY (pr_page, pr_type)\r
-);\r
---ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type);\r
-\r
-CREATE TABLE page_props (\r
- pp_page INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,\r
- pp_propname VARCHAR(255) NOT NULL,\r
- pp_value CLOB(64K) NOT NULL,\r
- PRIMARY KEY (pp_page,pp_propname) \r
-);\r
---ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname);\r
-CREATE INDEX page_props_propname ON page_props (pp_propname);\r
-\r
-\r
-\r
-CREATE TABLE archive (\r
- ar_namespace SMALLINT NOT NULL,\r
- ar_title VARCHAR(255) NOT NULL,\r
- ar_text CLOB(16M),\r
- ar_page_id INTEGER,\r
- ar_parent_id INTEGER,\r
- ar_comment clob(1K),\r
- ar_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- ar_user_text VARCHAR(255) NOT NULL,\r
- ar_timestamp TIMESTAMP NOT NULL,\r
- ar_minor_edit SMALLINT NOT NULL DEFAULT 0,\r
- ar_flags clob(1K),\r
- ar_rev_id INTEGER,\r
- ar_text_id INTEGER,\r
- ar_deleted SMALLINT NOT NULL DEFAULT 0,\r
- ar_len INTEGER\r
-);\r
-CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);\r
-CREATE INDEX archive_user_text ON archive (ar_user_text);\r
-\r
-\r
-\r
-CREATE TABLE redirect (\r
- rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,\r
- rd_namespace SMALLINT NOT NULL,\r
- rd_title VARCHAR(255) NOT NULL\r
-);\r
-CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from);\r
-\r
-\r
-CREATE TABLE pagelinks (\r
- pl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,\r
- pl_namespace SMALLINT NOT NULL,\r
- pl_title VARCHAR(255) NOT NULL\r
-);\r
-CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title);\r
-\r
-CREATE TABLE templatelinks (\r
- tl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,\r
- tl_namespace SMALLINT NOT NULL,\r
- tl_title VARCHAR(255) NOT NULL\r
-);\r
-CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from);\r
-\r
-CREATE TABLE imagelinks (\r
- il_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,\r
- il_to VARCHAR(255) NOT NULL\r
-);\r
-CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from);\r
-\r
-CREATE TABLE categorylinks (\r
- cl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,\r
- cl_to VARCHAR(255) NOT NULL,\r
- cl_sortkey VARCHAR(255),\r
- cl_timestamp TIMESTAMP NOT NULL\r
-);\r
-CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);\r
-CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from);\r
-\r
-\r
-\r
-CREATE TABLE externallinks (\r
- el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,\r
- el_to VARCHAR(255) NOT NULL,\r
- el_index VARCHAR(255) NOT NULL\r
-);\r
-CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);\r
-CREATE INDEX externallinks_index ON externallinks (el_index);\r
-\r
-CREATE TABLE langlinks (\r
- ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,\r
- ll_lang VARCHAR(255),\r
- ll_title VARCHAR(255)\r
-);\r
-CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang);\r
-CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title);\r
-\r
-\r
-CREATE TABLE site_stats (\r
- ss_row_id INTEGER NOT NULL UNIQUE,\r
- ss_total_views INTEGER DEFAULT 0,\r
- ss_total_edits INTEGER DEFAULT 0,\r
- ss_good_articles INTEGER DEFAULT 0,\r
- ss_total_pages INTEGER DEFAULT -1,\r
- ss_users INTEGER DEFAULT -1,\r
- ss_admins INTEGER DEFAULT -1,\r
- ss_images INTEGER DEFAULT 0\r
-);\r
-\r
-CREATE TABLE hitcounter (\r
- hc_id BIGINT NOT NULL\r
-);\r
-\r
-CREATE SEQUENCE ipblocks_ipb_id_val;\r
-CREATE TABLE ipblocks (\r
- ipb_id INTEGER NOT NULL PRIMARY KEY,\r
- --DEFAULT nextval('ipblocks_ipb_id_val'),\r
- ipb_address VARCHAR(255),\r
- ipb_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- ipb_by INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,\r
- ipb_by_text VARCHAR(255) NOT NULL DEFAULT '',\r
- ipb_reason VARCHAR(255) NOT NULL,\r
- ipb_timestamp TIMESTAMP NOT NULL,\r
- ipb_auto SMALLINT NOT NULL DEFAULT 0,\r
- ipb_anon_only SMALLINT NOT NULL DEFAULT 0,\r
- ipb_create_account SMALLINT NOT NULL DEFAULT 1,\r
- ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1,\r
- ipb_expiry TIMESTAMP NOT NULL,\r
- ipb_range_start VARCHAR(255),\r
- ipb_range_end VARCHAR(255),\r
- ipb_deleted SMALLINT NOT NULL DEFAULT 0,\r
- ipb_block_email SMALLINT NOT NULL DEFAULT 0\r
-\r
-);\r
-CREATE INDEX ipb_address ON ipblocks (ipb_address);\r
-CREATE INDEX ipb_user ON ipblocks (ipb_user);\r
-CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end);\r
-\r
-\r
-\r
-CREATE TABLE image (\r
- img_name VARCHAR(255) NOT NULL PRIMARY KEY,\r
- img_size INTEGER NOT NULL,\r
- img_width INTEGER NOT NULL,\r
- img_height INTEGER NOT NULL,\r
- img_metadata CLOB(16M) NOT NULL DEFAULT '',\r
- img_bits SMALLINT,\r
- img_media_type VARCHAR(255),\r
- img_major_mime VARCHAR(255) DEFAULT 'unknown',\r
- img_minor_mime VARCHAR(255) DEFAULT 'unknown',\r
- img_description clob(1K) NOT NULL DEFAULT '',\r
- img_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- img_user_text VARCHAR(255) NOT NULL DEFAULT '',\r
- img_timestamp TIMESTAMP,\r
- img_sha1 VARCHAR(255) NOT NULL DEFAULT ''\r
-);\r
-CREATE INDEX img_size_idx ON image (img_size);\r
-CREATE INDEX img_timestamp_idx ON image (img_timestamp);\r
-CREATE INDEX img_sha1 ON image (img_sha1);\r
-\r
-CREATE TABLE oldimage (\r
- oi_name VARCHAR(255) NOT NULL,\r
- oi_archive_name VARCHAR(255) NOT NULL,\r
- oi_size INTEGER NOT NULL,\r
- oi_width INTEGER NOT NULL,\r
- oi_height INTEGER NOT NULL,\r
- oi_bits SMALLINT NOT NULL,\r
- oi_description clob(1K),\r
- oi_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- oi_user_text VARCHAR(255) NOT NULL,\r
- oi_timestamp TIMESTAMP NOT NULL,\r
- oi_metadata CLOB(16M) NOT NULL DEFAULT '',\r
- oi_media_type VARCHAR(255) ,\r
- oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown',\r
- oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown',\r
- oi_deleted SMALLINT NOT NULL DEFAULT 0,\r
- oi_sha1 VARCHAR(255) NOT NULL DEFAULT '',\r
- FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE\r
-);\r
---ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascade FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE;\r
-CREATE INDEX oi_name_timestamp ON oldimage (oi_name,oi_timestamp);\r
-CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name);\r
-CREATE INDEX oi_sha1 ON oldimage (oi_sha1);\r
-\r
-\r
-CREATE SEQUENCE filearchive_fa_id_seq;\r
-CREATE TABLE filearchive (\r
- fa_id INTEGER NOT NULL PRIMARY KEY,\r
- --PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'),\r
- fa_name VARCHAR(255) NOT NULL,\r
- fa_archive_name VARCHAR(255),\r
- fa_storage_group VARCHAR(255),\r
- fa_storage_key VARCHAR(255),\r
- fa_deleted_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- fa_deleted_timestamp TIMESTAMP NOT NULL,\r
- fa_deleted_reason VARCHAR(255),\r
- fa_size INTEGER NOT NULL,\r
- fa_width INTEGER NOT NULL,\r
- fa_height INTEGER NOT NULL,\r
- fa_metadata CLOB(16M) NOT NULL DEFAULT '',\r
- fa_bits SMALLINT,\r
- fa_media_type VARCHAR(255),\r
- fa_major_mime VARCHAR(255) DEFAULT 'unknown',\r
- fa_minor_mime VARCHAR(255) DEFAULT 'unknown',\r
- fa_description clob(1K) NOT NULL,\r
- fa_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- fa_user_text VARCHAR(255) NOT NULL,\r
- fa_timestamp TIMESTAMP,\r
- fa_deleted SMALLINT NOT NULL DEFAULT 0\r
-);\r
-CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp);\r
-CREATE INDEX fa_dupe ON filearchive (fa_storage_group, fa_storage_key);\r
-CREATE INDEX fa_notime ON filearchive (fa_deleted_timestamp);\r
-CREATE INDEX fa_nouser ON filearchive (fa_deleted_user);\r
-\r
-CREATE SEQUENCE rc_rc_id_seq;\r
-CREATE TABLE recentchanges (\r
- rc_id INTEGER NOT NULL PRIMARY KEY,\r
- --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'),\r
- rc_timestamp TIMESTAMP NOT NULL,\r
- rc_cur_time TIMESTAMP NOT NULL,\r
- rc_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- rc_user_text VARCHAR(255) NOT NULL,\r
- rc_namespace SMALLINT NOT NULL,\r
- rc_title VARCHAR(255) NOT NULL,\r
- rc_comment VARCHAR(255),\r
- rc_minor SMALLINT NOT NULL DEFAULT 0,\r
- rc_bot SMALLINT NOT NULL DEFAULT 0,\r
- rc_new SMALLINT NOT NULL DEFAULT 0,\r
- rc_cur_id INTEGER REFERENCES page(page_id) ON DELETE SET NULL,\r
- rc_this_oldid INTEGER NOT NULL,\r
- rc_last_oldid INTEGER NOT NULL,\r
- rc_type SMALLINT NOT NULL DEFAULT 0,\r
- rc_moved_to_ns SMALLINT,\r
- rc_moved_to_title VARCHAR(255),\r
- rc_patrolled SMALLINT NOT NULL DEFAULT 0,\r
- rc_ip VARCHAR(255), -- was CIDR type\r
- rc_old_len INTEGER,\r
- rc_new_len INTEGER,\r
- rc_deleted SMALLINT NOT NULL DEFAULT 0,\r
- rc_logid INTEGER NOT NULL DEFAULT 0,\r
- rc_log_type VARCHAR(255),\r
- rc_log_action VARCHAR(255),\r
- rc_params CLOB(64K)\r
-);\r
-CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp);\r
-CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title);\r
-CREATE INDEX rc_cur_id ON recentchanges (rc_cur_id);\r
-CREATE INDEX new_name_timestamp ON recentchanges (rc_new, rc_namespace, rc_timestamp);\r
-CREATE INDEX rc_ip ON recentchanges (rc_ip);\r
-\r
-\r
-\r
-CREATE TABLE watchlist (\r
- wl_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,\r
- wl_namespace SMALLINT NOT NULL DEFAULT 0,\r
- wl_title VARCHAR(255) NOT NULL,\r
- wl_notificationtimestamp TIMESTAMP\r
-);\r
-CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user);\r
-\r
-\r
-CREATE TABLE math (\r
- math_inputhash VARGRAPHIC(255) NOT NULL UNIQUE,\r
- math_outputhash VARGRAPHIC(255) NOT NULL,\r
- math_html_conservativeness SMALLINT NOT NULL,\r
- math_html VARCHAR(255),\r
- math_mathml VARCHAR(255)\r
-);\r
-\r
-\r
-CREATE TABLE interwiki (\r
- iw_prefix VARCHAR(255) NOT NULL UNIQUE,\r
- iw_url CLOB(64K) NOT NULL,\r
- iw_local SMALLINT NOT NULL,\r
- iw_trans SMALLINT NOT NULL DEFAULT 0\r
-);\r
-\r
-\r
-CREATE TABLE querycache (\r
- qc_type VARCHAR(255) NOT NULL,\r
- qc_value INTEGER NOT NULL,\r
- qc_namespace SMALLINT NOT NULL,\r
- qc_title VARCHAR(255) NOT NULL\r
-);\r
-CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value);\r
-\r
-\r
-\r
-CREATE TABLE querycache_info (\r
- qci_type VARCHAR(255) UNIQUE NOT NULL,\r
- qci_timestamp TIMESTAMP\r
-);\r
-\r
-\r
-CREATE TABLE querycachetwo (\r
- qcc_type VARCHAR(255) NOT NULL,\r
- qcc_value INTEGER NOT NULL DEFAULT 0,\r
- qcc_namespace INTEGER NOT NULL DEFAULT 0,\r
- qcc_title VARCHAR(255) NOT NULL DEFAULT '',\r
- qcc_namespacetwo INTEGER NOT NULL DEFAULT 0,\r
- qcc_titletwo VARCHAR(255) NOT NULL DEFAULT ''\r
-);\r
-CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value);\r
-CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title);\r
-CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo);\r
-\r
-CREATE TABLE objectcache (\r
- keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable\r
- value CLOB(16M) NOT NULL DEFAULT '',\r
- exptime TIMESTAMP NOT NULL\r
-);\r
-CREATE INDEX objectcacache_exptime ON objectcache (exptime);\r
-\r
-\r
-\r
-CREATE TABLE transcache (\r
- tc_url VARCHAR(255) NOT NULL UNIQUE,\r
- tc_contents VARCHAR(255) NOT NULL,\r
- tc_time TIMESTAMP NOT NULL\r
-);\r
-\r
-CREATE SEQUENCE log_log_id_seq;\r
-CREATE TABLE logging (\r
- log_id INTEGER NOT NULL PRIMARY KEY,\r
- --PRIMARY KEY DEFAULT nextval('log_log_id_seq'),\r
- log_type VARCHAR(255) NOT NULL,\r
- log_action VARCHAR(255) NOT NULL,\r
- log_timestamp TIMESTAMP NOT NULL,\r
- log_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- log_namespace SMALLINT NOT NULL,\r
- log_title VARCHAR(255) NOT NULL,\r
- log_comment VARCHAR(255),\r
- log_params CLOB(64K),\r
- log_deleted SMALLINT NOT NULL DEFAULT 0\r
-);\r
-CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);\r
-CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);\r
-CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp);\r
-\r
-CREATE SEQUENCE trackbacks_tb_id_seq;\r
-CREATE TABLE trackbacks (\r
- tb_id INTEGER NOT NULL PRIMARY KEY,\r
- --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'),\r
- tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE,\r
- tb_title VARCHAR(255) NOT NULL,\r
- tb_url CLOB(64K) NOT NULL,\r
- tb_ex VARCHAR(255),\r
- tb_name VARCHAR(255)\r
-);\r
-CREATE INDEX trackback_page ON trackbacks (tb_page);\r
-\r
-\r
-CREATE SEQUENCE job_job_id_seq;\r
-CREATE TABLE job (\r
- job_id INTEGER NOT NULL PRIMARY KEY,\r
- --PRIMARY KEY DEFAULT nextval('job_job_id_seq'),\r
- job_cmd VARCHAR(255) NOT NULL,\r
- job_namespace SMALLINT NOT NULL,\r
- job_title VARCHAR(255) NOT NULL,\r
- job_params CLOB(64K) NOT NULL\r
-);\r
-CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);\r
-\r
-\r
-\r
--- Postgres' Tsearch2 dropped\r
---ALTER TABLE page ADD titlevector tsvector;\r
---CREATE FUNCTION ts2_page_title() RETURNS TRIGGER LANGUAGE plpgsql AS\r
---$mw$\r
---BEGIN\r
---IF TG_OP = 'INSERT' THEN\r
--- NEW.titlevector = to_tsvector('default',REPLACE(NEW.page_title,'/',' '));\r
---ELSIF NEW.page_title != OLD.page_title THEN\r
--- NEW.titlevector := to_tsvector('default',REPLACE(NEW.page_title,'/',' '));\r
---END IF;\r
---RETURN NEW;\r
---END;\r
---$mw$;\r
-\r
---CREATE TRIGGER ts2_page_title BEFORE INSERT OR UPDATE ON page\r
--- FOR EACH ROW EXECUTE PROCEDURE ts2_page_title();\r
-\r
-\r
---ALTER TABLE pagecontent ADD textvector tsvector;\r
---CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS\r
---$mw$\r
---BEGIN\r
---IF TG_OP = 'INSERT' THEN\r
--- NEW.textvector = to_tsvector('default',NEW.old_text);\r
---ELSIF NEW.old_text != OLD.old_text THEN\r
--- NEW.textvector := to_tsvector('default',NEW.old_text);\r
---END IF;\r
---RETURN NEW;\r
---END;\r
---$mw$;\r
-\r
---CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent\r
--- FOR EACH ROW EXECUTE PROCEDURE ts2_page_text();\r
-\r
--- These are added by the setup script due to version compatibility issues\r
--- If using 8.1, we switch from "gin" to "gist"\r
-\r
---CREATE INDEX ts2_page_title ON page USING gin(titlevector);\r
---CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector);\r
-\r
---TODO\r
---CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS\r
---$mw$\r
--- INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3);\r
--- SELECT 1;\r
---$mw$;\r
-\r
--- hack implementation\r
--- should be replaced with OmniFind, Contains(), etc\r
-CREATE TABLE searchindex (\r
- si_page int NOT NULL,\r
- si_title varchar(255) NOT NULL default '',\r
- si_text clob NOT NULL\r
-);\r
-\r
--- This table is not used unless profiling is turned on\r
-CREATE TABLE profiling (\r
- pf_count INTEGER NOT NULL DEFAULT 0,\r
- pf_time NUMERIC(18,10) NOT NULL DEFAULT 0,\r
- pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0,\r
- pf_name VARCHAR(255) NOT NULL,\r
- pf_server VARCHAR(255) \r
-);\r
-CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server);\r
-\r
-CREATE TABLE protected_titles (\r
- pt_namespace SMALLINT NOT NULL,\r
- pt_title VARCHAR(255) NOT NULL,\r
- pt_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,\r
- pt_reason clob(1K),\r
- pt_timestamp TIMESTAMP NOT NULL,\r
- pt_expiry TIMESTAMP ,\r
- pt_create_perm VARCHAR(255) NOT NULL DEFAULT ''\r
-);\r
-CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title);\r
-\r
-\r
-\r
-CREATE TABLE updatelog (\r
- ul_key VARCHAR(255) NOT NULL PRIMARY KEY\r
-);\r
-\r
-CREATE SEQUENCE category_id_seq;\r
-CREATE TABLE category (\r
- cat_id INTEGER NOT NULL PRIMARY KEY,\r
- --PRIMARY KEY DEFAULT nextval('category_id_seq'),\r
- cat_title VARCHAR(255) NOT NULL,\r
- cat_pages INTEGER NOT NULL DEFAULT 0,\r
- cat_subcats INTEGER NOT NULL DEFAULT 0,\r
- cat_files INTEGER NOT NULL DEFAULT 0,\r
- cat_hidden SMALLINT NOT NULL DEFAULT 0\r
-);\r
-CREATE UNIQUE INDEX category_title ON category(cat_title);\r
-CREATE INDEX category_pages ON category(cat_pages);\r
-\r
-CREATE TABLE mediawiki_version (\r
- type VARCHAR(255) NOT NULL,\r
- mw_version VARCHAR(255) NOT NULL,\r
- notes VARCHAR(255) ,\r
-\r
- pg_version VARCHAR(255) ,\r
- pg_dbname VARCHAR(255) ,\r
- pg_user VARCHAR(255) ,\r
- pg_port VARCHAR(255) ,\r
- mw_schema VARCHAR(255) ,\r
- ts2_schema VARCHAR(255) ,\r
- ctype VARCHAR(255) ,\r
-\r
- sql_version VARCHAR(255) ,\r
- sql_date VARCHAR(255) ,\r
- cdate TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP\r
-);\r
-\r
-INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date)\r
- VALUES ('Creation','??','$LastChangedRevision: 34049 $','$LastChangedDate: 2008-04-30 10:20:36 -0400 (Wed, 30 Apr 2008) $');\r
-\r
+-- DB2
+
+-- SQL to create the initial tables for the MediaWiki database.
+-- This is read and executed by the install script; you should
+-- not have to run it by itself unless doing a manual install.
+-- This is the IBM DB2 version.
+-- For information about each table, please see the notes in maintenance/tables.sql
+-- Please make sure all dollar-quoting uses $mw$ at the start of the line
+-- TODO: Change CHAR/SMALLINT to BOOL (still used in a non-bool fashion in PHP code)
+
+
+
+
+CREATE SEQUENCE user_user_id_seq AS INTEGER START WITH 0 INCREMENT BY 1;
+CREATE TABLE mwuser ( -- replace reserved word 'user'
+ user_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT nextval('user_user_id_seq'),
+ user_name VARCHAR(255) NOT NULL UNIQUE,
+ user_real_name VARCHAR(255),
+ user_password clob(1K),
+ user_newpassword clob(1K),
+ user_newpass_time TIMESTAMP,
+ user_token VARCHAR(255),
+ user_email VARCHAR(255),
+ user_email_token VARCHAR(255),
+ user_email_token_expires TIMESTAMP,
+ user_email_authenticated TIMESTAMP,
+ user_options CLOB(64K),
+ user_touched TIMESTAMP,
+ user_registration TIMESTAMP,
+ user_editcount INTEGER
+);
+CREATE INDEX user_email_token_idx ON mwuser (user_email_token);
+
+-- Create a dummy user to satisfy fk contraints especially with revisions
+INSERT INTO mwuser
+ VALUES (NEXTVAL FOR user_user_id_seq,'Anonymous','', NULL,NULL,CURRENT_TIMESTAMP,NULL, NULL,NULL,NULL,NULL, NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,0);
+
+CREATE TABLE user_groups (
+ ug_user INTEGER REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ ug_group VARCHAR(255) NOT NULL
+);
+CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group);
+
+CREATE TABLE user_newtalk (
+ user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ user_ip VARCHAR(255),
+ user_last_timestamp TIMESTAMP
+);
+CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id);
+CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip);
+
+
+CREATE SEQUENCE page_page_id_seq;
+CREATE TABLE page (
+ page_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT NEXT VALUE FOR user_user_id_seq,
+ page_namespace SMALLINT NOT NULL,
+ page_title VARCHAR(255) NOT NULL,
+ page_restrictions clob(1K),
+ page_counter BIGINT NOT NULL DEFAULT 0,
+ page_is_redirect SMALLINT NOT NULL DEFAULT 0,
+ page_is_new SMALLINT NOT NULL DEFAULT 0,
+ page_random NUMERIC(15,14) NOT NULL,
+ page_touched TIMESTAMP,
+ page_latest INTEGER NOT NULL, -- FK?
+ page_len INTEGER NOT NULL
+);
+CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title);
+--CREATE INDEX page_main_title ON page (page_title) WHERE page_namespace = 0;
+--CREATE INDEX page_talk_title ON page (page_title) WHERE page_namespace = 1;
+--CREATE INDEX page_user_title ON page (page_title) WHERE page_namespace = 2;
+--CREATE INDEX page_utalk_title ON page (page_title) WHERE page_namespace = 3;
+--CREATE INDEX page_project_title ON page (page_title) WHERE page_namespace = 4;
+CREATE INDEX page_random_idx ON page (page_random);
+CREATE INDEX page_len_idx ON page (page_len);
+
+--CREATE FUNCTION page_deleted() RETURNS TRIGGER LANGUAGE plpgsql AS
+--$mw$
+--BEGIN
+--DELETE FROM recentchanges WHERE rc_namespace = OLD.page_namespace AND rc_title = OLD.page_title;
+--RETURN NULL;
+--END;
+--$mw$;
+
+--CREATE TRIGGER page_deleted AFTER DELETE ON page
+-- FOR EACH ROW EXECUTE PROCEDURE page_deleted();
+
+CREATE SEQUENCE rev_rev_id_val;
+CREATE TABLE revision (
+ rev_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('rev_rev_id_val'),
+ rev_page INTEGER REFERENCES page (page_id) ON DELETE CASCADE,
+ rev_text_id INTEGER, -- FK
+ rev_comment clob(1K), -- changed from VARCHAR(255)
+ rev_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE RESTRICT,
+ rev_user_text VARCHAR(255) NOT NULL,
+ rev_timestamp TIMESTAMP NOT NULL,
+ rev_minor_edit SMALLINT NOT NULL DEFAULT 0,
+ rev_deleted SMALLINT NOT NULL DEFAULT 0,
+ rev_len INTEGER,
+ rev_parent_id INTEGER
+);
+CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id);
+CREATE INDEX rev_text_id_idx ON revision (rev_text_id);
+CREATE INDEX rev_timestamp_idx ON revision (rev_timestamp);
+CREATE INDEX rev_user_idx ON revision (rev_user);
+CREATE INDEX rev_user_text_idx ON revision (rev_user_text);
+
+
+CREATE SEQUENCE text_old_id_val;
+CREATE TABLE pagecontent ( -- replaces reserved word 'text'
+ old_id INTEGER NOT NULL,
+ --PRIMARY KEY DEFAULT nextval('text_old_id_val'),
+ old_text CLOB(16M),
+ old_flags clob(1K)
+);
+
+CREATE SEQUENCE pr_id_val;
+CREATE TABLE page_restrictions (
+ pr_id INTEGER NOT NULL UNIQUE,
+ --DEFAULT nextval('pr_id_val'),
+ pr_page INTEGER NOT NULL
+ --(used to be nullable)
+ REFERENCES page (page_id) ON DELETE CASCADE,
+ pr_type VARCHAR(255) NOT NULL,
+ pr_level VARCHAR(255) NOT NULL,
+ pr_cascade SMALLINT NOT NULL,
+ pr_user INTEGER,
+ pr_expiry TIMESTAMP,
+ PRIMARY KEY (pr_page, pr_type)
+);
+--ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
+
+CREATE TABLE page_props (
+ pp_page INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,
+ pp_propname VARCHAR(255) NOT NULL,
+ pp_value CLOB(64K) NOT NULL,
+ PRIMARY KEY (pp_page,pp_propname)
+);
+--ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname);
+CREATE INDEX page_props_propname ON page_props (pp_propname);
+
+
+
+CREATE TABLE archive (
+ ar_namespace SMALLINT NOT NULL,
+ ar_title VARCHAR(255) NOT NULL,
+ ar_text CLOB(16M),
+ ar_page_id INTEGER,
+ ar_parent_id INTEGER,
+ ar_comment clob(1K),
+ ar_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ ar_user_text VARCHAR(255) NOT NULL,
+ ar_timestamp TIMESTAMP NOT NULL,
+ ar_minor_edit SMALLINT NOT NULL DEFAULT 0,
+ ar_flags clob(1K),
+ ar_rev_id INTEGER,
+ ar_text_id INTEGER,
+ ar_deleted SMALLINT NOT NULL DEFAULT 0,
+ ar_len INTEGER
+);
+CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX archive_user_text ON archive (ar_user_text);
+
+
+
+CREATE TABLE redirect (
+ rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ rd_namespace SMALLINT NOT NULL,
+ rd_title VARCHAR(255) NOT NULL
+);
+CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from);
+
+
+CREATE TABLE pagelinks (
+ pl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ pl_namespace SMALLINT NOT NULL,
+ pl_title VARCHAR(255) NOT NULL
+);
+CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title);
+
+CREATE TABLE templatelinks (
+ tl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ tl_namespace SMALLINT NOT NULL,
+ tl_title VARCHAR(255) NOT NULL
+);
+CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from);
+
+CREATE TABLE imagelinks (
+ il_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ il_to VARCHAR(255) NOT NULL
+);
+CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from);
+
+CREATE TABLE categorylinks (
+ cl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ cl_to VARCHAR(255) NOT NULL,
+ cl_sortkey VARCHAR(255),
+ cl_timestamp TIMESTAMP NOT NULL
+);
+CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);
+CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from);
+
+
+
+CREATE TABLE externallinks (
+ el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ el_to VARCHAR(255) NOT NULL,
+ el_index VARCHAR(255) NOT NULL
+);
+CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);
+CREATE INDEX externallinks_index ON externallinks (el_index);
+
+CREATE TABLE langlinks (
+ ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,
+ ll_lang VARCHAR(255),
+ ll_title VARCHAR(255)
+);
+CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang);
+CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title);
+
+
+CREATE TABLE site_stats (
+ ss_row_id INTEGER NOT NULL UNIQUE,
+ ss_total_views INTEGER DEFAULT 0,
+ ss_total_edits INTEGER DEFAULT 0,
+ ss_good_articles INTEGER DEFAULT 0,
+ ss_total_pages INTEGER DEFAULT -1,
+ ss_users INTEGER DEFAULT -1,
+ ss_admins INTEGER DEFAULT -1,
+ ss_images INTEGER DEFAULT 0
+);
+
+CREATE TABLE hitcounter (
+ hc_id BIGINT NOT NULL
+);
+
+CREATE SEQUENCE ipblocks_ipb_id_val;
+CREATE TABLE ipblocks (
+ ipb_id INTEGER NOT NULL PRIMARY KEY,
+ --DEFAULT nextval('ipblocks_ipb_id_val'),
+ ipb_address VARCHAR(255),
+ ipb_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ ipb_by INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ ipb_by_text VARCHAR(255) NOT NULL DEFAULT '',
+ ipb_reason VARCHAR(255) NOT NULL,
+ ipb_timestamp TIMESTAMP NOT NULL,
+ ipb_auto SMALLINT NOT NULL DEFAULT 0,
+ ipb_anon_only SMALLINT NOT NULL DEFAULT 0,
+ ipb_create_account SMALLINT NOT NULL DEFAULT 1,
+ ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1,
+ ipb_expiry TIMESTAMP NOT NULL,
+ ipb_range_start VARCHAR(255),
+ ipb_range_end VARCHAR(255),
+ ipb_deleted SMALLINT NOT NULL DEFAULT 0,
+ ipb_block_email SMALLINT NOT NULL DEFAULT 0
+
+);
+CREATE INDEX ipb_address ON ipblocks (ipb_address);
+CREATE INDEX ipb_user ON ipblocks (ipb_user);
+CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end);
+
+
+
+CREATE TABLE image (
+ img_name VARCHAR(255) NOT NULL PRIMARY KEY,
+ img_size INTEGER NOT NULL,
+ img_width INTEGER NOT NULL,
+ img_height INTEGER NOT NULL,
+ img_metadata CLOB(16M) NOT NULL DEFAULT '',
+ img_bits SMALLINT,
+ img_media_type VARCHAR(255),
+ img_major_mime VARCHAR(255) DEFAULT 'unknown',
+ img_minor_mime VARCHAR(255) DEFAULT 'unknown',
+ img_description clob(1K) NOT NULL DEFAULT '',
+ img_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ img_user_text VARCHAR(255) NOT NULL DEFAULT '',
+ img_timestamp TIMESTAMP,
+ img_sha1 VARCHAR(255) NOT NULL DEFAULT ''
+);
+CREATE INDEX img_size_idx ON image (img_size);
+CREATE INDEX img_timestamp_idx ON image (img_timestamp);
+CREATE INDEX img_sha1 ON image (img_sha1);
+
+CREATE TABLE oldimage (
+ oi_name VARCHAR(255) NOT NULL,
+ oi_archive_name VARCHAR(255) NOT NULL,
+ oi_size INTEGER NOT NULL,
+ oi_width INTEGER NOT NULL,
+ oi_height INTEGER NOT NULL,
+ oi_bits SMALLINT NOT NULL,
+ oi_description clob(1K),
+ oi_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ oi_user_text VARCHAR(255) NOT NULL,
+ oi_timestamp TIMESTAMP NOT NULL,
+ oi_metadata CLOB(16M) NOT NULL DEFAULT '',
+ oi_media_type VARCHAR(255) ,
+ oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown',
+ oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown',
+ oi_deleted SMALLINT NOT NULL DEFAULT 0,
+ oi_sha1 VARCHAR(255) NOT NULL DEFAULT '',
+ FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE
+);
+--ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascade FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE;
+CREATE INDEX oi_name_timestamp ON oldimage (oi_name,oi_timestamp);
+CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name);
+CREATE INDEX oi_sha1 ON oldimage (oi_sha1);
+
+
+CREATE SEQUENCE filearchive_fa_id_seq;
+CREATE TABLE filearchive (
+ fa_id INTEGER NOT NULL PRIMARY KEY,
+ --PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'),
+ fa_name VARCHAR(255) NOT NULL,
+ fa_archive_name VARCHAR(255),
+ fa_storage_group VARCHAR(255),
+ fa_storage_key VARCHAR(255),
+ fa_deleted_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ fa_deleted_timestamp TIMESTAMP NOT NULL,
+ fa_deleted_reason VARCHAR(255),
+ fa_size INTEGER NOT NULL,
+ fa_width INTEGER NOT NULL,
+ fa_height INTEGER NOT NULL,
+ fa_metadata CLOB(16M) NOT NULL DEFAULT '',
+ fa_bits SMALLINT,
+ fa_media_type VARCHAR(255),
+ fa_major_mime VARCHAR(255) DEFAULT 'unknown',
+ fa_minor_mime VARCHAR(255) DEFAULT 'unknown',
+ fa_description clob(1K) NOT NULL,
+ fa_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ fa_user_text VARCHAR(255) NOT NULL,
+ fa_timestamp TIMESTAMP,
+ fa_deleted SMALLINT NOT NULL DEFAULT 0
+);
+CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp);
+CREATE INDEX fa_dupe ON filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX fa_notime ON filearchive (fa_deleted_timestamp);
+CREATE INDEX fa_nouser ON filearchive (fa_deleted_user);
+
+CREATE SEQUENCE rc_rc_id_seq;
+CREATE TABLE recentchanges (
+ rc_id INTEGER NOT NULL PRIMARY KEY,
+ --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'),
+ rc_timestamp TIMESTAMP NOT NULL,
+ rc_cur_time TIMESTAMP NOT NULL,
+ rc_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ rc_user_text VARCHAR(255) NOT NULL,
+ rc_namespace SMALLINT NOT NULL,
+ rc_title VARCHAR(255) NOT NULL,
+ rc_comment VARCHAR(255),
+ rc_minor SMALLINT NOT NULL DEFAULT 0,
+ rc_bot SMALLINT NOT NULL DEFAULT 0,
+ rc_new SMALLINT NOT NULL DEFAULT 0,
+ rc_cur_id INTEGER REFERENCES page(page_id) ON DELETE SET NULL,
+ rc_this_oldid INTEGER NOT NULL,
+ rc_last_oldid INTEGER NOT NULL,
+ rc_type SMALLINT NOT NULL DEFAULT 0,
+ rc_moved_to_ns SMALLINT,
+ rc_moved_to_title VARCHAR(255),
+ rc_patrolled SMALLINT NOT NULL DEFAULT 0,
+ rc_ip VARCHAR(255), -- was CIDR type
+ rc_old_len INTEGER,
+ rc_new_len INTEGER,
+ rc_deleted SMALLINT NOT NULL DEFAULT 0,
+ rc_logid INTEGER NOT NULL DEFAULT 0,
+ rc_log_type VARCHAR(255),
+ rc_log_action VARCHAR(255),
+ rc_params CLOB(64K)
+);
+CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp);
+CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title);
+CREATE INDEX rc_cur_id ON recentchanges (rc_cur_id);
+CREATE INDEX new_name_timestamp ON recentchanges (rc_new, rc_namespace, rc_timestamp);
+CREATE INDEX rc_ip ON recentchanges (rc_ip);
+
+
+
+CREATE TABLE watchlist (
+ wl_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ wl_namespace SMALLINT NOT NULL DEFAULT 0,
+ wl_title VARCHAR(255) NOT NULL,
+ wl_notificationtimestamp TIMESTAMP
+);
+CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user);
+
+
+CREATE TABLE math (
+ math_inputhash VARGRAPHIC(255) NOT NULL UNIQUE,
+ math_outputhash VARGRAPHIC(255) NOT NULL,
+ math_html_conservativeness SMALLINT NOT NULL,
+ math_html VARCHAR(255),
+ math_mathml VARCHAR(255)
+);
+
+
+CREATE TABLE interwiki (
+ iw_prefix VARCHAR(255) NOT NULL UNIQUE,
+ iw_url CLOB(64K) NOT NULL,
+ iw_local SMALLINT NOT NULL,
+ iw_trans SMALLINT NOT NULL DEFAULT 0
+);
+
+
+CREATE TABLE querycache (
+ qc_type VARCHAR(255) NOT NULL,
+ qc_value INTEGER NOT NULL,
+ qc_namespace SMALLINT NOT NULL,
+ qc_title VARCHAR(255) NOT NULL
+);
+CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value);
+
+
+
+CREATE TABLE querycache_info (
+ qci_type VARCHAR(255) UNIQUE NOT NULL,
+ qci_timestamp TIMESTAMP
+);
+
+
+CREATE TABLE querycachetwo (
+ qcc_type VARCHAR(255) NOT NULL,
+ qcc_value INTEGER NOT NULL DEFAULT 0,
+ qcc_namespace INTEGER NOT NULL DEFAULT 0,
+ qcc_title VARCHAR(255) NOT NULL DEFAULT '',
+ qcc_namespacetwo INTEGER NOT NULL DEFAULT 0,
+ qcc_titletwo VARCHAR(255) NOT NULL DEFAULT ''
+);
+CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value);
+CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title);
+CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo);
+
+CREATE TABLE objectcache (
+ keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable
+ value CLOB(16M) NOT NULL DEFAULT '',
+ exptime TIMESTAMP NOT NULL
+);
+CREATE INDEX objectcacache_exptime ON objectcache (exptime);
+
+
+
+CREATE TABLE transcache (
+ tc_url VARCHAR(255) NOT NULL UNIQUE,
+ tc_contents VARCHAR(255) NOT NULL,
+ tc_time TIMESTAMP NOT NULL
+);
+
+CREATE SEQUENCE log_log_id_seq;
+CREATE TABLE logging (
+ log_id INTEGER NOT NULL PRIMARY KEY,
+ --PRIMARY KEY DEFAULT nextval('log_log_id_seq'),
+ log_type VARCHAR(255) NOT NULL,
+ log_action VARCHAR(255) NOT NULL,
+ log_timestamp TIMESTAMP NOT NULL,
+ log_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ log_namespace SMALLINT NOT NULL,
+ log_title VARCHAR(255) NOT NULL,
+ log_comment VARCHAR(255),
+ log_params CLOB(64K),
+ log_deleted SMALLINT NOT NULL DEFAULT 0
+);
+CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);
+CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);
+CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp);
+
+CREATE SEQUENCE trackbacks_tb_id_seq;
+CREATE TABLE trackbacks (
+ tb_id INTEGER NOT NULL PRIMARY KEY,
+ --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'),
+ tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE,
+ tb_title VARCHAR(255) NOT NULL,
+ tb_url CLOB(64K) NOT NULL,
+ tb_ex VARCHAR(255),
+ tb_name VARCHAR(255)
+);
+CREATE INDEX trackback_page ON trackbacks (tb_page);
+
+
+CREATE SEQUENCE job_job_id_seq;
+CREATE TABLE job (
+ job_id INTEGER NOT NULL PRIMARY KEY,
+ --PRIMARY KEY DEFAULT nextval('job_job_id_seq'),
+ job_cmd VARCHAR(255) NOT NULL,
+ job_namespace SMALLINT NOT NULL,
+ job_title VARCHAR(255) NOT NULL,
+ job_params CLOB(64K) NOT NULL
+);
+CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
+
+
+
+-- Postgres' Tsearch2 dropped
+--ALTER TABLE page ADD titlevector tsvector;
+--CREATE FUNCTION ts2_page_title() RETURNS TRIGGER LANGUAGE plpgsql AS
+--$mw$
+--BEGIN
+--IF TG_OP = 'INSERT' THEN
+-- NEW.titlevector = to_tsvector('default',REPLACE(NEW.page_title,'/',' '));
+--ELSIF NEW.page_title != OLD.page_title THEN
+-- NEW.titlevector := to_tsvector('default',REPLACE(NEW.page_title,'/',' '));
+--END IF;
+--RETURN NEW;
+--END;
+--$mw$;
+
+--CREATE TRIGGER ts2_page_title BEFORE INSERT OR UPDATE ON page
+-- FOR EACH ROW EXECUTE PROCEDURE ts2_page_title();
+
+
+--ALTER TABLE pagecontent ADD textvector tsvector;
+--CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS
+--$mw$
+--BEGIN
+--IF TG_OP = 'INSERT' THEN
+-- NEW.textvector = to_tsvector('default',NEW.old_text);
+--ELSIF NEW.old_text != OLD.old_text THEN
+-- NEW.textvector := to_tsvector('default',NEW.old_text);
+--END IF;
+--RETURN NEW;
+--END;
+--$mw$;
+
+--CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent
+-- FOR EACH ROW EXECUTE PROCEDURE ts2_page_text();
+
+-- These are added by the setup script due to version compatibility issues
+-- If using 8.1, we switch from "gin" to "gist"
+
+--CREATE INDEX ts2_page_title ON page USING gin(titlevector);
+--CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector);
+
+--TODO
+--CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS
+--$mw$
+-- INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3);
+-- SELECT 1;
+--$mw$;
+
+-- hack implementation
+-- should be replaced with OmniFind, Contains(), etc
+CREATE TABLE searchindex (
+ si_page int NOT NULL,
+ si_title varchar(255) NOT NULL default '',
+ si_text clob NOT NULL
+);
+
+-- This table is not used unless profiling is turned on
+CREATE TABLE profiling (
+ pf_count INTEGER NOT NULL DEFAULT 0,
+ pf_time NUMERIC(18,10) NOT NULL DEFAULT 0,
+ pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0,
+ pf_name VARCHAR(255) NOT NULL,
+ pf_server VARCHAR(255)
+);
+CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server);
+
+CREATE TABLE protected_titles (
+ pt_namespace SMALLINT NOT NULL,
+ pt_title VARCHAR(255) NOT NULL,
+ pt_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ pt_reason clob(1K),
+ pt_timestamp TIMESTAMP NOT NULL,
+ pt_expiry TIMESTAMP ,
+ pt_create_perm VARCHAR(255) NOT NULL DEFAULT ''
+);
+CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title);
+
+
+
+CREATE TABLE updatelog (
+ ul_key VARCHAR(255) NOT NULL PRIMARY KEY
+);
+
+CREATE SEQUENCE category_id_seq;
+CREATE TABLE category (
+ cat_id INTEGER NOT NULL PRIMARY KEY,
+ --PRIMARY KEY DEFAULT nextval('category_id_seq'),
+ cat_title VARCHAR(255) NOT NULL,
+ cat_pages INTEGER NOT NULL DEFAULT 0,
+ cat_subcats INTEGER NOT NULL DEFAULT 0,
+ cat_files INTEGER NOT NULL DEFAULT 0,
+ cat_hidden SMALLINT NOT NULL DEFAULT 0
+);
+CREATE UNIQUE INDEX category_title ON category(cat_title);
+CREATE INDEX category_pages ON category(cat_pages);
+
+CREATE TABLE mediawiki_version (
+ type VARCHAR(255) NOT NULL,
+ mw_version VARCHAR(255) NOT NULL,
+ notes VARCHAR(255) ,
+
+ pg_version VARCHAR(255) ,
+ pg_dbname VARCHAR(255) ,
+ pg_user VARCHAR(255) ,
+ pg_port VARCHAR(255) ,
+ mw_schema VARCHAR(255) ,
+ ts2_schema VARCHAR(255) ,
+ ctype VARCHAR(255) ,
+
+ sql_version VARCHAR(255) ,
+ sql_date VARCHAR(255) ,
+ cdate TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP
+);
+
+INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date)
+ VALUES ('Creation','??','$LastChangedRevision: 34049 $','$LastChangedDate: 2008-04-30 10:20:36 -0400 (Wed, 30 Apr 2008) $');
+