Index: openacs-4/packages/acs-core-docs/www/db-api-detailed.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/db-api-detailed.html,v diff -u -r1.48.2.11 -r1.48.2.12 --- openacs-4/packages/acs-core-docs/www/db-api-detailed.html 21 Jun 2016 07:44:35 -0000 1.48.2.11 +++ openacs-4/packages/acs-core-docs/www/db-api-detailed.html 23 Jun 2016 08:32:45 -0000 1.48.2.12 @@ -10,7 +10,7 @@

There were four significant problems with the way OpenACS previously used the database (i.e., directly through the ns_db interface):

  1. 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 +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 allocate a new handle. @@ -50,7 +50,7 @@ set_variables_after_subquery and subselection). -

  2. Hard-coded reliance on Oracle. It's difficult to +

  3. 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 @@ -68,9 +68,9 @@ 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:

    • how to add WHERE clause criteria dynamically

    • how to build a dynamic ORDER BY clause (Ben Adida has a -proposed solution for this)

    • how to define a statement's formal interface (i.e., what bind +proposed solution for this)

    • how to define a statement's formal interface (i.e., what bind variables it expects, what columns its SELECT clause must -contain if it's a query) without actually implementing the statement in a +contain if it's a query) without actually implementing the statement in a specific SQL dialect

    So why is the incremental change of adding statement naming to the API worth the effort? It is worth the effort because we know that giving each SQL @@ -79,25 +79,25 @@ advantage of the new support for bind variables will already require code that uses 3.3.0 version of the API to be updated.

The Bell Tolls for set_variables_after_query

-set_variables_after_query is gone! (Well, it's still there, -but you'll never need to use it.) The new API routines set local +set_variables_after_query is gone! (Well, it's still there, +but you'll never need to use it.) The new API routines set local variables automatically. For instance:

 
 db_1row select_names "select first_names, last_name from users where user_id = [ad_conn user_id]"
 doc_body_append "Hello, $first_names $last_name!"
 
 

-Like ns_db 1row, this will bomb if the query doesn't return -any rows (no such user exists). If this isn't what you want, you can +Like ns_db 1row, this will bomb if the query doesn't return +any rows (no such user exists). If this isn't what you want, you can write:

 
 if { [db_0or1row select_names "select first_names, last_name from users where user_id = [ad_conn user_id]"] } {
     doc_body_append "Hello, $first_names $last_name!"
 } else {
     # Executed if the query returns no rows.
-    doc_body_append "There's no such user!"
+    doc_body_append "There's no such user!"
 }
 
 

@@ -110,7 +110,7 @@ }

-That's right, db_foreach is now like ns_db +That's right, db_foreach is now like ns_db select plus a while loop plus set_variables_after_query plus an if statement (containing code to be executed if no rows are returned). @@ -120,7 +120,7 @@ db_foreach select_names "select first_names, last_name from users where last_name like 'S%'" { doc_body_append "Say hi to $first_names $last_name for me!<br>" } if_no_rows { - doc_body_append "There aren't any users with last names beginnings with S!" + doc_body_append "There aren't any users with last names beginnings with S!" }

Handle Management

@@ -135,7 +135,7 @@ doc_body_append "<li>User $first_names $last_name\n<ul>" db_foreach select_groups "select group_id from user_group_map where user_id = $user_id" { - # There's a selection in progress, so we allocated a database handle + # There's a selection in progress, so we allocated a database handle # from the subquery pool for this selection. doc_body_append "<li>Member of group #$group_id.\n" } if_no_rows { @@ -147,12 +147,12 @@ db_release_unused_handles

-A new handle isn't actually allocated and released for every selection, +A new handle isn't actually allocated and released for every selection, of course - as a performance optimization, the API keeps old handles around until db_release_unused_handles is invoked (or the script terminates).

Note that there is no analogue to ns_db gethandle - the -handle is always automatically allocated the first time it's needed.

Bind Variables

Introduction

+handle is always automatically allocated the first time it's needed.

Bind Variables

Introduction

Most SQL statements require that the code invoking the statement pass along data associated with that statement, usually obtained from the user. For instance, in order to delete a WimpyPoint presentation, a Tcl script might @@ -163,7 +163,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 +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 data. A bind variable is specified as a colon followed by an identifier, so @@ -177,7 +177,7 @@

When this SQL statement is invoked, the value for the bind variable :some_presentation_id is pulled from the Tcl variable -$some_presentation_id (in the caller's environment). Note +$some_presentation_id (in the caller's environment). Note that bind variables are not limited to one per statement; you can use an arbitrary number, and each will pull from the correspondingly named Tcl variable. (Alternatively, you can also specify an list or ns_set @@ -187,7 +187,7 @@ variable, or to use db_quote to escape single-quotes contained in the value. The following works fine, despite the apostrophe:

 
-set exclamation "That's all, folks!"
+set exclamation "That's all, folks!"
 db_dml exclamation_insert { insert into exclamations(exclamation) values(:exclamation) }
 
 

Note that you can use a bind variable in a SQL statement only where you @@ -223,7 +223,7 @@ eliminates this gaping security hole: since bind variable values are taken literally. Oracle will attempt to delete presentations whose presentation ID is literally '3 or 1 = 1' (i.e., no presentations, since -'3 or 1 = 1' can't possibly be a valid integer +'3 or 1 = 1' can't possibly be a valid integer primary key for wp_presentations. In general, since Oracle always considers the values of bind variables to be literals, it becomes more difficult for users to perform URL surgery to trick scripts into running @@ -377,7 +377,7 @@

API

Note that you never have to use ns_db anymore (including ns_db gethandle)! Just start doing stuff, and (if you want) call -db_release_unused_handles when you're done as a hint to +db_release_unused_handles when you're done as a hint to release the database handle.

db_null @@ -413,7 +413,7 @@ column values. Raises an error if the query does not return exactly 1 row.

Example:

 
 db_1row select_foo "select foo, bar from greeble where greeble_id = $greeble_id"
-# Bombs if there's no such greeble!
+# Bombs if there's no such greeble!
 # Now $foo and $bar are set.
 
 
db_0or1row
@@ -424,7 +424,7 @@
 returns 0. If more than one row is returned, throws an error. 

db_string
 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 -sql. If sql doesn't return a +sql. If sql doesn't return a row, returns default (or throws an error if default is unspecified). Analogous to database_to_tcl_string and @@ -440,15 +440,15 @@

db_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 -query sql. If sql doesn't +query sql. If sql doesn't return any rows, returns an empty list. Analogous to database_to_tcl_list.

db_list_of_lists
 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 in a row of the result of SQL query sql. If -sql doesn't return any rows, returns an empty list. +sql doesn't return any rows, returns an empty list. (Analogous to database_to_tcl_list_list.)

db_list_of_ns_sets
@@ -560,11 +560,11 @@
     

Each row also has a column, rownum, automatically added and set to the row number, starting with 1. Note that this will - override any column in the SQL statement named 'rownum', also if you're + override any column in the SQL statement named 'rownum', also if you're using the Oracle rownum pseudo-column.

If the -local is passed, the variables defined - by db_multirow will be set locally (useful if you're compiling dynamic templates + by db_multirow will be set locally (useful if you're compiling dynamic templates in a function or similar situations).

You may supply a code block, which will be executed for each row in @@ -580,7 +580,7 @@ useful for things like constructing a URL for the object retrieved by the query.

- If you're constructing your multirow through multiple queries with the + If you're constructing your multirow through multiple queries with the same set of columns, but with different rows, you can use the -append switch. This causes the rows returned by this query to be appended to the rows already in the multirow, instead of starting @@ -612,7 +612,7 @@

db_with_handle
 db_with_handle var code_block
 

Places a database handle into the variable var and -executes code_block. This is useful when you don't +executes code_block. This is useful when you don't want to have to use the new API (db_foreach, db_1row, etc.), but need to use database handles explicitly.

Example:

 
@@ -623,7 +623,7 @@
 }
 
 db_with_handle db {
-    # Now there's a database handle in $db.
+    # Now there's a database handle in $db.
     set selection [ns_db select $db "select foo from bar"]
     while { [ns_db getrow $db $selection] } {
         set_variables_after_query
@@ -677,7 +677,7 @@
 			db_package_supports_rdbms_p db_type_list
 		

Returns 1 if db_type_list contains the current RDMBS type. A package - intended to run with a given RDBMS must note this in it's package info + intended to run with a given RDBMS must note this in it's package info file regardless of whether or not it actually uses the database.