Index: openacs-4/packages/acs-core-docs/www/xml/kernel/db-api.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/xml/kernel/db-api.xml,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-core-docs/www/xml/kernel/db-api.xml 24 Dec 2001 19:31:43 -0000 1.2 +++ openacs-4/packages/acs-core-docs/www/xml/kernel/db-api.xml 2 Feb 2002 03:47:32 -0000 1.3 @@ -17,17 +17,17 @@ The Big Picture -One of ACS's great strengths is that code written for it is very close to +One of OpenACS's great strengths is that code written for it is very close to the database. It is very easy to interact with the database from anywhere -within ACS. Our goal is to develop a coherent API for database access which +within OpenACS. Our goal is to develop a coherent API for database access which makes this even easier. -There were four significant problems with the way ACS previously used the +There were four significant problems with the way OpenACS previously used the database (i.e., directly through the ns_db interface): -Handle management. We required code to pass database +Handle management. We required code to pass database handles around, and for routines which needed to perform database access but didn't receive a database handle as input, it was difficult to know from which of the three "magic pools" (main, subquery, and log) to @@ -36,7 +36,7 @@ -Nested transactions. In our Oracle driver, begin +Nested transactions. In our Oracle driver, begin transaction really means "turn auto-commit mode off" and end transaction means "commit the current transaction and turn auto-commit mode on." Thus if transactional code needed to call a @@ -73,7 +73,7 @@ -Unorthodox use of variables. The standard mechanism for +Unorthodox use of variables. The standard mechanism for mapping column values into variables involved the use of the set_variables_after_query routine, which relies on an uplevel variable named selection (likewise for @@ -82,7 +82,7 @@ -Hard-coded reliance on Oracle. It's difficult to +Hard-coded reliance on Oracle. It's difficult to write code supporting various different databases (dynamically using the appropriate dialect based on the type of database being used, e.g., using DECODE on Oracle and CASE ... WHEN on @@ -102,14 +102,14 @@ It lays the groundwork for addressing the fourth problem by assigning each -SQL statement a logical name. In a future version of the ACS Core, this API +SQL statement a logical name. In a future version of the OpenACS Core, this API will translate logical statement names into actual SQL, based on the type of database in use. (To smooth the learning curve, we provide a facility for writing SQL inline for a "default SQL dialect", which we assume to be Oracle for now.) -To be clear, SQL abstraction is not fully implemented in ACS +To be clear, SQL abstraction is not fully implemented in OpenACS 3.3.1. The statement names supplied to each call are not used by the API at all. The API's design for SQL abstraction is in fact incomplete; unresolved issues include: @@ -255,7 +255,7 @@ Bind Variables -Introduction +Introduction Most SQL statements require that the code invoking the statement pass along @@ -275,7 +275,7 @@ where some_presentation_id is a number which is a valid presentation ID of the presentation I want to delete. It's easy to write code handling situations like this since SQL statements can include -bind variables, which represent placeholders for actual +bind variables, which represent placeholders for actual data. A bind variable is specified as a colon followed by an identifier, so the statement above can be coded as: @@ -330,7 +330,7 @@ -Why Bind Variables Are Useful +Why Bind Variables Are Useful Why bother with bind variables at all - why not just write the Tcl statement @@ -374,7 +374,7 @@ dangerous queries and DML. -Usage +Usage Every db_* command accepting a SQL command as an argument supports bind variables. You can either @@ -466,7 +466,7 @@ -Nulls and Bind Variables +Nulls and Bind Variables When processing a DML statement, Oracle coerces empty strings into @@ -518,7 +518,7 @@ Therefore, the Database Access API provides a database-independent way to represent null (instead of the Oracle-specific idiom of the -empty string): db_null. +empty string): db_null. Use it instead of the empty string whenever you want to set a column value explicitly to null, e.g.: @@ -669,12 +669,12 @@ -db_abort_transaction +db_abort_transaction -db_abort_transaction +db_abort_transaction Aborts all levels of a transaction. That is if this is called within @@ -686,14 +686,14 @@ -db_null +db_null -db_null +db_null Returns a value which can be used in a bind variable to represent the SQL @@ -703,12 +703,12 @@ -db_foreach +db_foreach -db_foreach statement-name sql [ -bind bind_set_id | -bind bind_value_list ] \ +db_foreach statement-name sql [ -bind bind_set_id | -bind bind_value_list ] \ [ -column_array array_name | -column_set set_name ] \ code_block [ if_no_rows if_no_rows_block ] @@ -742,11 +742,11 @@ -db_1row +db_1row -db_1row statement-name sql [ -bind bind_set_id | -bind bind_value_list ] \ +db_1row statement-name sql [ -bind bind_set_id | -bind bind_value_list ] \ [ -column_array array_name | -column_set set_name ] @@ -769,11 +769,11 @@ -db_0or1row +db_0or1row -db_0or1row statement-name sql [ -bind bind_set_id | -bind bind_value_list ] \ +db_0or1row statement-name sql [ -bind bind_set_id | -bind bind_value_list ] \ [ -column_array array_name | -column_set set_name ] @@ -785,11 +785,11 @@ -db_nextval +db_nextval -db_nextval sequence-name +db_nextval sequence-name Returns the next value for the sequence sequence-name (using a @@ -802,11 +802,11 @@ -db_register_pooled_sequence +db_register_pooled_sequence -db_register_pooled_sequence sequence-name pool-size +db_register_pooled_sequence sequence-name pool-size Registers the sequence sequence-name to be pooled, with a pool @@ -816,11 +816,11 @@ -db_string +db_string -db_string statement-name sql [ -default default ] [ -bind bind_set_id | -bind bind_value_list ] +db_string statement-name sql [ -default default ] [ -bind bind_set_id | -bind bind_value_list ] Returns the first column of the result of SQL query @@ -834,11 +834,11 @@ -db_list +db_list -db_list statement-name sql [ -bind bind_set_id | -bind bind_value_list ] +db_list statement-name sql [ -bind bind_set_id | -bind bind_value_list ] Returns a Tcl list of the values in the first column of the result of SQL @@ -850,11 +850,11 @@ -db_list_of_lists +db_list_of_lists -db_list_of_lists statement-name sql [ -bind bind_set_id | -bind bind_value_list ] +db_list_of_lists statement-name sql [ -bind bind_set_id | -bind bind_value_list ] Returns a Tcl list, each element of which is a list of all column values @@ -866,11 +866,11 @@ -db_dml +db_dml -db_dml statement-name sql \ +db_dml statement-name sql \ [ -bind bind_set_id | -bind bind_value_list ] \ [ -blobs blob_list | -clobs clob_list | -blob_files blob_file_list | -clob_files clob_file_list ] @@ -915,18 +915,18 @@ -db_write_clob, -db_write_blob, -db_blob_get_file +db_write_clob, +db_write_blob, +db_blob_get_file -db_write_clob statement-name sql [ -bind bind_set_id | -bind bind_value_list ] +db_write_clob statement-name sql [ -bind bind_set_id | -bind bind_value_list ] -db_write_blob statement-name sql [ -bind bind_set_id | -bind bind_value_list ] +db_write_blob statement-name sql [ -bind bind_set_id | -bind bind_value_list ] -db_blob_get_file statement-name sql [ -bind bind_set_id | -bind bind_value_list ] +db_blob_get_file statement-name sql [ -bind bind_set_id | -bind bind_value_list ] Analagous to ns_ora write_clob/write_blob/blob_get_file. @@ -936,10 +936,10 @@ -db_release_unused_handles +db_release_unused_handles -db_release_unused_handles +db_release_unused_handles Releases any allocated, unused database handles. @@ -948,11 +948,11 @@ -db_transaction +db_transaction -db_transaction code_block [ on_error { code_block } ] +db_transaction code_block [ on_error { code_block } ] Executes code_block transactionally. Nested @@ -1003,12 +1003,12 @@ -db_resultrows +db_resultrows -db_resultrows +db_resultrows Returns the number of rows affected or returned by the previous @@ -1019,10 +1019,10 @@ -db_with_handle +db_with_handle -db_with_handle var code_block +db_with_handle var code_block @@ -1059,17 +1059,17 @@ -db_nullify_empty_string +db_nullify_empty_string -db_nullify_empty_string string +db_nullify_empty_string string For true SQL purists, we provide the convenience function -db_nullify_empty_string, which returns +db_nullify_empty_string, which returns [db_null] if its string argument is the empty string and can be used to encapsulate another Oracle quirk: @@ -1109,123 +1109,7 @@ -Implementation Design (work in progress) - -The ideas here are preliminary, so please send feedback to michael@arsdigita.com. There may well -be a much simpler, superior design that I (Michael) am just missing right -now. If so, please let me know! - - -The basic idea is to translate the logical -statement-name into an actual SQL statement, written in -the appropriate SQL dialect for the RDBMS that is in use. The -sql argument is essentially a convenience that enables -the SQL for the "default dialect" to be written inline. For 3.4, we -will probably use configuration parameters to tell the Database Access API -what the default dialect is and what dialect is actually in use: - - - - - -[ns/server/server_name/acs] -... -DefaultSQLDialect=oracle8 -SQLDialect=postgres7 - - - - -(An alternative approach would be to use the ACS Package Manager, i.e., -install a "pseudo-package" with no actual code to indicate what -RDBMS is installed. Then, the Database Access API could query the APM to -figure what SQL dialect to employ.) - - -For instructing the Database Access API to translate a named statement in -a specific SQL dialect, we may define a new API call: - - - - - -db_implement_statement statement_location statement_name sql_dialect sql - - - - -which would be called at server initialization time. The Database Access API -will then know to use the SQL statement appropriate for the specified -SQLDialect. (The name db_implement_statement is -very tentative.) - -Issues: - - -Is making the caller of db_implement_statement explicitly -specify the statement location (e.g., "/bboard/q-and-a") too much -of a pain? Can we make this more convenient somehow? - - - - -In the case that the inline SQL is not in the specified -SQLDialect, reading the rewritten SQL into memory for the life -of the server may not be a good idea. The three basic approaches I can think -of to implement the db_implement_statement API are: - - -Cache the rewritten SQL for the appropriate SQL dialect in an -nsv array - -Cache the rewritten SQL for the appropriate SQL dialect in a special -database table that we keep pinned in memory - -Cache the rewritten SQL for the appropriate SQL dialect in a special -file, maybe even a DBM file - - - - - -Given the above two issues, should we rethink the -db_implement_statement API altogether? - -One possibility is a file-based approach, where the alternative SQL -statements would live in conventionally named and located files, e.g., -/bboard/q-and-a.postgres7 would contain Postgres 7 versions of -the SQL statements in /bboard/q-and-a.tcl.) A potential con of -this approach is that the Database Access API would have to perform file I/O -for every SQL statement that's been rewritten. This may be a non-issue; I -don't actually know. (We could augment this approach with caching too, -perhaps a fixed-size LRU cache.) - -Another similar approach would be just to have one massive, magic file for -each SQL dialect that maps each statement identifier (location plus name) to -the corresponding statement. - - - -Another larger problem is the fact that this design does not work for -instances where we build a SQL statement based on control flow logic, e.g., -we sometimes join in an extra table based on the user input. This problem -doesn't mean that the design as a whole is broken; it just means that -this design alone does not get us all the way to full SQL abstraction. - - - -Version 2.1 of the ArsDigita Oracle -Driver adds a set of ns_ora analogs for the following -ns_db calls: 0or1row, 1row, -select, and dml. (It also adds ns_ora -array_dml.) Thus, the groundwork for implementing the above API for -ACS/Oracle is already established. - -We plan to defer to the OpenACS team for the Postgres implementation of -the API. - - ($Id$)