AlaSQL / alasql

AlaSQL.js - JavaScript SQL database for browser and Node.js. Handles both traditional relational tables and nested JSON data (NoSQL). Export, store, and import data from localStorage, IndexedDB, or Excel.
http://alasql.org
MIT License
7.04k stars 659 forks source link

Add numbers to errors #154

Open agershun opened 9 years ago

agershun commented 9 years ago

To make error catching easy it is reasonable to number errors, like:

00001: Table 'addresses' doesn't exist.
00002: Database 'MyAtlase' doesn't exist.
etc.

So, people can distinguish errors by their numbers rather than text description:

alasql.options.errorlog = true;
alasql('SELECT * FROM addresses',[],function(res,err){
  if(err.substr(0,5) == '00001') console.log("La table n'existe pas");
  done();
});
agershun commented 9 years ago

I found some guidelines for different databases:

What version to choose? IMHO SQLite is preferable...

mathiasrw commented 9 years ago

The SQLite looks good - and suits our needs - but if we ever make a module setup where you can select your flavour AlaSQL could have an internal error number that it converts to the flavor chosen...

agershun commented 9 years ago

:+1: Great idea! Agree, we can start with SQLite and make mapping to other SQLs error systems.

For example for UNIQUE constraint:

// Definition
var errcodes = {};

// Error function
function erorr(errid) {
   if(errcodes[errid]) errid = errcodes[errid];
   throw new Error(errid)
}

// Somewhere in SQLite module
// This will work only after 'REQUIRE SQLITE' command
errcode['CONSTRAINT_UNIQUE'] = 'UNIQUE constraint failed'

//Usage
error('CONSTRAINT_UNIQUE');
mathiasrw commented 9 years ago

Looked into the SQLite one. its quite simple, but we might end up in too many 21's

SQLITE_MISUSE 21 /* Library used incorrectly */

MsSQL and Oracle have way too many special cases to start with them. The postgress looks good: http://www.postgresql.org/docs/8.2/static/errcodes-appendix.html

mathiasrw commented 8 years ago

I had a feeling it was a bit crazy to try to map all the (very good) error descriptions in the source to the generic ones in the other frameworks.

I feel the objective is to minimise the code, so saw no reason not to keep a system that makes sense to the alasql setup.

I have made it so when developing with alasql.js the full errors are in the source, while only the error code is in the .min.js file.

Example:

In this alasql.js you can get an error as

You can remove files only in Node.js and Apache Cordova

now the .min.js version only contains the string

e01501

as error description.

Error codes implemented so far: https://github.com/agershun/alasql/wiki/Error-codes

agershun commented 8 years ago

@mathiasrw hmm ... People need error codes not for minimizing the library size, but for automatic processing in case of problems. E.g. AlaSQL returns the error "Cannot open the file", so the main program can generate this file itself, because it understand that this is the particular erro. An it try to recognize the problem by the code, not by text description.

So, I think we need:

1) create a special error(code, description, args) function with two parameters:

2) This function can be rewritten by user in a standard manner, like:

alasql.error = function(code,description,args) {.
  if(code === 123 && args.databaseid == "MyBase") {
   // fix something with database MyBase
   return;  // error was trapped and processed
  }
  alasql.defaulterror(code, description, args); 
}

3) By default it generates new Error() message:

alasql.defaulterror = function() {
    new Error(code+' '+description);
}

In more complex case, we can create a special error descriptions table:

alasql.errorcodes = {
   123: 'Database $databaseid can not be dropped',
};

alasql.defaulterror = function(code, args) {
    new Error(code+' '+template(errorcodes.description,args));
}

And rase if with:

   error(123,{databaseid:'MyBase'});

So, it is not good to have different behavior between alasql.js and alasql.min.js.

agershun commented 8 years ago

The smallest code for temlate() function (from here):

(function(){
var cache = {};
this.template = function(str,data) {  
  var fn = cache[str] = cache[str] || new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +       
        "with(obj){p.push('" +
        str
          .replace(/[\r\t\n]/g, " ")
          .split("{{").join("\t")
          .replace(/((^|}})[^\t]*)'/g, "$1\r")
          .replace(/\t(.*?)}}/g, "',$1,'")
          .split("\t").join("');")
          .split("}}").join("p.push('")
          .split("\r").join("\\'")
      + "');}return p.join('');");
      return fn(data);
  }
})();
mathiasrw commented 8 years ago

ok ok ok... good point...

agershun commented 8 years ago

Sorry and thank you for code reverting! Together we will do it better :)

Let's define interfaces:

alasql.errorhandler = function(code, args) {...} // by default refers to alasql.defaulterrorhandler()
alasql.defaulterrorhandler = function(code, args) {
    throws new Error(code+':'+template(alasql.errorcodes,args));
};
alasql.errorcodes = {
    <code-int> : <template-string>
}
alasql.stdfn.template = function(pattern, obj) {
   // replace all {{name}} or $name to object properties
};

BTW:Bonus - we can use this template function in SQL queries :)

What do you think about this approach?

mathiasrw commented 8 years ago

Looks good but must say the function(code,description,args) version makes more sense to me. is the idea to put the discription in the args?

agershun commented 8 years ago

The idea here is to have separate table for all error template messages. The args object is used only for parameters of error.

agershun commented 8 years ago

Off-topic: what syntax for TEMPLATE(str,obj) function is better?

option 1 - {{prop}}
option 2 - <%prop%>

or use options:

alasql.options.templright = '<$';
alasql.options.templleft = '$>';

We will use the same function for such queries like:

var tmpl = '{{a}} rows included in {{b}}';
var data = [{a:10,b:'one.txt'}, {a:20,b:'two.txt'}];
alasql('SELECT TEMPL(?,_) FROM ?', [tmpl, data]);
mathiasrw commented 8 years ago

Personally I really prefer the {{...}} syntax.

I have a feeling that setting it as an option creates a complexity with no real benefit.


How come you have a _ as second parameter?

agershun commented 8 years ago
  1. Let it be so, {{ }} by default.
  2. _ is similar to *, but in case of SELECT * FROM ? AlaSQL copies all columns into the new records, but with SELECT _ FROM ? (or simply SELECT FROM ?) AlaSQL uses the same object.

This is the example:

var data = [{a:1}];
var res = alasql('SELECT _ AS q, _->a AS w FROM ?',[data]);
// [{q:{a:1}, w:1}]
mathiasrw commented 8 years ago

:+1: