Commit a61584ad authored by Paul McCullagh's avatar Paul McCullagh

Added PBXT storage engine

parent 032ef1fa
Paul McCullagh
paul.mccullagh@primebase.org
http://www.primebase.org
http://pbxt.blogspot.com
This diff is collapsed.
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory. After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script). Here is a another example:
/bin/bash ./configure CONFIG_SHELL=/bin/bash
Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
configuration-related scripts to be executed by `/bin/bash'.
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
SUBDIRS = src
EXTRA_DIST = plug.in
PrimeBase XT for MySQL 5.1
==========================
This is the PrimeBase XT (PBXT) transactional storage engine for MySQL. PBXT is "pluggable", which means that it can be loaded dynamically by MySQL at runtime. It uses a unique "write-once" update strategy and MVCC (multi-version concurrency control) to provide optimal performance over a wide range of tasks.
This package includes the complete source code for the engine. Although this is a standalone project it must be built against a compiled version of the MySQL 5.1 source tree, because it references headers files used internally by the server.
Details about how to build PBXT both under UNIX or Windows, as a standalone plug-in, or as part of the MySQL source code, is distribed in the documentation which is avaliable online at:
http://www.primebase.org/documentation
Bug reports, questions and comments can be sent directly to me.
Thanks for your support!
Paul McCullagh
SNAP Innovation GmbH
paul.mccullagh@primebase.org
PBXT To-Do List
===============
My thanks to all who have downloaded and tested PBXT. If an issue you reported before the date below is not on this list, please e-mail me again.
------- 2008-12-09
0063: The option for not using memory mapped files must be fixed.
0062: Dynamic option for using memory mapping on a table (Dimitri).
------- 2008-09-12
0061: Add records per key result to ha_pbxt:info() call (Mark).
------- 2008-08-31
0060: Add table option to determine if a table should be memory mapped or not (also requested by Dimitri).
0059: Add table options:
AVG_ROW_LENGTH [=] value
DATA DIRECTORY [=] 'absolute path to directory'
INDEX DIRECTORY [=] 'absolute path to directory'
MAX_ROWS [=] value
------- 2008-03-28
0058: Consolidate writes when changes in the log are applied to the database.
------- 2008-03-07
0057: Cluster updates onto a single page.
0056: Add checksum to index and data pages.
0055: When no index cache is available, the complete index must be flushed (not just single pages).
0054: Optimize indexes by not creating indexes that are a complete sub-set of some other index. In this case we must be able to identify part of an index as unique. For example: primary key (a, b), index (a, b, c). Here we would just create index (a, b, c), and specify that the part (a, b) must be unique. Operations on (a, b) will be directed to index (a, b, c).
0053: Check and test lock tables.
0052: Cache data log data in the handle data cache. Must be purged when a handle data record is written.
0051: Write data log data alternatively to the transaction log. The compactor must then compact transaction logs.
0050: [RESOLVED: RN126] Implement consistent write for indexes.
0049: [RESOLVED: RN114] Set the index block size to 4K, or 16K as used by InnoDB.
0048: [RESOLVED: RN110] Add row ID to indexes. This should only be set once the row is cleaned by the sweeper. Then the row ID can be used to make a quite check if the row is the most recent version.
------- 2007-06-19
0047: Test build with ./configure --with-innodb under Linux (Vadim).
0046: [RESOLVED: RN85] Add plug.in file to enable drop in compile under Linux.
0045: Provide libstdc++.so.6 binaries (Vadim).
0044: [RESOLVED: RN73] Limit number of file handles used per table (Brian).
0043: XA (two-phase commit) support (Peter).
------- 2007-03-13
0042: [RESOLVED: RN108] Implemement STATUS commands.
0041: Implement index prefix compression.
------- 2007-03-07
0040: [RESOLVED: RN60] Update in-place when a transaction updates the same record more than once.
0039: Set the number and size of the segments dynamically according to the amount of memory in the cache (and the number of CPUs?) (as discussed with: Peter & Vadim).
0038: [RESOLVED: RN133] Improve the efficiency of the locks by using atomic compare and swap (Peter & Vadim).
0037: [RESOLVED: RN133] Instead of a global LRU list, use a LRU list for segment of the cache (Peter & Vadim). [ Note: a global list using a TAS lock and change time (so that LRU is not always updated) is most efficient].
0036: Add support for deferred foreign key checking (requested by: Mark).
0035: [RESOLVED: RN71] Remove the 2000 table limit (reported by: Hakan).
------- 2007-02-28
0035: [RESOLVED: RN74, RN107] Build in the PBXT system parameters (currently they must be set using environment variables.
0034: [RESOLVED: RN117] Initial documentation (yes, it must be done!)
0033: Make the error code returned on lock error configurable.
0032: [RESOLVED: RN65] Create a source code pluggable version for Windows.
0031: [RESOLVED: RN66] PBXT corrupts the index file when the size exceeds 4 GB (reported by: Luciano)
0030: [RESOLVED: RN102] Implement pbxt_index_flush_delay. Postpones index writing in order to speed up imports. [Resolution uses that fact hat index entries that are missing are added during recovery. As a result, index flushing can be delayed.]
0029: [RESOLVED: RN103] Implement SELECT ... FOR UPDATE (recommended by: Robin).
------- 2007-02-14
0028: Implement CREATE TABLE ... DATA/INDEX DIRECTORY (suggested by: Robin).
------- 2006-12-06
0027: [RESOLVED: RN53] Bug in pbxt with query caching (reported by: Giuseppe) caused violation of transaction isolation.
------- 2006-08-05
0026: Implement BACKUP and RESTORE table (planned for the first post release version).
0025: Implement DISABLE/ENABLE KEYS. Works for FOREIGN KEYs, currently no plans to implement for disabling indexes.
0024: Implement ANALYZE TABLE (planned for the first post release version).
0023: Implement CHECK TABLE (planned for the first release candidate).
0022: [RESOLVED: RN18] Implement TRUNCATE TABLE and DELETE FROM <table>; (i.e. a DELETE without WHERE clause). Currently this function does not cause an error, but no rows are deleted.
------- 2006-07-06
0021: [RESOLVED: RN28] .../mysql-test/mysql-test-run --force --mysqld=--default-storage-engine=pbxt produces a number of errors (reported by: Hakan): As far as I can tell some failures are unnessary but others are bugs. All need to be checked.
------- 2006-07-03
0020: [RESOLVED: RN49] Implement referential integrity (planned for the first release candidate).
------- 2006-04-01
0019: [RESOLVED: RN28] mysql-test-run hangs on alter table (reported by: Hakan): Running a test like ./mysql-test-run.pl --mysqld=--default-storage-engine=pbxt, hangs on ALTER TABLE.
0018: Implement GEOMETRY date type. Note: There are currently no plans to implement this feature.
------- 2006-03-31
0017: [RESOLVED: RN37] MySQL 5.x Version (reported by: Ronald, Giuseppe).
0016: [RESOLVED: RN13] Hang on "DROP DATABASE" (reported by: Giuseppe). Load the world database (http://downloads.mysql.com/docs/world.sql) and convert all tables into PBXT. Then, the drop database command hangs.
0015: [RESOLVED: RN12] Implement isolation level "repeatable read" (reported by: Giuseppe). Current PBXT only supports isolation level "committed read". This means committed data can be seen no matter when it was committed. Use SELECT ... FOR UPDATE to guarantee repeatable read, on data already read.
0014: [RESOLVED: RN7] Two transactions cannot insert simaltaneously if they use auto_increment (reported by: Giuseppe). See also 0005.
0013: [RESOLVED: RN11] Implement buffered write (reported by: Giuseppe): Lack of buffered write leads to bad performance in operations such as ALTER TABLE ENGINE = PBXT and INSERT ... SELECT.
0012: [RESOLVED: RN18] TRUNCATE does not work (reported by: Giuseppe)
0011: [RESOLVED: RN2] Load Sakila Sample Database (reported by: Ronald): ALTER TABLE film ENGINE=PBXT; fails
0010: [RESOLVED: RN6] sql-bench (reported by: Dmitry): ./run-all-tests --create-options=TYPE=PBXT fails.
0009: [RESOLVED: RN29] 64-bit Linux (reported by: Hakan): PBXT current does not compile under 64-bit Linux.
------- 2006-03-16
0008: [RESOLVED: RN10] Enforcing the unique index constraint:
An index declared as "unique" must return a "duplicate unique key" error when inserting a duplicate value. The difficulty part of implementing this in PBXT is that we may encounter a duplicate value that has not yet been committed. The index reading thread must then wait for the transaction to commit or abort.
0007: [RESOLVED: RN9] Cleaning up empty index nodes:
The Lehman and Yoa algorithm used for indexing does not describe a way of cleaning up empty index nodes on-the-fly. A search of the relevant literature for an algorithm also turns up empty handed (periodic "reorg" is mostly suggested). I have subsequently devised an algorithm that will do the job. This needs to be implemented.
0006: [RESOLVED: RN8] Cache Balancing:
PBXT uses a number of small caches in order to improve concurrency (rather than one large cache). A process is required to manage the amount of cache memory used as a whole. The process must distribute the overall amount of memory available for caching over the small caches, according to demand.
0005: [RESOLVED: RN7] Implement a faster auto-increment method
Currently the auto-increment is handled by the default method used in MySQL. This is done by performing a "fetch-last" on the index for each insert to find the highest key value. This works well unless there are large number empty index nodes due to the problem described in (2) above.
PBXT Testing To-Do List
This is my first take on what still must be tested. My thanks to Ronald Bradford who is working on a generic testing framework that can be used to test PBXT.
0004: [RESOLVED: RN6, RN28] MySQL Tests:
Several tests (for mysql-test-run) written for other engines can be adapted and used to test PBXT.
0003: [RESOLVED: RN30] Multi-processor Test:
There is a difference between preemptive multitasking and true multitasking, which you have on a multi-processor (or dual core) machine. I don't expect any fundamental problems here, but it must be tested.
0002: [RESOLVED: RN5, RN30, RN43] Multi-user/locking Test:
How does the engine perform with a number of concurrent users running various transactions on a number of different tables?
This is a difficult test to write because it need to simulate a production situation. To test at least 2 or 3 machines is required. The idea is not to use too much data so that a lot of conflicts may occur.
0001: [RESOLVED: RN4, RN43] Load/Stability Test:
How does the engine perform under heavy load over a long period of time? How stable is the engine on power outage, etc?
The test could use a variation of the test program written for test (3) above. At least 3 test machines would be required. The test must be modified to cause as much activity as possible. The test should monitor the performance under load.
DRIZZLE_STORAGE_ENGINE(pbxt,no, [PBXT Storage Engine],
[MVCC-based transactional engine], [max,max-no-ndb])
DRIZZLE_PLUGIN_DIRECTORY(pbxt, [storage/pbxt])
DRIZZLE_PLUGIN_STATIC(pbxt, [src/libpbxt.a])
DRIZZLE_PLUGIN_MANDATORY(pbxt) dnl Default
DRIZZLE_PLUGIN_ACTIONS(pbxt, [
AC_CONFIG_FILES(storage/pbxt/src/Makefile)
])
# Copyright (c) 2008 PrimeBase Technologies GmbH
#
# PrimeBase XT
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# 2006-03-22 Paul McCullagh
#
# H&G2JCtL
#
# This file is used to make the Windows version
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMYSQL_SERVER")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMYSQL_SERVER")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMYSQL_SERVER -DSAFEMALLOC -DSAFE_MUTEX -DDEBUG")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DMYSQL_SERVER -DSAFEMALLOC -DSAFE_MUTEX -DDEBUG")
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/regex
${CMAKE_SOURCE_DIR}/extra/yassl/include)
SET(PBXT_SOURCES ha_pbxt.cc bsearch_xt.cc index_xt.cc strutil_xt.cc cache_xt.cc linklist_xt.cc
ccutils_xt.cc lock_xt.cc table_xt.cc database_xt.cc thread_xt.cc
datadic_xt.cc memory_xt.cc trace_xt.cc datalog_xt.cc myxt_xt.cc util_xt.cc
filesys_xt.cc pthread_xt.cc xaction_xt.cc restart_xt.cc xactlog_xt.cc
hashtab_xt.cc sortedlist_xt.cc heap_xt.cc streaming_xt.cc tabcache_xt.cc
systab_xt.cc ha_xtsys.cc discover_xt.cc
bsearch_xt.h linklist_xt.h tabcache_xt.h cache_xt.h lock_xt.h table_xt.h
ccutils_xt.h thread_xt.h database_xt.h memory_xt.h trace_xt.h
datadic_xt.h pbms.h util_xt.h datalog_xt.h myxt_xt.h xaction_xt.h
filesys_xt.h pthread_xt.h xactlog_xt.h ha_pbxt.h restart_xt.h xt_config.h
hashtab_xt.h sortedlist_xt.h xt_defs.h heap_xt.h streaming_xt.h xt_errno.h
systab_xt.h ha_xtsys.h discover_xt.h
index_xt.h strutil_xt.h)
IF(NOT SOURCE_SUBLIBS)
ADD_LIBRARY(pbxt ${PBXT_SOURCES})
ADD_DEPENDENCIES(pbxt GenError)
ENDIF(NOT SOURCE_SUBLIBS)
# Used to build Makefile.in
MYSQLDATAdir = $(localstatedir)
MYSQLSHAREdir = $(pkgdatadir)
MYSQLBASEdir= $(prefix)
MYSQLLIBdir= $(pkglibdir)
pkgplugindir = $(pkglibdir)/plugin
AM_CPPFLAGS = -I$(top_srcdir)/../../
LIBS =
LDADD =
noinst_HEADERS = bsearch_xt.h cache_xt.h ccutils_xt.h database_xt.h \
datadic_xt.h datalog_xt.h filesys_xt.h hashtab_xt.h \
ha_pbxt.h heap_xt.h index_xt.h linklist_xt.h \
memory_xt.h myxt_xt.h pthread_xt.h restart_xt.h \
streaming_xt.h sortedlist_xt.h strutil_xt.h \
tabcache_xt.h table_xt.h trace_xt.h thread_xt.h \
util_xt.h xaction_xt.h xactlog_xt.h lock_xt.h \
systab_xt.h ha_xtsys.h discover_xt.h \
mybs.h xt_config.h xt_defs.h xt_errno.h
EXTRA_LTLIBRARIES = libpbxt.la
libpbxt_la_SOURCES = bsearch_xt.cc cache_xt.cc ccutils_xt.cc database_xt.cc \
datadic_xt.cc datalog_xt.cc filesys_xt.cc hashtab_xt.cc \
ha_pbxt.cc heap_xt.cc index_xt.cc linklist_xt.cc \
memory_xt.cc myxt_xt.cc pthread_xt.cc restart_xt.cc \
streaming_xt.cc sortedlist_xt.cc strutil_xt.cc \
tabcache_xt.cc table_xt.cc trace_xt.cc thread_xt.cc \
systab_xt.cc ha_xtsys.cc discover_xt.cc \
util_xt.cc xaction_xt.cc xactlog_xt.cc lock_xt.cc
libpbxt_la_LDFLAGS = -module
# These are the warning Drizzle uses:
# DRIZZLE_WARNINGS = -W -Wall -Wextra -pedantic -Wundef -Wredundant-decls -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter
libpbxt_la_CXXFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN
libpbxt_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN -std=c99
EXTRA_LIBRARIES = libpbxt.a
noinst_LIBRARIES = libpbxt.a
libpbxt_a_SOURCES = $(libpbxt_la_SOURCES)
libpbxt_a_CXXFLAGS = $(AM_CFLAGS) -DDRIZZLED -Wno-long-long
libpbxt_a_CFLAGS = $(AM_CFLAGS) -DDRIZZLED -std=c99
EXTRA_DIST = CMakeLists.txt
# Used to build Makefile.in
INCLUDES = $(ENG_MYSQL_INC)
LIBS =
LDADD =
plugindir = $(ENG_PLUGIN_DIR)
noinst_HEADERS = bsearch_xt.h cache_xt.h ccutils_xt.h database_xt.h \
datadic_xt.h datalog_xt.h filesys_xt.h hashtab_xt.h \
ha_pbxt.h heap_xt.h index_xt.h linklist_xt.h \
memory_xt.h myxt_xt.h pthread_xt.h restart_xt.h \
streaming_xt.h sortedlist_xt.h strutil_xt.h \
tabcache_xt.h table_xt.h trace_xt.h thread_xt.h \
util_xt.h xaction_xt.h xactlog_xt.h lock_xt.h \
systab_xt.h ha_xtsys.h discover_xt.h \
pbms.h xt_config.h xt_defs.h xt_errno.h locklist_xt.h
plugin_LTLIBRARIES = libpbxt.la
libpbxt_la_SOURCES = bsearch_xt.cc cache_xt.cc ccutils_xt.cc database_xt.cc \
datadic_xt.cc datalog_xt.cc filesys_xt.cc hashtab_xt.cc \
ha_pbxt.cc heap_xt.cc index_xt.cc linklist_xt.cc \
memory_xt.cc myxt_xt.cc pthread_xt.cc restart_xt.cc \
streaming_xt.cc sortedlist_xt.cc strutil_xt.cc \
tabcache_xt.cc table_xt.cc trace_xt.cc thread_xt.cc \
systab_xt.cc ha_xtsys.cc discover_xt.cc \
util_xt.cc xaction_xt.cc xactlog_xt.cc lock_xt.cc locklist_xt.cc
libpbxt_la_LDFLAGS = -module
# These are the warnings Drizzle uses:
# DRIZZLE_WARNINGS = -W -Wall -Wextra -pedantic -Wundef -Wredundant-decls -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter
libpbxt_la_CXXFLAGS = $(AM_CXXFLAGS) -DMYSQL_DYNAMIC_PLUGIN
libpbxt_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN -std=c99
EXTRA_LIBRARIES = libpbxt.a libxtutil.a
noinst_LIBRARIES = libpbxt.a libxtutil.a
libpbxt_a_SOURCES = $(libpbxt_la_SOURCES)
libpbxt_a_CXXFLAGS = $(AM_CXXFLAGS) $(DRIZZLE_WARNINGS)
libpbxt_a_CFLAGS = $(AM_CFLAGS) -std=c99 $(DRIZZLE_WARNINGS)
libxtutil_a_SOURCES = strutil_xt.cc \
trace_xt.cc
libxtutil_a_CXXFLAGS = $(AM_CXXFLAGS)
libxtutil_a_CFLAGS = $(AM_CFLAGS)
EXTRA_DIST = CMakeLists.txt
/* Copyright (c) 2005 PrimeBase Technologies GmbH
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* 2004-01-03 Paul McCullagh
*
* H&G2JCtL
*/
#include "xt_config.h"
#include <stdio.h>
#include "bsearch_xt.h"
#include "pthread_xt.h"
#include "thread_xt.h"
/**
* Binary search a array of 'count' items, with byte size 'size'. This
* function returns a pointer to the element and the 'index'
* of the element if found.
*
* If not found the index of the insert point of the item
* is returned (0 <= index <= count).
*
* The comparison routine 'compar' may throw an exception.
* In this case the error details will be stored in 'thread'.
*/
void *xt_bsearch(XTThreadPtr thread, const void *key, register const void *base, size_t count, size_t size, size_t *idx, const void *thunk, XTCompareFunc compar)
{
register size_t i;
register size_t guess;
register int r;
i = 0;
while (i < count) {
guess = (i + count - 1) >> 1;
r = (compar)(thread, thunk, key, ((char *) base) + guess * size);
if (r == 0) {
*idx = guess;
return ((char *) base) + guess * size;
}
if (r < 0)
count = guess;
else
i = guess + 1;
}
*idx = i;
return NULL;
}
/* Copyright (c) 2005 PrimeBase Technologies GmbH
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* 2004-01-03 Paul McCullagh
*
* H&G2JCtL
*/
#ifndef __xt_bsearch_h__
#define __xt_bsearch_h__
#include "xt_defs.h"
struct XTThread;
void *xt_bsearch(struct XTThread *self, const void *key, register const void *base, size_t count, size_t size, size_t *idx, const void *thunk, XTCompareFunc compar);
#endif
This diff is collapsed.
/* Copyright (c) 2005 PrimeBase Technologies GmbH
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* 2005-05-24 Paul McCullagh
*
* H&G2JCtL
*/
#ifndef __xt_cache_h__
#define __xt_cache_h__
//#define XT_USE_MYSYS
#include "filesys_xt.h"
#include "index_xt.h"
struct XTOpenTable;
struct XTIdxReadBuffer;
#ifdef DEBUG
//#define XT_USE_CACHE_DEBUG_SIZES
#endif
#ifdef XT_USE_CACHE_DEBUG_SIZES
#define XT_INDEX_CACHE_SEGMENT_SHIFTS 1
#else
#define XT_INDEX_CACHE_SEGMENT_SHIFTS 3
#endif
#define IDX_CAC_BLOCK_FREE 0
#define IDX_CAC_BLOCK_CLEAN 1
#define IDX_CAC_BLOCK_DIRTY 2
typedef enum XTPageLockType { XT_LOCK_READ, XT_LOCK_WRITE, XT_XLOCK_LEAF };
typedef enum XTPageUnlockType { XT_UNLOCK_NONE, XT_UNLOCK_READ, XT_UNLOCK_WRITE, XT_UNLOCK_R_UPDATE, XT_UNLOCK_W_UPDATE };
/* A block is X locked if it is being changed or freed.
* A block is S locked if it is being read.
*/
typedef struct XTIndBlock {
xtIndexNodeID cb_address; /* The block address. */
u_int cb_file_id; /* The file id of the block. */
/* This is protected by cs_lock */
struct XTIndBlock *cb_next; /* Pointer to next block on hash list, or next free block on free list. */
/* This is protected by mi_dirty_lock */
struct XTIndBlock *cb_dirty_next; /* Double link for dirty blocks, next pointer. */
struct XTIndBlock *cb_dirty_prev; /* Double link for dirty blocks, previous pointer. */
/* This is protected by cg_lock */
xtWord4 cb_ru_time; /* If this is in the top 1/4 don't change position in MRU list. */
struct XTIndBlock *cb_mr_used; /* More recently used blocks. */
struct XTIndBlock *cb_lr_used; /* Less recently used blocks. */
/* Protected by cb_lock: */
XTAtomicRWLockRec cb_lock;
xtWord1 cb_state; /* Block status. */
xtWord2 cb_handle_count; /* TRUE if this page is referenced by a handle. */
xtWord2 cp_flush_seq;
#ifdef XT_USE_DIRECT_IO_ON_INDEX
xtWord1 *cb_data;
#else
xtWord1 cb_data[XT_INDEX_PAGE_SIZE];
#endif
} XTIndBlockRec, *XTIndBlockPtr;
typedef struct XTIndReference {
XTPageUnlockType ir_ulock;
XTIndBlockPtr ir_block;
XTIdxBranchDPtr ir_branch;
} XTIndReferenceRec, *XTIndReferencePtr;
typedef struct XTIndFreeBlock {
XTDiskValue1 if_status_1;
XTDiskValue1 if_unused1_1;
XTDiskValue2 if_unused2_2;
XTDiskValue4 if_unused3_4;
XTDiskValue8 if_next_block_8;
} XTIndFreeBlockRec, *XTIndFreeBlockPtr;
typedef struct XTIndHandleBlock {
xtWord4 hb_ref_count;
struct XTIndHandleBlock *hb_next;
XTIdxBranchDRec hb_branch;
} XTIndHandleBlockRec, *XTIndHandleBlockPtr;
typedef struct XTIndHandle {
struct XTIndHandle *ih_next;
struct XTIndHandle *ih_prev;
XTSpinLockRec ih_lock;
xtIndexNodeID ih_address;
xtBool ih_cache_reference; /* True if this handle references the cache. */
union {
XTIndBlockPtr ih_cache_block;
XTIndHandleBlockPtr ih_handle_block;
} x;
XTIdxBranchDPtr ih_branch;
} XTIndHandleRec, *XTIndHandlePtr;
void xt_ind_init(XTThreadPtr self, size_t cache_size);
void xt_ind_exit(XTThreadPtr self);
xtInt8 xt_ind_get_usage();
xtInt8 xt_ind_get_size();
xtBool xt_ind_write(struct XTOpenTable *ot, XTIndexPtr ind, xtIndexNodeID offset, size_t size, xtWord1 *data);
xtBool xt_ind_write_cache(struct XTOpenTable *ot, xtIndexNodeID offset, size_t size, xtWord1 *data);
xtBool xt_ind_clean(struct XTOpenTable *ot, XTIndexPtr ind, xtIndexNodeID offset);
xtBool xt_ind_read_bytes(struct XTOpenTable *ot, xtIndexNodeID offset, size_t size, xtWord1 *data);
void xt_ind_check_cache(XTIndexPtr ind);
xtBool xt_ind_reserve(struct XTOpenTable *ot, u_int count, XTIdxBranchDPtr not_this);
void xt_ind_free_reserved(struct XTOpenTable *ot);
void xt_ind_unreserve(struct XTOpenTable *ot);
void xt_load_indices(XTThreadPtr self, struct XTOpenTable *ot);
xtBool xt_ind_fetch(struct XTOpenTable *ot, xtIndexNodeID node, XTPageLockType ltype, XTIndReferencePtr iref);
xtBool xt_ind_release(struct XTOpenTable *ot, XTIndexPtr ind, XTPageUnlockType utype, XTIndReferencePtr iref);
void xt_ind_lock_handle(XTIndHandlePtr handle);
void xt_ind_unlock_handle(XTIndHandlePtr handle);
xtBool xt_ind_copy_on_write(XTIndReferencePtr iref);
XTIndHandlePtr xt_ind_get_handle(struct XTOpenTable *ot, XTIndexPtr ind, XTIndReferencePtr iref);
void xt_ind_release_handle(XTIndHandlePtr handle, xtBool have_lock, XTThreadPtr thread);
#ifdef DEBUG
//#define DEBUG_CHECK_IND_CACHE
#endif
//#define XT_TRACE_INDEX
#ifdef XT_TRACE_INDEX
#define IDX_TRACE(x, y, z) xt_trace(x, y, z)
#else
#define IDX_TRACE(x, y, z)
#endif
#endif
/* Copyright (c) 2005 PrimeBase Technologies GmbH
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* 2006-05-16 Paul McCullagh
*
* H&G2JCtL
*
* C++ Utilities
*/
#include "xt_config.h"
#include "pthread_xt.h"
#include "ccutils_xt.h"
#include "bsearch_xt.h"
static int ccu_compare_object(XTThreadPtr XT_UNUSED(self), register const void XT_UNUSED(*thunk), register const void *a, register const void *b)
{
XTObject *obj_ptr = (XTObject *) b;
return obj_ptr->compare(a);
}
void XTListImp::append(XTThreadPtr self, XTObject *info, void *key) {
size_t idx;
if (li_item_count == 0)
idx = 0;
else if (li_item_count == 1) {
int r;
if ((r = li_items[0]->compare(key)) == 0)
idx = 0;
else if (r < 0)
idx = 0;
else
idx = 1;
}
else {
xt_bsearch(self, key, li_items, li_item_count, sizeof(void *), &idx, NULL, ccu_compare_object);
}
if (!xt_realloc(NULL, (void **) &li_items, (li_item_count + 1) * sizeof(void *))) {
if (li_referenced)
info->release(self);
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
return;
}
memmove(&li_items[idx+1], &li_items[idx], (li_item_count-idx) * sizeof(void *));
li_items[idx] = info;
li_item_count++;
}
/* Copyright (c) 2005 PrimeBase Technologies GmbH
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* 2006-05-16 Paul McCullagh
*
* H&G2JCtL
*
* C++ Utilities
*/
#ifndef __ccutils_xt_h__
#define __ccutils_xt_h__
#include <errno.h>
#include "xt_defs.h"
#include "thread_xt.h"
class XTObject
{
private:
u_int o_refcnt;
public:
inline XTObject() { o_refcnt = 1; }
virtual ~XTObject() { }
inline void reference() {
o_refcnt++;
}
inline void release(XTThreadPtr self) {
ASSERT(o_refcnt > 0);
o_refcnt--;
if (o_refcnt == 0) {
finalize(self);
delete this;
}
}
virtual XTObject *factory(XTThreadPtr self) {
XTObject *new_obj;
if (!(new_obj = new XTObject()))
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
return new_obj;
}
virtual XTObject *clone(XTThreadPtr self) {
XTObject *new_obj;
new_obj = factory(self);
new_obj->init(self, this);
return new_obj;
}
virtual void init(XTThreadPtr self) { (void) self; }
virtual void init(XTThreadPtr self, XTObject *obj) { (void) obj; init(self); }
virtual void finalize(XTThreadPtr self) { (void) self; }
virtual int compare(const void *key) { (void) key; return -1; }
};
class XTListImp
{
protected:
bool li_referenced;
u_int li_item_count;
XTObject **li_items;
public:
inline XTListImp() : li_referenced(true), li_item_count(0), li_items(NULL) { }
inline void setNonReferenced() { li_referenced = false; }
void append(XTThreadPtr self, XTObject *info) {
if (!xt_realloc(NULL, (void **) &li_items, (li_item_count + 1) * sizeof(void *))) {
if (li_referenced)
info->release(self);
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
return;
}
li_items[li_item_count] = info;
li_item_count++;
}
void insert(XTThreadPtr self, XTObject *info, u_int i) {
if (!xt_realloc(NULL, (void **) &li_items, (li_item_count + 1) * sizeof(void *))) {
if (li_referenced)
info->release(self);
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
return;
}
memmove(&li_items[i+1], &li_items[i], (li_item_count-i) * sizeof(XTObject *));
li_items[i] = info;
li_item_count++;
}
void addToFront(XTThreadPtr self, XTObject *info) {
insert(self, info, 0);
}
/* Will sort! */
void append(XTThreadPtr self, XTObject *info, void *key);
inline bool remove(XTObject *info) {
for (u_int i=0; i<li_item_count; i++) {
if (li_items[i] == info) {
li_item_count--;
memmove(&li_items[i], &li_items[i+1], (li_item_count - i) * sizeof(XTObject *));
return true;
}
}
return false;
}
inline bool remove(XTThreadPtr self, u_int i) {
XTObject *item;
if (i >= li_item_count)
return false;
item = li_items[i];
li_item_count--;
memmove(&li_items[i], &li_items[i+1], (li_item_count - i) * sizeof(void *));
if (li_referenced)
item->release(self);
return true;
}
inline XTObject *take(u_int i) {
XTObject *item;
if (i >= li_item_count)
return NULL;
item = li_items[i];
li_item_count--;
memmove(&li_items[i], &li_items[i+1], (li_item_count - i) * sizeof(void *));
return item;
}
inline u_int size() const { return li_item_count; }
inline void setEmpty(XTThreadPtr self) {
if (li_items)
xt_free(self, li_items);
li_item_count = 0;
li_items = NULL;
}
inline bool isEmpty() { return li_item_count == 0; }
inline XTObject *itemAt(u_int i) const {
if (i >= li_item_count)
return NULL;
return li_items[i];
}
};
template <class T> class XTList : public XTListImp
{
public:
inline XTList() : XTListImp() { }
inline void append(XTThreadPtr self, T *a) { XTListImp::append(self, a); }
inline void insert(XTThreadPtr self, T *a, u_int i) { XTListImp::insert(self, a, i); }
inline void addToFront(XTThreadPtr self, T *a) { XTListImp::addToFront(self, a); }
inline bool remove(T *a) { return XTListImp::remove(a); }
inline bool remove(XTThreadPtr self, u_int i) { return XTListImp::remove(self, i); }
inline T *take(u_int i) { return (T *) XTListImp::take(i); }
inline T *itemAt(u_int i) const { return (T *) XTListImp::itemAt(i); }
inline u_int indexOf(T *a) {
u_int i;
for (i=0; i<size(); i++) {
if (itemAt(i) == a)
break;
}
return i;
}
void deleteAll(XTThreadPtr self)
{
for (u_int i=0; i<size(); i++) {
if (li_referenced)
itemAt(i)->release(self);
}
setEmpty(self);
}
void clone(XTThreadPtr self, XTListImp *list)
{
deleteAll(self);
for (u_int i=0; i<list->size(); i++) {
XTListImp::append(self, list->itemAt(i)->clone(self));
}
}
};
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* Copyright (c) 2008 PrimeBase Technologies GmbH, Germany
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created by Leslie on 8/27/08.
*
*/
#ifndef __DISCOVER_XT_H__
#define __DISCOVER_XT_H__
#ifdef DRIZZLED
#include <drizzled/common.h>
#else
#include "mysql_priv.h"
#endif
/*
* ---------------------------------------------------------------
* TABLE DISCOVERY HANDLER
*/
typedef struct dt_field_info {
/**
This is used as column name.
*/
const char* field_name;
/**
For string-type columns, this is the maximum number of
characters. For numeric data this can be NULL.
*/
uint field_length;
/**
For decimal columns, this is the maximum number of
digits after the decimal. For other data this can be NULL.
*/
char* field_decimal_length;
/**
This denotes data type for the column. For the most part, there seems to
be one entry in the enum for each SQL data type, although there seem to
be a number of additional entries in the enum.
*/
enum enum_field_types field_type;
/**
This is the charater set for non numeric data types including blob data.
*/
CHARSET_INFO *field_charset;
uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.)
const char* comment;
} DT_FIELD_INFO;
typedef struct dt_key_info
{
const char* key_name;
uint key_type; /* PRI_KEY_FLAG, UNIQUE_KEY_FLAG, MULTIPLE_KEY_FLAG */
const char* key_columns[8]; // The size of this can be set to what ever you need.
} DT_KEY_INFO;
int xt_create_table_frm(handlerton *hton, THD* thd, const char *db, const char *name, DT_FIELD_INFO *info, DT_KEY_INFO *keys, xtBool skip_existing);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* Copyright (c) 2005 PrimeBase Technologies GmbH
*
* PrimeBase XT
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* 2006-06-07 Paul McCullagh
*
* H&G2JCtL
*
* This file contains PBXT streaming interface.
*/
#ifndef __streaming_xt_h__
#define __streaming_xt_h__
#include "xt_defs.h"
#define PBMS_API pbms_api_PBXT
#include "pbms.h"
xtBool xt_init_streaming(void);
void xt_exit_streaming(void);
void xt_pbms_close_all_tables(const char *table_url);
xtBool xt_pbms_close_connection(void *thd, XTExceptionPtr e);
xtBool xt_pbms_open_table(void **open_table, char *table_path);
void xt_pbms_close_table(void *open_table);
xtBool xt_pbms_use_blob(void *open_table, char **ret_blob_url, char *blob_url, unsigned short col_index);
xtBool xt_pbms_retain_blobs(void *open_table, PBMSEngineRefPtr eng_ref);
void xt_pbms_release_blob(void *open_table, char *blob_url, unsigned short col_index, PBMSEngineRefPtr eng_ref);
void xt_pbms_drop_table(const char *table_path);
void xt_pbms_rename_table(const char *from_table, const char *to_table);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment