From: Marc Boeren Date: Fri, 4 May 2001 09:39:52 +0000 (+0000) Subject: Added docs about creating dbx-support for other databases. (Mc) X-Git-Tag: php-4.0.6RC1~153 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e3770563fca81dd48626b49d5eaa1b07edc559f4;p=php Added docs about creating dbx-support for other databases. (Mc) --- diff --git a/ext/dbx/howto_extend_dbx.html b/ext/dbx/howto_extend_dbx.html new file mode 100644 index 0000000000..8cbd0f69fc --- /dev/null +++ b/ext/dbx/howto_extend_dbx.html @@ -0,0 +1,316 @@ + + + +HOWTO extend dbx + + + + + + + + + + + +
+How-to code support for another database
+
+
+Every supported database module must be loaded by PHP before it can be used. Every supported database module must be added to the dbx-module before it can be used. Currently there is support for MySQL, PostgreSQL, Microsoft SQL Server and ODBC, but it is not difficult to add support for more databases.
+
+The dbx module is found in de PHP ext/dbx folder. The support-code is found in the same folder
+
+To add support for module 'blabla' the following steps must be taken:
+1. the dbx.c source file must be extended to recognize module 'blabla' and switch to the 'blabla' functions.
+2. the files dbx_blabla.h and dbx_blabla.c must be created and edited to produce the required response.
+3. add the files from step 2 to the project.
+4. compile.
+5. enjoy.
+
+You may need a bit of help for step 1 and 2. If you need help for step 3 or 4, you shouldn't try to attempt this probably :-). If you need help with step 5 you're in big trouble ;o)
+Help for step 1 and 2 is given below, bold text in code indicate the important bits.
+
+

+home
+

+ +
+1. the dbx.c source file must be extended
+
+
+Define a module identifier and assign it a unique number. Include your header file here as well.
+
+// defines for supported databases
+#define DBX_UNKNOWN 0
+#define DBX_MYSQL 1
+#define DBX_ODBC 2
+#define DBX_BLABLA 3
+// includes for supported databases
+#include "dbx.h"
+#include "dbx_mysql.h"
+#include "dbx_odbc.h"
+#include "dbx_blabla.h"
+
+Add code to the module_identifier_exists function so DBX_BLABLA will be recognized:
+
+int module_identifier_exists(long module_identifier) {
+    switch (module_identifier) {
+        case DBX_MYSQL: return module_exists("mysql");
+        case DBX_ODBC: return module_exists("odbc");
+        case DBX_BLABLA: return module_exists("blabla");
+        }
+    return 0;
+    }
+
+Add code to the get_module_identifier function so your extension will be recognized:
+
+int get_module_identifier(char * module_name) {
+    if (!strcmp("mysql", module_name)) return DBX_MYSQL;
+    if (!strcmp("odbc", module_name)) return DBX_ODBC;
+    if (!strcmp("blabla", module_name)) return DBX_BLABLA;
+    return DBX_UNKNOWN;
+    }
+
+Add code for exposing the DBX_BLABLA constant to the world:
+
+ZEND_MINIT_FUNCTION(dbx)
+{
+/*/	REGISTER_INI_ENTRIES(); /*/
+
+    REGISTER_LONG_CONSTANT("DBX_MYSQL", DBX_MYSQL, CONST_CS | CONST_PERSISTENT);
+    REGISTER_LONG_CONSTANT("DBX_ODBC", DBX_ODBC, CONST_CS | CONST_PERSISTENT);
+    REGISTER_LONG_CONSTANT("DBX_BLABLA", DBX_BLABLA CONST_CS | CONST_PERSISTENT);
+
+    [...]
+
+    return SUCCESS;
+    }
+
+Add code for inclusion in the phpinfo() function (optional, but recommended):
+
+ZEND_MINFO_FUNCTION(dbx)
+{
+    php_info_print_table_start();
+    php_info_print_table_row(2, "dbx support", "enabled");
+    php_info_print_table_row(2, "dbx support for MySQL", "enabled");
+    php_info_print_table_row(2, "dbx support for ODBC", "enabled");
+    php_info_print_table_row(2, "dbx support for BlaBla", "enabled");
+    php_info_print_table_end();
+    DISPLAY_INI_ENTRIES();
+}
+
+Finally, for the implementation of all switch_dbx_XXXXX functions, copy a 'case'-line for every function that you support (should be all functions!). Here is an example for only the switch_dbx_connect function:
+
+int switch_dbx_connect(zval ** rv, zval ** host, zval ** db, zval ** username, zval ** password, INTERNAL_FUNCTION_PARAMETERS, zval ** dbx_module) {
+    // returns connection handle as resource on success or 0 as long on failure
+    switch ((*dbx_module)->value.lval) {
+        case DBX_MYSQL: return dbx_mysql_connect(rv, host, db, username, password, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+        case DBX_ODBC: return dbx_odbc_connect(rv, host, db, username, password, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+        case DBX_BLABLA: return dbx_blabla_connect(rv, host, db, username, password, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+        }
+    zend_error(E_WARNING, "dbx_connect: not supported in this module");
+    return 0;
+    }
+
+This should be done for all switch_dbx_XXXXX functions. They are listed below:
+
+int switch_dbx_connect(...);
+int switch_dbx_pconnect(...);
+int switch_dbx_close(...);
+int switch_dbx_query(...);
+int switch_dbx_getcolumncount(...);
+int switch_dbx_getcolumnname(...);
+int switch_dbx_getcolumntype(...);
+int switch_dbx_getrow(...);
+int switch_dbx_error(...);
+
+This concludes the changes for the dbx.c file. All that is needed now is to actually code the dbx_blabla_connect and other functions, which we will see in the following step.
+
+

+top
+

+ +
+2. the files dbx_blabla.h and dbx_blabla.c
+
+
+The dbx_blabla.h and dbx_blabla.c file are created in the folder /ext/dbx.
+The easiest method is to just copy dbx_mysql.h en dbx_mysql.c, open both files, and do a search and replace ('blabla' for 'mysql' and 'BLABLA' for 'MYSQL'). Yes, case-sensitive.
+For the .h file, that's all.
+For the .c file, the fun has just started :-)
+In the .c is the actual realization of the database abstraction, where a call to a standard function is translated into one or more database-specific calls. For mysql, a dbx_connect translates to a mysql_connect followed by a mysql_select_db. Refer to the dbx_mysql.c and dbx_odbc.c files regularly for examples!
+In dbx.h one macro and one function are defined to make the calling of external module functions and returning of the results easier: dbx_call_any_function and MOVE_RETURNED_TO_RV.
+

+ The details of what each of the functions do, what parameters they get, and what parameters they should return are discussed below. But first, the dbx_mysql_connect function is presented and explained, so you get an idea of how things work.
+

+int dbx_mysql_connect(zval ** rv, zval ** host, zval ** db, zval ** username, zval ** password, INTERNAL_FUNCTION_PARAMETERS) {
+    // returns connection handle as resource on success or 0 as long on 
+    // failure
+    int number_of_arguments;
+    zval ** arguments[3];
+    zval * returned_zval=NULL;
+    zval * select_db_zval=NULL;
+
+    number_of_arguments=3;
+    arguments[0]=host;
+    arguments[1]=username;
+    arguments[2]=password;
+    dbx_call_any_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "mysql_connect", &returned_zval, number_of_arguments, arguments);
+    if (!returned_zval || returned_zval->type!=IS_RESOURCE) {
+        if (returned_zval) zval_ptr_dtor(&returned_zval);
+        return 0;
+        }
+    MOVE_RETURNED_TO_RV(rv, returned_zval);
+
+    number_of_arguments=2;
+    arguments[0]=db;
+    arguments[1]=rv;
+    dbx_call_any_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "mysql_select_db", &select_db_zval, number_of_arguments, arguments);
+    zval_ptr_dtor(&select_db_zval);
+
+    return 1;
+    }
+
+First of all, all functions return 0 on failure and 1 on success. These values are used in the dbx-routines, they are never actually given back to the PHP-script writer that calls the dbx_connect function.
+The actual value that is of interest to the caller is returned in the rv parameter. In this case it is a connection handle (or link identifier, in mysql-speak), that is also returned if the database selection doesn't succeed.
+The parameters that are of interest to the function are located between the rv and INTERNAL_FUNCTION_PARAMETERS parameters, in this case it is a host name, a db name, a username and a password. These are the values that the user specifies if he calls dbx_connect(); These parameters are used in the calls to the mysql-database functions. The user actually also specifies a module-name, that decides which connect-function should be called. Here, he specified 'mysql'.
+To actually call a mysql module function, you can use dbx_call_any_function where you specify the function name (it is used twice in dbx_mysql_connect, see 'mysql_connect' and 'mysql_select_db', they are printed bold in the code). The value that is returned from the function will be stored in the next argument, a zval * (e.g. returned_zval) parameter that you must declare locally. To actually return such a parameter, use the MOVE_RETURNED_TO_RV(rv, returned_zval) macro, which copies the values to rv and frees anything that may be left in returned_zval. Parameters that must be passed to the mysql-function are stored in the arguments array, which must be large enough to hold all parameters to the function-call that requires the most parameters (in this case, mysql_connect expects 3 parameters, mysql_select_db expects two parameters, so the arguments array is defined 'zval ** arguments[3]'). The number_of_arguments parameter is set to the actual number of arguments that the function-call requires. As you can see it is initialized to 3, for the first call to mysql_connect. Then it is set to 2, for the call to mysql_select_db. If you call a function that retrieves a value, and you don't return it with MOVE_RETURNED_TO_RV, then you must free the value using zval_ptr_dtor, as can be seen right after the call to mysql_select_db. This can also be seen directly after the call to mysql_connect, if somehow this function failed or didn't return a resource (on a successful connect mysql_connect returns a resource) the returned value is freed as well (and 0 is returned because the connection failed).
+

+OK, now the description of all functions that you should implement, and what is expected of them...
+

+int dbx_blabla_connect(zval ** rv, zval ** host, zval ** db, zval ** username, zval ** password, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on connect-failure and 1 on success
+// rv: connection handle as resource on success or nothing on failure
+
+dbx_blabla_connect creates a connection to a database on a specified host, using username and password for authentication. This may be done by connecting to a server and selecting a database (as mysql does), or connecting to a specific database directly (as in ODBC).
+What must be returned (in rv) is the link identifier that is returned from the blabla_connect function, in it's native form so the end-user can use $db->handle to call other blabla_* functions that expect this parameter.
+What must be returned from the function is a 1 on success and a 0 on failure. Remember that a failed database selection can still return a 1 because the connection succeeded!
+The host (string) is the name of the machine the server is run on, but it may be empty if a database name is enough to establish a connection.
+The db (string) is the name of the database to select, or, for e.g. ODBC, the identifier that is needed to actually select the database.
+The username (string) and password (string) are used for authentication.
+
+int dbx_blabla_pconnect(zval ** rv, zval ** host, zval ** db, zval ** username, zval ** password, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on pconnect-failure and 1 on success
+// rv: persistent connection handle as resource on success or nothing
+// on failure
+
+dbx_blabla_pconnect is identical to dbx_blabla_connect except that it will create a persistent connection.
+
+int dbx_blabla_close(zval ** rv, zval ** dbx_handle, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on close-failure and 1 on success
+// rv: 1 as bool on success or nothing on failure
+
+dbx_blabla_close closes an open connection, whether it was created persistently or not.
+What must be returned (in rv) is a boolean true that indicates when the connection was closed successfully. If it wasn't, no value is returned in rv.
+What must be returned from the function is a 1 on success and a 0 on failure. Note that an unsuccessful close is still a succeeded function call.
+The dbx_handle is the same value that you returned from dbx_blabla_connect or dbx_blabla_pconnect.
+
+int dbx_blabla_query(zval ** rv, zval ** dbx_handle, zval ** sql_statement, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on query-failure and 1 on success
+// rv: 1 as bool or a result identifier as resource on success 
+// or nothing on failure
+
+dbx_blabla_query executes an SQL statement over the connection.
+What must be returned (in rv) is a nothing on failure, on success it must return either a boolean 1 for queries that don't return data (like INSERT INTO) or a native result-handle for queries that do return data (SELECT). The native result handle ($q->handle) can be used by the end-user to call other blabla_* functions that expect this parameter.
+What must be returned from the function is a 1 on success and a 0 on failure. Note that a failed query execution can still return a 1 because the query function succeeded!
+The dbx_handle is the same value that you returned from dbx_blabla_connect or dbx_blabla_pconnect.
+The sql_statement (string) can have any value.
+
+int dbx_blabla_getcolumncount(zval ** rv, zval ** result_handle, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on query-failure and 1 on success
+// returns column-count as long on success or nothing on failure
+
+dbx_blabla_getcolumncount gets the number of fields that the query-result contains.
+What must be returned (in rv) is the number of fields as long from the query result specified by the result_handle.
+What must be returned from the function is a 1 on success and a 0 on failure.
+The result_handle is the same value that you returned from dbx_query.
+
+int dbx_blabla_getcolumnname(zval ** rv, zval ** result_handle, long column_index, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on failure and 1 on success
+// returns column-name as string on success or nothing on failure
+
+dbx_blabla_getcolumnname gets the fieldname of the specified column.
+What must be returned (in rv) is the fieldname as string of the given column.
+What must be returned from the function is a 1 on success and a 0 on failure.
+The result_handle is the same value that you returned from dbx_query.
+The column_index is a long that ranges from 0 to the value you returned from dbx_blabla_getcolumncount minus 1 [0..columncount-1].
+
+int dbx_blabla_getcolumntype(zval ** rv, zval ** result_handle, long column_index, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on failure and 1 on success
+// returns column-type as string on success or nothing on failure
+
+dbx_blabla_getcolumnname gets the field type of the specified column.
+What must be returned (in rv) is the field type as string of the given column.
+What must be returned from the function is a 1 on success and a 0 on failure.
+The result_handle is the same value that you returned from dbx_query.
+The column_index is a long that ranges from 0 to the value you returned from dbx_blabla_getcolumncount minus 1 [0..columncount-1].
+
+int dbx_blabla_getrow(zval ** rv, zval ** result_handle, long row_number, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on failure and 1 on success
+// returns array[0..columncount-1] as strings on success or 0 as long 
+// on failure
+
+dbx_blabla_getrow gets the next row from the query-results.
+In some cases (PostgreSQL) the rownumber is needed to actually fetch the row. This will be provided (it will be indexed starting at 0) by the dbx_query function. In other cases it is not needed and thus not used.
+What must be returned (in rv) is an indexed array[0..columncount-1] of strings, containing the data from the row (for mysql this is easy since it already performs this way, for ODBC the array has to be constructed inside this function from a loop that fetches the data for each column).
+What must be returned from the function is a 1 on success and a 0 on failure (function failed or there are no more rows available).
+The result_handle is the same value that you returned from dbx_query.
+
+int dbx_blabla_error(zval ** rv, zval ** dbx_handle, INTERNAL_FUNCTION_PARAMETERS);
+// int: returns 0 on failure and 1 on success
+// returns error message as string
+
+dbx_blabla_error gets the error message from the last database call.
+What must be returned (in rv) is the error message as a string.
+What must be returned from the function is a 1 on success and a 0 on failure.
+The dbx_handle is the same value that you returned from dbx_blabla_connect or dbx_blabla_pconnect.
+
+

+top
+

+

+

+For specifics or the finer details you can always refer to dbx_mysql.c and dbx_odbc.c to see everything in action.
+More Zend API documentation can be found at http://www.zend.com/apidoc.
+This document can be found at http://www.guidance.nl/php/dbx.
+
+

+top
+

+ + +