Sunday, February 26, 2006

Don't you need proper error handling?

In the mysql forums, there's been a quite lively discussion regarding how to go about error handling. A quick summary:

The MySQL Stored Procedure, trigger and function syntax supports HANDLERs to react to on or more CONDITIONs, should they arise. The syntax and the semantics of these are compliant with the 2003 version of the SQL Standard (this is both an ANSI as well as an ISO standard).

A condition can take several forms: it might be:

  • a particular SQLSTATE (these are defined in the SQL Standard)

  • a particular mysql specific error code

  • one of the predefined identifiers for the general conditions: NOT FOUNDSQLEXCEPTION or SQLWARNING
  • the name of a previously declared, user defined condition (right now, a condition declaration must be bound to a SQLSTATE or a mysql specific errorcode, effectively aliasing it)



I think the concept of declaring separate handlers for capturing specific error conditions is both powerful and elegant: It allows you to deal with errors in a generic manner, and it separates the code that deals with errors entirely from the code that is supposed to do the 'business-as-usual' work. So far, so good. However, just being able to react to errors is not good enough:


  • In order to sucessfully handle the general conditions NOT FOUND, SQLWARNING and SQLEXCEPTION, the handler need to be able to retrieve some details concerning the specific error.

  • One needs to be able to explicitly raise a (user-defined)condition using a special statement

  • When a handler finds it is not suitable to handle the condition fully itself after all, it should be able to pass the condition on to the outer blocks to give other handlers a chance



Getting information on the condition being handled



The lack of this feature has been the subject of questions and confusion in the mysql forums for quite some time, at least as long as I am on them (which is about july 2005 - we're talking mysql 5.0.7 here. There are older ones too: check out this one).
Especially when declaring a handler for something as general as SQLEXCEPTION or SQLWARNING, both of which act as a sort of 'wildcard' conditions, one needs to be able to find out the nature of the actual problem that occurred. A snipppet might explain this:


begin
declare continue handler for not found
--handle not found
;
--
--other specific handlers go here
--

-- catch all warnings: log what's left unhandled sofar
declare exit handler for sqlwarning
--
-- log the error
--
-- call p_log_condition(
-- sqlstate
-- , message_text
-- );

call p_log_condition(
'?'
, 'some problem occurred'
);

end;


The snippet does it's best to handle all the conditions it can. However, it is anticipated that there might be some condition that is unforseen, or that cannot be handled here. So, it declares a handler for SQLWARNING in order to log the occurrence of the warning. But alas! Theres no way we can store the message text or error number for that matter, because we have no way to access it.

But is this really true? Can't we access it? We know the mysql command line client is capable of showing us the message text using a show warnings statement. Also, the libraries that connect to mysql do have methods to get to the message text and error number. For example, in php we can use mysql_error() to get the message and mysql_errno() to get the error number.

Well, I don't know why, but has far as I have looked I keep concluding that the mysql stored procedure language has no construct whatsoever to get to the error number or message. And, no, I really do not want to build all the generic error handling in my applications.

Other rdbms-es I work with do have facilities to do this. For example, in Oracle you can use the global variables SQLERRM and SQLCODE to get to the error message and number of the last executed statement respectively. In MS SQL, some similar device is available. There, you can use the @@ERROR global variable to retrieve the error number caused by the last executed statement. For example, take a look at how this snippet would look in Oracle PLSQL:


begin
--statements go here
exception
when no_data_found then
--handle not found
when others then
p_log_exception(
sqlcode
, sqlerrm
);
end;


Some people might argue that I shouldn't compare mysql to those rdbms-es, but I make a point of continuing to do so. A lot of my interest in mysql has to do with how well it can do whatever I do now with Oracle and MS SQL. I really think mysql is competitive with those rdbms-es in a lot of ways, and I regret to say that error handling from inside stored procedures is not one of the things where mysql is winning that battle - not right now anyway.

What can be said of the way Oracle and MS SQL have solved this is that they use proprietary feature to do it. From a practical point of view, I do feel that any feature, proprietary or otherwise, is a lot better than having no such feature at all. However, having said that, the SQL Standard that MySQL strives to adhere too does describe a construct that can be used: the GET DIAGNOSTICS statement.

I don't want to get into all the details and underlying concepts but I trust that a simple snippet will explain most of the functionalities of this statement:


begin
declare continue handler for not found
--handle not found
;
--
--other specific handlers go here
--

-- catch all warnings: log what's left unhandled sofar
declare exit handler for sqlwarning
begin
get diagnostics condition 1
set v_sqlstate := RETURNED_SQLSTATE
, v_message_text := MESSAGE_TEXT
;
call p_log_condition(
v_sqlstate
, v_message_text
);
end;
end;


So, the get diagnostics statement means we are ordering some info from the diagnostics area. You can think of that as a piece of memory that stores all kinds of characteristics associated with statement execution. The condition keyword means we are requesting one of the detail sections of the diagnostics area, and the 1 means we want to examine the first of these detail sections (as statement execution maybe associated with several details). Then, an ordinary SET statement follows, assigning the values of the predefined fields (MESSAGE_TEXT, RETURNED_SQL_STATE) of the diagnostics area detail section to our locally declared variables.

I must say that I didn't examine the standard thoroughly enough to know when to expect multiple detail sections in the diagnostics area. If the number of detail sections would be limited to just one, you could just as well have global variables for all the predefined fields in the diagnostics area. Anyway, whatever the syntax will be, I think this feature should be implemented in mysql.

Explicitly raising a condition



This keeps coming back in the forums. And I've seen these too from the start (well, my start) on the forums. In fact, a forum search found me one that dates back more than a year ago (see: http://forums.mysql.com/read.php?98,55535; http://forums.mysql.com/read.php?20,15856; http://forums.mysql.com/read.php?99,22523; http://forums.mysql.com/read.php?98,24044).

The idea is quite simple: normally, conditions arise as a side effect of executing statements. Conditions that arise as a result of some runtime error are especially interesting, as they will probably need to be handled (the procedure or trigger or function simply exits if the condition is serious enough and is left unhandled). In some cases a procedure, function or trigger needs to deliberately cause a condition because it detects some state of affairs that it violate some business rule.

A simple case is parameter checking. SUppose you have some kind of procedure to encapsulate some piece of business logic. You really must check if the parameters have valid values before doing all kinds of work inside the procedure, but you certainly don't want to go beyond that. The cleanest way to solve this would be to raise a user defined exception, and set up the diagnostics area in the process so that the caller can extract usefule information from there when it handles the condition.

Another case are triggers. Especially in the absence of check constraints, triggers receive a great deal of interest. In principle, triggers allow one to guard complex
business rules and integrity constraints. However, what to do when the tigger detects a possible violation of the business rule? You'd want the trigger to raise a condition, so that the triggering statement fails because of it. Of course, the diagnostics area would be enriched prior to allow proper handling.

Now, some people might shrug and say something like "..well, I can deal with the procedure parameter problem just fine, because I can check the parameters and exit the procedure when they aren't valid." But this is besides the point. The point is that the caller should have a clear, clean way of determining whether the procedure did the job it was supposed to do, and if not, why it failed.

Of course, you can work around all that, and that's what's been happening for some time now. It's always possible to deliberately provoke a condition. Some of the flavours are presented here:


  • insert a duplicate to cause a unique constraint violation

  • call a procedure that does not exist

  • call an UDF of which the xxx_init() function always returns 1

  • assign a string value to an TINYINT variable



You can find a lively exchange of these methods here, along with some links and more explanations. Lately, the discussion there is also about which method is best.

In my opinion, neither of them is very good. Andrew Gilfrin too has pointed out in several locations that the real problem is not so much raising the condition - the real problem is finding out afterwards what the real problem is. Or - the other way around, distinguishing the cases where a non-deliberate unique constraint violation occurrs, when a non-existant procedure was called undeliberatley (maybe someone dropped the procedure? ) etc. - you'll get the picture.

Again, other rdbms-es have solved this in their own way. In oracle you can use the raise statement, or the RAISE_APPLICATION_ERROR procedure. In MS SQL, a similar device exists in the form of the RAISEERROR procedure.

Again, the SQL standard also provides the syntax and the semantics in the form of the SIGNAL statement:


create procedure p_myproc(
p_id int unsigned
, p_name varchar(64)
)
begin
declare condition BUSINESS_RULE_VIOLATED;
-- do some processing
if not check_parameters(
p_id
, p_name
) then
SIGNAL BUSINESS_RULE_VIOLATED
SET MESSAGE_TEXT := 'Invalid parameter values'
;
end;
end;


Here, the SIGNAL statement tells the server to propagate the (user defined) condition with the name BUSINESS_RULE_VIOLATED to the calling environment. It also sets the value of the MESSAGE_TEXT field in the diagnostics area, allowing the caller to examine it's contents.

Passing the condition on to the outer blocks


Sometimes, a handler can't take care of all the actions that should be done to properly handle the error. For example, a procedure that obtained a lock with GET_LOCK should release it when it encounters some condition that makes further processing futile. But the procedure should generally not take the responsibility to perform a ROLLBACK, as the call to the current procedure might be a small part of a large transaction that was started long before the current procedure was hit. So, after cleaning up, the current procedure might want to convey the fact that it encountered a condition to it's caller so that has a chance to perform thier cleanup. What could be more convenient than to reuse the condition that was already captured by the handler?

The SQL standard also provides this in the form of the RESIGNAL statement. It can be used just as a plain statement, propagating the currently handled condition, without altering the diagnostics area:

begin
declare exit handler for not found
begin
release_lock('my_lock');
RESIGNAL;
end;
end;


It can also be used in the same manner as SIGNAL, giving a chance to alter the diagnostics area.

Final thoughts


I put up a few feature requests regarding the lack of these features (11660 and 11661). So far, they've barely been touched. Now, I don´t want to blame anyone for that. Maybe It's my own fault, and I didnt give em a snappy title.

However, I do sense that the need for proper, robust error handling is increasing. Now that the basic functionality of stored procedures and triggers has sunken in, and people are starting to make serious use of it, this error handling issue poses a real problem that people need to solve. And they are, that is, they're working around it, all in their own way. However, as long as there's no standard that everyone can use in a durable way, things like building big applications using stored procedures will be a big pain. Also, exchange of stored procedures might be hampered.

So, I did something I (as far as I can remember) never did before: I made out a new request, but labeled it immediately as bug, severity 2. I'm hoping this could put this thing on the agenda. Of course, I'm also hoping not to offend anyone. So, if you feel you need this functionality, please add a comment to the bug report. It might just help. Thank you.

Monday, February 13, 2006

Oracle XE License: I jumped the gun, my bad

Phew, it seems that the restrictions on the usage of Oracle XE I blogged about in my previous entry pertain to the BETA edition only.

Word is that the production release is scheduled for the end of februari. This will be accompanied with a license that will explicitly allow the usage in a production environment. Also, this license will do this for an unlimited amount of time.

I received the information regarding the licensing from Tom Kyte. Apart from being a genuine Oracle RDBMS Guru, he's also host to the Oracle XE Discussion forum and the author of the Ask Tom Oracle Magazine columns. In short - I'm very much inclined to trust this info.

Thanks again Tom for clearing it up. It puts me at ease for sure!

Oracle XE Beta 3 Released

I just noticed Oracle released the 3rd edition of the Oracle 10g Express edition, the 'free as in bear' rdbms (10 feb 2006). Just I was about to rejoice in the fact that Oracle seems to be continuing to release it's excellent product (I really think the Oracle RDBMS is a great product quality- and featurewise) I noticed a few interesting lines in the License:

BETA TRIAL LICENSE: Oracle Corporation ("Oracle") grants to you a no-charge trial license to use the pre-production beta version of the Oracle Database Express Edition software, documentation and product training (the "Software")...


Which is great, Thank you! It continues:

...provided to you by Oracle solely for evaluation purposes until March 31, 2006. Either party may terminate the license for the Software at any time. Upon termination, you shall cease using the Software...


Huh? Evaluation purposes? End of March?!...But that's just a little more than one month from now!

...You may not use the Software for any commercial or production purpose...[...]...You shall not: [....] (b) use the Software for commercial timesharing, rental, or service bureau use; ...


Well, this just tops it off. When they first released this product, I was led to believe you could deploy this on a server, and even embed it inside your own applications. It could've been my own wishful thinking, but I really wasn't aware all these strings were attached. I can't remember Tom Kyte's podcast on XE either. Maybe I'm allowed only as long as I don't charge money for it?

But what if the product I'm selling is not itself powered by a database? Suppose I'm selling a database development tool, and I've got all this documentation and samples based on an Oracle database. Am I allowed to ship my product on a commercial basis including OracleXE? After all, I'm not selling a rdbms here.

And what about a scenario like this: suppose I run a consultancy agency. I maintain a website where my customers can monitor progress on certain projects I'm conducting on their behalf. Naturally, I will charge my customers for the consultancy - that's my job. And as a service, they obtain the right to access my site to monitor the progress on their projects. Can or can I not power my website with OracleXE?

It's a big puzzle, and I'm a bit disappointed that I am going to have to solve it. Especially since I have no interest in making illegal use of the software. Damn.

Saturday, February 11, 2006

Webenabling OracleXE - You can, Free of cost

In my day job, I'm quite involved as an Oracle Developer. As such, I'm developing and maintaining a particular web-application for one of my clients. Right now, I'm trying to deploy some of the application to OracleXE for several reasons. I was experiencing a slight problem there, because my application uses the 'Oracle mod PL/SQL gateway', wich is essentially an apache module that provides direct access to stored procuders via HTTP requests. You gueassed it...this is not included in the XE product.

Luckily, it can be solved entirely, without much effort and without cost. Just read on, if you want to know how.

Oracle mod_plsql


The architecture of the application is quite straightforward:


  1. The client side front end is made up entirely by the Internet Explorer browser.

  2. From the clients, http requests are issued against a HTTP Server: the OracleHTTPServer. This is just an ordinary Apache webserver pimped up with a plugin (an Apache module) in order to have it handle particular HTTP requests by an Oracle Database. Ultimately, the apache configuration (location directives and the like) is responsible for routing only particular requests to the pls_handler wich is the internal name for the plugin (the module itself is called mod_plsql).

  3. The pls_handler calls a stored procedure that resides in an oracle database. Connectivity issues are all handled via the Apache configuration, and the last part of the HTTP request uri is interpreted as a procedurename. If the URI has a query string (not and SQL query - I mean the ?name1=value1&name2=value2... bit you see in internet adresses), these are mapped to procedure parameters. Inside stored procedures, you can call a particular procedure (htp.p) to pass plaintext to a buffer. When the outmost procedure ends, the plugin uses the buffer contents and uses that as the HTTP reponse.

  4. The pls_handler passes the HTTP response on to the webserver, which sends it to the client that issued the original HTTP request.


For some more info, check out this FAQ.

What about php, asp and jsp?


So, the data access layer is stored inside the database. Queries or other procedure calls all reside inside the database. This is different in the dynamic web-page solutions such as asp (classic asp used javascript or vbscript; nowadays its all .NET), jsp (java) and php.

With mod_plsql, there's only a thin component inside the webserver that routes requests to the database. mod_plsql knows nothing about the underlying database - it just tries to call the procedures requested via the URI. In a dynamic webpage solution, the page needs to have at least some knowledge about the database schema to be able to interact with the database. In a minimal scenario, such a solution needs to know the name of at least one stored procedure or table. In a worst case scenario, there are multiple pages that each contain a couple of SQL statements in the form of string.

The advantage of a solution like mod_plsql is that all references to the database schema are located inside the database. If something is modified in the database structure, all dependant objects, including the stored procedures that make up the web application, are marked invalid, wich means they require to be recompiled. So, you are immediately aware what parts of the application need to be modified in concert.

Of course, mod_plsql is not the solution to all web application problems - far from it. For example, suppose you need your webpage to generate a png image or a pdf document on the fly - this is not a job that's particularly suitable for a database procedure. But in cases were the application is data-oriented or were one needs to develop only a HTTP Service that returns only data (for example, in an XML format) mod_plsql is actually quite good.

There's of course another disadvantage concerning mod_plsql. It is entirely specific to Oracle. In the early days (I think it was about 2000), the webserver and mod_plsql component were shipped separately (and had to be paid for separately too). Later, the webserver was bundled with the database product, wich was Ok as long as you purchased the database.

OracleXE and mod_plsql


Recently, Oracle released it's express edition, OracleXE. This is a free ('...as in bear') edition, that is to say; it is free of cost even for commercial deployment. Alas, this version does not include mod_plsql. So, webapplications that are dependant upon mod_plsql cannot be ported to Oracle XE without having a separate installation of the Oracle HTTP server including mod_plsql.

Although finding out that XE didn't include this functionality wasn't a big surprise, it was a bit disapointing. For a couple of reasons, my client needs to duplicate it's environment - with the webenabling - and the particular Oracle license prevents that. That is, the client only has a single processor license which means we may not run (even install) that software on other machines simultaneously. Given the cost-drive, I thought XE would be a perfect match as far as database capabilities are concerned, but I really need the mod_plsql functionality as well.

As it turns out, there is a very good solution for all of this:


So, instead of having a installation of the 'Oracle HTTP Server', you just plug mod_owa into your apache server yourself - works great!

mod_owa: first impression


You can download mod_owa from Doug's site. Both source as well as binaries for a number of platforms are distributed there. The site forms the online documentation, and the distribution includes documentation as well. However, I think this documentation is not suitable for those that have never worked with mod_plsql before. In fact, I recommend those people to read the official documentation first and to keep a copy nearby.

One of the first differences between mod_plsql and mod_owa is the way configuration is handeld. Mod_plsl ships with a couple of webpages you can use to edit the configuration such as adding a DAD (Database Access Descriptor, wich is like a 'virtual directory' for wich requests should be handled by the pls_handler). In the Oracle HTTP Server version I use, the mod_plsql specific configuration is stored in a little text file that means somthing to mod_plsql only. Mod_owa is entirely configurable through the apache configuration; something I like better than the original (especially since I'm always hacking the OracleHTTPServer configuration directly in a text editor afterwards anyway).

Another difference between Mod_owa and mod_plsql is the fact that you can plug mod_owa into an Apache 2.0 server. mod_plsql runs on Apache 1.3. (BTW, This is availble for mod_owa too). Altough the documentation suggests there could be made some improvements on the mod_owa implementation for Apache 2.0, I tink it's cool Doug has done something Oracle didn't take on as of yet. (Something wich I find quite bad, I mean, how long has Apache 2 been around?)

There are a couple of differences between the mapping of request uri's to database stored procedure calls. Its nothing big, but it really helps if you're aware of the differences.

Both mod_plsql and mod_owa derive the procedure name from the resource name part of the request uri. In both cases, the apache configuration is used to route particular uri's paths to the module. The resource name is interpreted as the procedure name (this may include an owner and package if it is a packaged procedure). If the request URI included (http) parameters, these are usually simply mapped to proceudre parameters:


--
-- pleas call me on the browser like this:
--
-- http://www.host.com/pls/dad/p_test?p_text=hello%20world
--
create or replace procedure p_test(
p_text varchar2
)
is
begin
htp.p(p_text);
end;


So here, there's a rather tight mapping between the URI query name - value pairs and the stored procedure parmeter name and value.
This works the same in both mod_plsql and mod_owa.

You can also use 'flexible parameter passing'. In this scheme, the procedure must be declared to accept PLSQL arrays (actually, it's called a PLSQL Table, wich is a integer indexed associative array). Flexible parameter passing is controlled from the HTTP request uri by prefixing the resource name with an exclamation mark (!).

Both mod_owa and mod_plsql support the so-called 4-parameter interface:


--
-- pleas call me on the browser like this:
--
-- http://www.host.com/pls/dad/!p_test?name1=value1&name2=value2...etc.
--
create or replace procedure p_test(
num_entries IN NUMBER
, name_array IN owa_util.vc_arr
, value_array IN owa_util.vc_arr
, reserved IN owa_util.vc_arr
);


Inside the procedure, the name_array and value_array arrays can be read to get to the actual values passed at the url. Each position present in either of the arrays corresponds to the same position in the ohter array, so for each position in either of the array's the original name/value pair can be reconstructed.

An interesting point is that mod_plsql is very picky about both the parameter names (must be exactly num_entries, name_array ..etc). Mod_owa is more lenient, and just tries to call the procedure derived from the URI. This could a potential security hazard id you're just blindly porting your code: mod_owa will allow any procedure that matches the proceure name and datatypes to be called, wherea mod_plsql needs to match all the names exactly.

Another difference is that mod_plsql also supports the 2-parameter interface. This is exactly like the 4-parameter interface, but without the first and last element in the parameterlist:


--
-- pleas call me on the browser like this:
--
-- http://www.host.com/pls/dad/!p_test?name1=value1&name2=value2...etc.
--
create or replace procedure p_test(
num_entries IN number
, name_array IN owa_util.vc_arr
, value_array IN owa_util.vc_arr
, reserved IN owa_util.vc_arr
);


So, because owa does not use this, I simple overloaded my plsql:calls:


--
-- pleas call me on the browser like this:
--
create or replace procedure p_test(
name_array IN owa_util.vc_arr
, value_array IN owa_util.vc_arr
);

create or replace procedure p_test(
num_entries IN NUMBER
, name_array IN owa_util.vc_arr
, value_array IN owa_util.vc_arr
, reserved IN owa_util.vc_arr
)
is begin
p_test(
name_array
, value_array
)
end;


Apart from all the stuff i mentioned here: mod_owa has some extra functionality still as compared to mod_plsql, such as caching etc.

Anyway, thanks Doug for this terrific contribution. I'm looking forward to show this solution to my customers.

Tuesday, February 07, 2006

Some things I am doing with javascript right now.

Lately, I'm developing a bunch of (D)HTML Widgets. Basically, it's just fun, there's only a slightly serious undertone. Although I (as well as so many others) have developed lots and lots of this stuff already, I've set a few goals. Theyre modest, I gueass, but enough to make it interesting (to me, that is)


  • The associated javascript code should not easily clash with other javascripts obtained from other sources

  • Javascript and widgets should be reasonably thin and perform reasonable fast



Avoiding namespace clashes


Well, first one's easy when you take it into account in advance. The trick I'm using goes like this:


//create the root of my little framework
var org = new Object();

//fab is the project codename;
//you may think of it as FABricated
//or FABulous. You can also think of it as
//Fabulous Ajax Basis, or Framework for Ajax Builds.
org.fab = new Object();

//util will be the namespace for utility objects
org.fab.util = new Object();

//reflect will be the namespace for javascript metadata/reflection/introspection
org.fab.reflect = new Object();

//ui will be the namespace for
//user-interface related stuff, e.g. widgets
org.fab.ui = new Object();



Well, so far, nothing interesting, just a few objects doing nothing. Now, I'm hooking up my javascript functions and Constructors into these:


//some of the stuff in the org.fab.reflect namespace:

org.fab.reflect.isObject = function (
v
){
if (org.fab.reflect.isNull(v)){
return false;
}
return typeof(v)=="object";
}

org.fab.reflect.isArray = function (
obj
){
if (org.fab.reflect.isObject(obj)){
return org.fab.reflect.getFunctionName(
obj.constructor
)=="Array"
} else {
return false;
}
}

//Collection constructor
org.fab.util.Collection = function (
array
){
if (org.fab.reflect.isArray(array)){
this.items = array;
} else {
this.items = new Array();
}
this.currentItem = -1;
return this;
}


And now we have a nice, extensible system of categories to structure all javascript code.

(Actually, I just found out that ActiveWidgets use a similar technique to achieve this kind of categorization. LOL!)

Of course, this does not totally prevent our code from clashing with someone else's, but it will avoid most of the trouble most of the time. When you need to do a lot with just one particular branch of the framework, you can always use the javascript with scope modifier:


var v;
with (org.fab.reflect){
if (isObject(v)){
//yadda yadda yadda
} else if (isArray(v)) {
//yadda yadda yadda
}
}


Performance


I see a lot of object oriented javascript examples that do stuff like this:


function MyConstructor(){
//a normal property
this.property = null;

//a 'meant to be' static property
this.staticProperty = "Please do not change this";

return this;
}


The intention is to create a constructor for objects. The objects will have two properties: property and staticProperty. If course staticProperty was meant to be static, but it isnt. Javascript does not know true static objects or variables like you have in C/C++ or java.

However, this can be improved quite a lot by using the builtin prototype property of the constructor:


function MyConstructor(){
//a normal property
this.property = null;
return this;
}

//a static property
MyConstructor.prototype.staticProperty = "Please do not change this";


Apart from the fact that this resembles a static propery much better than the previous example, this implementation will also cost less memory. There is really only one instance of MyConstructor.prototype.staticProperty; in the previous case, the property would be created a new for each new call to the constructor.

The prototype is also very useful to adorn our Class (or actually, it's only a a constructor in javascrispeak) with methods:


MyConstructor.prototype.method = function(
arg
){
alert(arg + ": " + this.property);
}


This is not so different from the static property. Instead of assiging a function reference to this, we assign it to the prototype. The fun is that you can still invoke the method in an entirely natural way:


var m = new MyConstructor();
m.method("prompt: ");


So, even though we created the method as a member of the prototype property of MyConstructor, we can call it without using the full: MyConstructor.prototype.method("prompt: "). In fact, that would not've worked even if we wanted. That's because our method references this. When called as MyConstructor.prototype.method("prompt: ") the this 'pointer' would reference the MyConstructor.prototye; and presumably, this object does not have a property named property

If course, the MyConstructor.prototype.method() is useful when creating static functions. Then, this syntax is the only right and consistent one.

Surely, this is an efficient way of adorning constructors with methods, at least more so than:


function MyConstructor(){
//a normal property
this.property = null;

//a method
this.method = function(arg){
alert(arg + ": " + this.property);
}
return this;
}



In my opinion, it also beats this syntax:


function MyConstructor(){
//a normal property
this.property = null;

//a method
this.method = new Function(
'arg'
, 'alert(arg + ": " + this.property);'
);
return this;
}



next time: how to get Mozilla to render a HTML table anew after programmatically changing the HTML table's layout.

DuckDB bag of tricks: Processing PGN chess games with DuckDB - Rolling up each game's lines into a single game row (6/6)

DuckDB bag of tricks is the banner I use on this blog to post my tips and tricks about DuckDB . This post is the sixth installment of a s...