ACHTUNG: THE CPDO WIKI IS NOW (AS OF 2011-May-17) MAINTAINED ON A DEDICATED WIKI SITE: http://whiki.wanderinghorse.net/wikis/cpdo/?page=CpdoOverview
Overview of cpdo
The primary classes are:
- cpdo_driver: represents a single connection to a "native" database connection (e.g. to MySQL or sqlite3). It is comparable, in terms of overall design, to PHP's PDO interface. Drivers do things like quote SQL strings and create prepared statements.
- cpdo_stmt: a prepared statement. It is analog to PHP's PDOStatement interface, and represents both output-only SQL (INSERT, CREATE TABLE, etc.) and input-only SQL (e.g. SELECT) result sets. This is a departure from common conventions, which separate the statement and result set classes, but in my experience it is rather useful.
Each of those classes has member function whose behaviours are documented by the cpdo interface, but whose implementations are driver-specific. Features which do not need driver-specific details to work are provided in the form of non-member functions. (Many of the member functions are also available via non-member interfaces, for code readability reasons - some people prefer foo(x) over x->api->foo(x)).
The guiding principals of this project include:
- All operations which require db-specific implementations go into the cpdo_driver or cpdo_stmt APIs. All functionality which can be developed using only the generic operations defined via those APIs are implemented as non-member functions. The driver/statement APIs are quite large, and implementing them requires no small amount of effort. However, all of the functions in the API are there because those features require driver-specific implementations.
- SQL is to be sent via prepared statements, not user-supplied strings. The API of course provides convenience functions for sending "plain SQL", but it internally does so using prepared statements. (Why are the "plain SQL" functions not in the driver API? Because they can be done generically. See the previous point.)
- Portability is important, both in terms of the C code and the DB operations the API specifies. The driver/statement interface APIs only contain functions which i believe to be implementable using the majority of commonly-used DB back-ends. Some of the behaviours allow some leeway on the driver's part, e.g. it is up to the driver whether or not multiple statements can be opened/running at one time.
- Ease of use for the client is important.
- Well-documented. (Anyone familiar with my code knows that this is my modus operandi.) i pride myself on writing thorough, useful documentation, and at three days old the library had more documentation than many projects 10 times its size do. If i may be so bold as to say so, i've don't know of any other single developer who can out-document me :), and i do know of (many) whole teams of developers can't (or don't) collectively out-document me.
- No memory leaks are tolerated. i am a memory management fanatic, regularly run the code through valgrind, and also make an effort to test for leaks in error-handling code (where they happen most often, in my experience, where a function prematurely returns and fails to clean up resources it allocated). Unfortunately, MySQL introduces once leak when a connection is opened, but i can do nothing about it (see cpdo_mysql5 for details).
The core library knows, of course, nothing about any specific database server. That's where "drivers" come in. Each db back-end requires a driver, which is a cpdo_driver instance customized to work with that back-end. It is those drivers, and only those drivers, which create cpdo_stmt objects on behalf of the client.
The sqlite3 driver is used for the core development (because it's just so damned easy to use), and cpdo's sqlite3 driver supports the cpdo interface requirements to the letter.
The MySQL driver proved to be much more difficult to implement because of MySQL's truly odd requirement of making client code manually manage all input/output values, including their allocation, deallocation, and cryptic setup. In terms of code lines, the MySQL driver requires almost about 2.5 times as much code as cpdo_sqlite3, plus i had to add additional internal infrastructure in the core library to be able to support MySQL's buffering requirements.
It would be cool to wrap up ocilib, and i have access to a machine i could do that from, but i don't want to have to sit in the office late at night to work on this (that's where the machine is).