From 91a579d87e6ffebdba7d258ed7a6657e0127f8fd Mon Sep 17 00:00:00 2001 From: Alain Prouté Date: Mon, 12 Dec 2005 11:44:10 +0000 Subject: [PATCH] *** empty log message *** --- anubis_dev/compiler/src/lex.yy.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------ anubis_dev/compiler/src/lexer.y | 5 +++++ anubis_dev/compiler/src/predef.aux | 36 +++++++++++++++++++----------------- anubis_dev/compiler/src/show.c | 3 ++- anubis_dev/library/examples/paint.anubis | 4 ++-- anubis_dev/library/examples/runge_kutta.anubis | 17 +++++++++++++---- anubis_dev/library/network/dns.anubis | 3 +++ anubis_dev/library/predefined.anubis | 8 +++++--- anubis_dev/library/tools/sdbms.anubis | 978 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ anubis_dev/library/tools/sdbms1.anubis | 1599 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ anubis_dev/library/tools/sdbms2.anubis | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------ 11 files changed, 2192 insertions(+), 1023 deletions(-) create mode 100644 anubis_dev/library/tools/sdbms1.anubis diff --git a/anubis_dev/compiler/src/lex.yy.c b/anubis_dev/compiler/src/lex.yy.c index 0e951ab..c4097a5 100644 --- a/anubis_dev/compiler/src/lex.yy.c +++ b/anubis_dev/compiler/src/lex.yy.c @@ -971,6 +971,7 @@ char *path_prefix(char *name) int i = strlen(name); if (i >= path_prefix_length-5) { + err_line_col(linecol()); fprintf(errfile, msgtext_file_path_too_long[language], name); @@ -1028,6 +1029,7 @@ FILE *fopensrc(char *name) /* if path is absolute do not try the libraries */ if (name[0] == '/' || name[0] == '\\') { + err_line_col(linecol()); fprintf(errfile, msgtext_cannot_find_src_file[language], name, @@ -1066,6 +1068,7 @@ FILE *fopensrc(char *name) } /* The file is not found: send a message and exit */ + err_line_col(linecol()); fprintf(errfile, msgtext_cannot_find_src_file[language], name, @@ -1172,6 +1175,7 @@ void add_to_already_included(char *name) //printf("Adding '%s'.\n",name); if (next_already_included == max_already_included) { + err_line_col(linecol()); fprintf(errfile, msgtext_too_many_files[language]); anb_exit(1); @@ -1390,7 +1394,7 @@ static char *rec_name(char *s, int del) /* state STRTL is used when a string is too long. */ /* states CONF and CONFCOM are used to read 'compiler.conf'.*/ /* the lexer ----------------------------------------------------------------------------*/ -#line 1394 "lex.yy.c" +#line 1398 "lex.yy.c" /* Macros after this point can all be overridden by user definitions in * section 1. @@ -1555,9 +1559,9 @@ YY_DECL register char *yy_cp, *yy_bp; register int yy_act; -#line 562 "lexer.y" +#line 566 "lexer.y" -#line 1561 "lex.yy.c" +#line 1565 "lex.yy.c" if ( yy_init ) { @@ -1643,27 +1647,27 @@ do_action: /* This label is used only to access EOF actions. */ case 1: YY_RULE_SETUP -#line 563 "lexer.y" +#line 567 "lexer.y" { comlevel = 1; BEGIN COM; } YY_BREAK case 2: YY_RULE_SETUP -#line 564 "lexer.y" +#line 568 "lexer.y" { comlevel++; } YY_BREAK case 3: YY_RULE_SETUP -#line 565 "lexer.y" +#line 569 "lexer.y" { comlevel--; if (!comlevel) { BEGIN PAR; } } YY_BREAK case 4: YY_RULE_SETUP -#line 566 "lexer.y" +#line 570 "lexer.y" { } YY_BREAK case 5: YY_RULE_SETUP -#line 567 "lexer.y" +#line 571 "lexer.y" { } YY_BREAK case 6: @@ -1671,17 +1675,17 @@ case 6: yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 568 "lexer.y" +#line 572 "lexer.y" { /* dbl_slash_comment(yytext+2); */ } YY_BREAK case 7: YY_RULE_SETUP -#line 569 "lexer.y" +#line 573 "lexer.y" { BEGIN STR; str_index = 0; } YY_BREAK case 8: YY_RULE_SETUP -#line 570 "lexer.y" +#line 574 "lexer.y" { BEGIN PAR; str_buf[str_index] = 0; yylval.expr = new_string(str_buf); @@ -1689,598 +1693,598 @@ YY_RULE_SETUP YY_BREAK case 9: YY_RULE_SETUP -#line 574 "lexer.y" +#line 578 "lexer.y" { store_str_char('\"'); } YY_BREAK case 10: YY_RULE_SETUP -#line 575 "lexer.y" +#line 579 "lexer.y" { store_str_char('\n'); } YY_BREAK case 11: YY_RULE_SETUP -#line 576 "lexer.y" +#line 580 "lexer.y" { store_str_char('\r'); } YY_BREAK case 12: YY_RULE_SETUP -#line 577 "lexer.y" +#line 581 "lexer.y" { store_str_char('\t'); } YY_BREAK case 13: YY_RULE_SETUP -#line 578 "lexer.y" +#line 582 "lexer.y" { store_str_char('\\'); } YY_BREAK case 14: YY_RULE_SETUP -#line 579 "lexer.y" +#line 583 "lexer.y" { store_str_char('\n'); } YY_BREAK case 15: YY_RULE_SETUP -#line 580 "lexer.y" +#line 584 "lexer.y" { store_str_char(yytext[0]); } YY_BREAK case 16: YY_RULE_SETUP -#line 581 "lexer.y" +#line 585 "lexer.y" { BEGIN PAR; } YY_BREAK case 17: YY_RULE_SETUP -#line 582 "lexer.y" +#line 586 "lexer.y" { } YY_BREAK case 18: YY_RULE_SETUP -#line 583 "lexer.y" +#line 587 "lexer.y" { } YY_BREAK case 19: YY_RULE_SETUP -#line 584 "lexer.y" +#line 588 "lexer.y" { yylval.integer = (int)(unsigned char)yytext[1]; return yy__char; } YY_BREAK case 20: YY_RULE_SETUP -#line 585 "lexer.y" +#line 589 "lexer.y" { yylval.integer = (int)'\n'; return yy__char; } YY_BREAK case 21: YY_RULE_SETUP -#line 586 "lexer.y" +#line 590 "lexer.y" { yylval.integer = (int)'\r'; return yy__char; } YY_BREAK case 22: YY_RULE_SETUP -#line 587 "lexer.y" +#line 591 "lexer.y" { yylval.integer = (int)'\t'; return yy__char; } YY_BREAK case 23: YY_RULE_SETUP -#line 588 "lexer.y" +#line 592 "lexer.y" { yylval.integer = (int)'\"'; return yy__char; } YY_BREAK case 24: YY_RULE_SETUP -#line 589 "lexer.y" +#line 593 "lexer.y" { yylval.integer = (int)'\\'; return yy__char; } YY_BREAK case 25: YY_RULE_SETUP -#line 590 "lexer.y" +#line 594 "lexer.y" { yylval.integer = (int)'\''; return yy__char; } YY_BREAK case 26: YY_RULE_SETUP -#line 591 "lexer.y" +#line 595 "lexer.y" { yylval.expr = linecol(); return yy__lpar; } YY_BREAK case 27: YY_RULE_SETUP -#line 592 "lexer.y" +#line 596 "lexer.y" { yylval.expr = linecol(); return yy__rpar; } YY_BREAK case 28: YY_RULE_SETUP -#line 593 "lexer.y" +#line 597 "lexer.y" { yylval.expr = linecol(); return yy__rpar; } YY_BREAK case 29: YY_RULE_SETUP -#line 594 "lexer.y" +#line 598 "lexer.y" { yylval.expr = linecol(); return yy__lbrace; } YY_BREAK case 30: YY_RULE_SETUP -#line 595 "lexer.y" +#line 599 "lexer.y" { yylval.expr = linecol(); return yy__rbrace; } YY_BREAK case 31: YY_RULE_SETUP -#line 596 "lexer.y" +#line 600 "lexer.y" { yylval.expr = linecol(); return yy__rbrace; } YY_BREAK case 32: YY_RULE_SETUP -#line 597 "lexer.y" +#line 601 "lexer.y" { yylval.expr = linecol(); return yy__lbracket; } YY_BREAK case 33: YY_RULE_SETUP -#line 598 "lexer.y" +#line 602 "lexer.y" { yylval.expr = linecol(); return yy__rbracket; } YY_BREAK case 34: YY_RULE_SETUP -#line 599 "lexer.y" +#line 603 "lexer.y" { yylval.expr = linecol(); return yy__rbracket; } YY_BREAK case 35: YY_RULE_SETUP -#line 600 "lexer.y" +#line 604 "lexer.y" { yylval.expr = linecol(); return yy__colon; } YY_BREAK case 36: YY_RULE_SETUP -#line 601 "lexer.y" +#line 605 "lexer.y" { yylval.expr = linecol(); return yy__semicolon; } YY_BREAK case 37: YY_RULE_SETUP -#line 602 "lexer.y" +#line 606 "lexer.y" { yylval.expr = linecol(); return yy__minus; } YY_BREAK case 38: YY_RULE_SETUP -#line 603 "lexer.y" +#line 607 "lexer.y" { yylval.expr = linecol(); return yy__dot; } YY_BREAK case 39: YY_RULE_SETUP -#line 604 "lexer.y" +#line 608 "lexer.y" { yylval.expr = linecol(); return yy__percent; } YY_BREAK case 40: YY_RULE_SETUP -#line 605 "lexer.y" +#line 609 "lexer.y" { yylval.expr = linecol(); return yy__comma; } YY_BREAK case 41: YY_RULE_SETUP -#line 606 "lexer.y" +#line 610 "lexer.y" { yylval.expr = linecol(); return yy__tilde; } YY_BREAK case 42: YY_RULE_SETUP -#line 607 "lexer.y" +#line 611 "lexer.y" { yylval.expr = linecol(); return yy__equals; } YY_BREAK case 43: YY_RULE_SETUP -#line 608 "lexer.y" +#line 612 "lexer.y" { yylval.expr = linecol(); return yy__non_equal; } YY_BREAK case 44: YY_RULE_SETUP -#line 609 "lexer.y" +#line 613 "lexer.y" { yylval.expr = linecol(); return yy__plus; } YY_BREAK case 45: YY_RULE_SETUP -#line 610 "lexer.y" +#line 614 "lexer.y" { yylval.expr = linecol(); return yy__star; } YY_BREAK case 46: YY_RULE_SETUP -#line 611 "lexer.y" +#line 615 "lexer.y" { yylval.expr = linecol(); return yy__carret; } YY_BREAK case 47: YY_RULE_SETUP -#line 612 "lexer.y" +#line 616 "lexer.y" { yylval.expr = linecol(); return yy__ampersand; } YY_BREAK case 48: YY_RULE_SETUP -#line 613 "lexer.y" +#line 617 "lexer.y" { yylval.expr = linecol(); return yy__vbar; } YY_BREAK case 49: YY_RULE_SETUP -#line 614 "lexer.y" +#line 618 "lexer.y" { yylval.expr = linecol(); return yy__right_shift; } YY_BREAK case 50: YY_RULE_SETUP -#line 615 "lexer.y" +#line 619 "lexer.y" { yylval.expr = linecol(); return yy__left_shift; } YY_BREAK case 51: YY_RULE_SETUP -#line 616 "lexer.y" +#line 620 "lexer.y" { yylval.expr = linecol(); return yy__implies; } YY_BREAK case 52: YY_RULE_SETUP -#line 617 "lexer.y" +#line 621 "lexer.y" { yylval.expr = linecol(); return yy__forall; } YY_BREAK case 53: YY_RULE_SETUP -#line 618 "lexer.y" +#line 622 "lexer.y" { yylval.expr = linecol(); return yy__exists; } YY_BREAK case 54: YY_RULE_SETUP -#line 619 "lexer.y" +#line 623 "lexer.y" { yylval.expr = linecol(); return yy__exists_unique; } YY_BREAK case 55: YY_RULE_SETUP -#line 620 "lexer.y" +#line 624 "lexer.y" { yylval.expr = linecol(); return yy__description; } YY_BREAK case 56: YY_RULE_SETUP -#line 621 "lexer.y" +#line 625 "lexer.y" { yylval.expr = linecol(); return yy__slash; } YY_BREAK case 57: YY_RULE_SETUP -#line 622 "lexer.y" +#line 626 "lexer.y" { yylval.expr = linecol(); return yy__mod; } YY_BREAK case 58: YY_RULE_SETUP -#line 623 "lexer.y" +#line 627 "lexer.y" { yylval.expr = linecol(); return yy__less; } YY_BREAK case 59: YY_RULE_SETUP -#line 624 "lexer.y" +#line 628 "lexer.y" { yylval.expr = linecol(); return yy__greater; } YY_BREAK case 60: YY_RULE_SETUP -#line 625 "lexer.y" +#line 629 "lexer.y" { yylval.expr = linecol(); return yy__lessoreq; } YY_BREAK case 61: YY_RULE_SETUP -#line 626 "lexer.y" +#line 630 "lexer.y" { yylval.expr = linecol(); return yy__greateroreq; } YY_BREAK case 62: YY_RULE_SETUP -#line 627 "lexer.y" +#line 631 "lexer.y" { yylval.expr = linecol(); return yy__write; } YY_BREAK case 63: YY_RULE_SETUP -#line 628 "lexer.y" +#line 632 "lexer.y" { yylval.expr = linecol(); return yy__exchange; } YY_BREAK case 64: YY_RULE_SETUP -#line 629 "lexer.y" +#line 633 "lexer.y" { yylval.expr = linecol(); return yy__arrow; } YY_BREAK case 65: YY_RULE_SETUP -#line 630 "lexer.y" +#line 634 "lexer.y" { yylval.expr = linecol(); return yy__arroww; } YY_BREAK case 66: YY_RULE_SETUP -#line 631 "lexer.y" +#line 635 "lexer.y" { yylval.expr = linecol(); return yy__mapsto; } YY_BREAK case 67: YY_RULE_SETUP -#line 632 "lexer.y" +#line 636 "lexer.y" { yylval.expr = linecol(); return yy__mapstoo; } YY_BREAK case 68: YY_RULE_SETUP -#line 633 "lexer.y" +#line 637 "lexer.y" { yylval.expr = linecol(); return yy__dots; } YY_BREAK case 69: YY_RULE_SETUP -#line 634 "lexer.y" +#line 638 "lexer.y" { yylval.expr = linecol(); return yy__rpar_arrow; } YY_BREAK case 70: YY_RULE_SETUP -#line 635 "lexer.y" +#line 639 "lexer.y" { yylval.expr = linecol(); return yy__type_String; } YY_BREAK case 71: YY_RULE_SETUP -#line 636 "lexer.y" +#line 640 "lexer.y" { yylval.expr = linecol(); return yy__type_ByteArray; } YY_BREAK case 72: YY_RULE_SETUP -#line 637 "lexer.y" +#line 641 "lexer.y" { yylval.expr = linecol(); return yy__type_Int32; } YY_BREAK case 73: YY_RULE_SETUP -#line 638 "lexer.y" +#line 642 "lexer.y" { yylval.expr = linecol(); return yy__type_Omega; } YY_BREAK case 74: YY_RULE_SETUP -#line 639 "lexer.y" +#line 643 "lexer.y" { yylval.expr = linecol(); return yy__bit_width; } YY_BREAK case 75: YY_RULE_SETUP -#line 640 "lexer.y" +#line 644 "lexer.y" { yylval.expr = linecol(); return yy__indirect; } YY_BREAK case 76: YY_RULE_SETUP -#line 641 "lexer.y" +#line 645 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__vcopy; } else lexical_error(); } YY_BREAK case 77: YY_RULE_SETUP -#line 644 "lexer.y" +#line 648 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__load_module; } else lexical_error(); } YY_BREAK case 78: YY_RULE_SETUP -#line 647 "lexer.y" +#line 651 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__serialize; } else lexical_error(); } YY_BREAK case 79: YY_RULE_SETUP -#line 650 "lexer.y" +#line 654 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__unserialize; } else lexical_error(); } YY_BREAK case 80: YY_RULE_SETUP -#line 653 "lexer.y" +#line 657 "lexer.y" { yylval.expr = linecol(); return yy__type_Float; } YY_BREAK case 81: YY_RULE_SETUP -#line 654 "lexer.y" +#line 658 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__type_Listener; } else lexical_error(); } YY_BREAK case 82: YY_RULE_SETUP -#line 657 "lexer.y" +#line 661 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__StructPtr; } else lexical_error(); } YY_BREAK case 83: YY_RULE_SETUP -#line 660 "lexer.y" +#line 664 "lexer.y" { if (reading_predef) { yylval.expr = linecol(); return yy__avm; } else lexical_error(); } YY_BREAK case 84: YY_RULE_SETUP -#line 663 "lexer.y" +#line 667 "lexer.y" { yylval.expr = linecol(); return yy__if; } YY_BREAK case 85: YY_RULE_SETUP -#line 664 "lexer.y" +#line 668 "lexer.y" { yylval.expr = linecol(); return yy__proof; } YY_BREAK case 86: YY_RULE_SETUP -#line 665 "lexer.y" +#line 669 "lexer.y" { yylval.expr = linecol(); return yy__type_Proof; } YY_BREAK case 87: YY_RULE_SETUP -#line 666 "lexer.y" +#line 670 "lexer.y" { yylval.expr = linecol(); return yy__since; } YY_BREAK case 88: YY_RULE_SETUP -#line 667 "lexer.y" +#line 671 "lexer.y" { return yy__is; } YY_BREAK case 89: YY_RULE_SETUP -#line 668 "lexer.y" +#line 672 "lexer.y" { yylval.expr = linecol(); return yy__then; } YY_BREAK case 90: YY_RULE_SETUP -#line 669 "lexer.y" +#line 673 "lexer.y" { yylval.expr = linecol(); return yy__alert; } YY_BREAK case 91: YY_RULE_SETUP -#line 670 "lexer.y" +#line 674 "lexer.y" { yylval.expr = linecol(); return yy__protect; } YY_BREAK case 92: YY_RULE_SETUP -#line 671 "lexer.y" +#line 675 "lexer.y" { yylval.expr = linecol(); return yy__lock; } YY_BREAK case 93: YY_RULE_SETUP -#line 672 "lexer.y" +#line 676 "lexer.y" { yylval.expr = linecol(); return yy__succeeds; } YY_BREAK case 94: YY_RULE_SETUP -#line 673 "lexer.y" +#line 677 "lexer.y" { yylval.expr = linecol(); return yy__succeeds_as; } YY_BREAK case 95: YY_RULE_SETUP -#line 674 "lexer.y" +#line 678 "lexer.y" { yylval.expr = linecol(); return yy__wait_for; } YY_BREAK case 96: YY_RULE_SETUP -#line 675 "lexer.y" +#line 679 "lexer.y" { yylval.expr = linecol(); return yy__checking_every; } YY_BREAK case 97: YY_RULE_SETUP -#line 676 "lexer.y" +#line 680 "lexer.y" { yylval.expr = linecol(); return yy__delegate; } YY_BREAK case 98: YY_RULE_SETUP -#line 677 "lexer.y" +#line 681 "lexer.y" { yylval.expr = linecol(); return yy__else; } YY_BREAK case 99: YY_RULE_SETUP -#line 678 "lexer.y" +#line 682 "lexer.y" { yylval.expr = linecol(); return yy__with; } YY_BREAK case 100: YY_RULE_SETUP -#line 679 "lexer.y" +#line 683 "lexer.y" { yylval.expr = linecol(); return yy__alt_number; } YY_BREAK case 101: YY_RULE_SETUP -#line 680 "lexer.y" +#line 684 "lexer.y" { yylval.expr = linecol(); return yy__RAddr; } YY_BREAK case 102: YY_RULE_SETUP -#line 681 "lexer.y" +#line 685 "lexer.y" { yylval.expr = linecol(); return yy__WAddr; } YY_BREAK case 103: YY_RULE_SETUP -#line 682 "lexer.y" +#line 686 "lexer.y" { yylval.expr = linecol(); return yy__RWAddr; } YY_BREAK case 104: YY_RULE_SETUP -#line 683 "lexer.y" +#line 687 "lexer.y" { yylval.expr = linecol(); return yy__GAddr; } YY_BREAK case 105: YY_RULE_SETUP -#line 684 "lexer.y" +#line 688 "lexer.y" { yylval.expr = linecol(); return yy__Var; } YY_BREAK case 106: YY_RULE_SETUP -#line 685 "lexer.y" +#line 689 "lexer.y" { yylval.expr = linecol(); return yy__MVar; } YY_BREAK case 107: YY_RULE_SETUP -#line 686 "lexer.y" +#line 690 "lexer.y" { yylval.expr = linecol(); return yy__connect_to_file; } YY_BREAK case 108: YY_RULE_SETUP -#line 687 "lexer.y" +#line 691 "lexer.y" { yylval.expr = linecol(); return yy__connect_to_IP; } YY_BREAK case 109: YY_RULE_SETUP -#line 688 "lexer.y" +#line 692 "lexer.y" { yylval.expr = linecol(); return yy__debug_avm; } YY_BREAK case 110: YY_RULE_SETUP -#line 689 "lexer.y" +#line 693 "lexer.y" { yylval.expr = linecol(); return yy__terminal; } YY_BREAK case 111: YY_RULE_SETUP -#line 690 "lexer.y" +#line 694 "lexer.y" { yylval.expr = linecol(); return yy__we_have; } YY_BREAK case 112: YY_RULE_SETUP -#line 691 "lexer.y" +#line 695 "lexer.y" { yylval.expr = linecol(); return yy__enough; } YY_BREAK case 113: YY_RULE_SETUP -#line 693 "lexer.y" +#line 697 "lexer.y" { yylval.expr = linecol(); return yy__let; } YY_BREAK case 114: YY_RULE_SETUP -#line 694 "lexer.y" +#line 698 "lexer.y" { yylval.expr = linecol(); return yy__assume; } YY_BREAK case 115: YY_RULE_SETUP -#line 695 "lexer.y" +#line 699 "lexer.y" { yylval.expr = linecol(); return yy__hence; } YY_BREAK case 116: YY_RULE_SETUP -#line 696 "lexer.y" +#line 700 "lexer.y" { yylval.expr = linecol(); return yy__indeed; } YY_BREAK case 117: YY_RULE_SETUP -#line 697 "lexer.y" +#line 701 "lexer.y" { BEGIN CONF; return yy__stop_after; } YY_BREAK case 118: YY_RULE_SETUP -#line 698 "lexer.y" +#line 702 "lexer.y" { BEGIN CONF; return yy__djed; } YY_BREAK case 119: YY_RULE_SETUP -#line 699 "lexer.y" +#line 703 "lexer.y" { return yy__verbose; } YY_BREAK case 120: YY_RULE_SETUP -#line 700 "lexer.y" +#line 704 "lexer.y" { BEGIN CONF; return yy__language; } YY_BREAK case 121: YY_RULE_SETUP -#line 701 "lexer.y" +#line 705 "lexer.y" { fprintf(errfile,msgtext_syntax_error_in_conf_file[language], my_anubis_directory,lineno); anb_exit(1); } YY_BREAK case 122: YY_RULE_SETUP -#line 703 "lexer.y" +#line 707 "lexer.y" { } YY_BREAK case 123: YY_RULE_SETUP -#line 704 "lexer.y" +#line 708 "lexer.y" { } YY_BREAK case 124: YY_RULE_SETUP -#line 705 "lexer.y" +#line 709 "lexer.y" { yytext[yyleng-1] = 0; yylval.expr = new_string(yytext+1); @@ -2289,118 +2293,118 @@ YY_RULE_SETUP YY_BREAK case 125: YY_RULE_SETUP -#line 710 "lexer.y" +#line 714 "lexer.y" { yylval.expr = new_string(yytext); return yy__conf_symbol; } YY_BREAK case 126: YY_RULE_SETUP -#line 713 "lexer.y" +#line 717 "lexer.y" { yylval.expr = new_integer(atoi(yytext)); return yy__conf_int; } YY_BREAK case 127: YY_RULE_SETUP -#line 714 "lexer.y" +#line 718 "lexer.y" { } YY_BREAK case 128: YY_RULE_SETUP -#line 715 "lexer.y" +#line 719 "lexer.y" { BEGIN CONFCOM; } YY_BREAK case 129: YY_RULE_SETUP -#line 716 "lexer.y" +#line 720 "lexer.y" { BEGIN CONFCOM; fprintf(errfile,msgtext_syntax_error_in_conf_file[language], my_anubis_directory,lineno); anb_exit(1); } YY_BREAK case 130: YY_RULE_SETUP -#line 718 "lexer.y" +#line 722 "lexer.y" { printf(yytext); fflush(stdout); } YY_BREAK case 131: YY_RULE_SETUP -#line 719 "lexer.y" +#line 723 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__type; } YY_BREAK case 132: YY_RULE_SETUP -#line 721 "lexer.y" +#line 725 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__p_type; } YY_BREAK case 133: YY_RULE_SETUP -#line 723 "lexer.y" +#line 727 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__variable; } YY_BREAK case 134: YY_RULE_SETUP -#line 725 "lexer.y" +#line 729 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__p_variable; } YY_BREAK case 135: YY_RULE_SETUP -#line 727 "lexer.y" +#line 731 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__theorem; } YY_BREAK case 136: YY_RULE_SETUP -#line 729 "lexer.y" +#line 733 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__p_theorem; } YY_BREAK case 137: YY_RULE_SETUP -#line 731 "lexer.y" +#line 735 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__operation; } YY_BREAK case 138: YY_RULE_SETUP -#line 733 "lexer.y" +#line 737 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__g_operation; } YY_BREAK case 139: YY_RULE_SETUP -#line 735 "lexer.y" +#line 739 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__operation; } YY_BREAK case 140: YY_RULE_SETUP -#line 737 "lexer.y" +#line 741 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__g_operation; } YY_BREAK case 141: YY_RULE_SETUP -#line 739 "lexer.y" +#line 743 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__p_operation; } YY_BREAK case 142: YY_RULE_SETUP -#line 741 "lexer.y" +#line 745 "lexer.y" { if (polish) { yylval.expr = linecol(); BEGIN INCL; return yy__read; } else if (!gindex && !errors) { BEGIN INCL; } } YY_BREAK case 143: YY_RULE_SETUP -#line 743 "lexer.y" +#line 747 "lexer.y" { BEGIN PAR; par_seen = 1; current_par_line = lineno; yylval.expr = linecol(); return yy__C_constr_for; } YY_BREAK case 144: YY_RULE_SETUP -#line 745 "lexer.y" +#line 749 "lexer.y" { if (polish) { yylval.expr = new_string(yytext); @@ -2421,6 +2425,7 @@ YY_RULE_SETUP else { if (include_stack_ptr >= max_include) { + err_line_col(linecol()); fprintf(errfile, msgtext_include_too_deeply[language], max_include); @@ -2456,12 +2461,12 @@ YY_RULE_SETUP YY_BREAK case 145: YY_RULE_SETUP -#line 797 "lexer.y" +#line 802 "lexer.y" { lexical_kwread_error(); } YY_BREAK case 146: YY_RULE_SETUP -#line 798 "lexer.y" +#line 803 "lexer.y" { lexical_kwread_error(); } YY_BREAK case YY_STATE_EOF(INITIAL): @@ -2472,7 +2477,7 @@ case YY_STATE_EOF(STR): case YY_STATE_EOF(STRTL): case YY_STATE_EOF(CONF): case YY_STATE_EOF(CONFCOM): -#line 799 "lexer.y" +#line 804 "lexer.y" { if (include_stack_ptr == 0) { yyterminate(); @@ -2505,44 +2510,44 @@ case YY_STATE_EOF(CONFCOM): YY_BREAK case 147: YY_RULE_SETUP -#line 828 "lexer.y" +#line 833 "lexer.y" { yylval.integer = atoi(yytext); return yy__integer; } YY_BREAK case 148: YY_RULE_SETUP -#line 829 "lexer.y" +#line 834 "lexer.y" { yylval.expr = new_string(yytext); return yy__float; } YY_BREAK case 149: YY_RULE_SETUP -#line 830 "lexer.y" +#line 835 "lexer.y" { yylval.expr = new_utvar(yytext+1); return yy__utvar; } YY_BREAK case 150: YY_RULE_SETUP -#line 831 "lexer.y" +#line 836 "lexer.y" { yylval.expr = new_string(yytext); return yy__Symbol; } YY_BREAK case 151: YY_RULE_SETUP -#line 832 "lexer.y" +#line 837 "lexer.y" { yylval.expr = new_string(yytext); return yy__symbol; } YY_BREAK case 152: YY_RULE_SETUP -#line 833 "lexer.y" +#line 838 "lexer.y" { yylval.expr = new_string(rec_name(yytext,4)); return yy__rec_mapsto; } YY_BREAK case 153: YY_RULE_SETUP -#line 835 "lexer.y" +#line 840 "lexer.y" { yylval.expr = new_string(rec_name(yytext,5)); return yy__rec_mapstoo; } YY_BREAK case 154: YY_RULE_SETUP -#line 837 "lexer.y" +#line 842 "lexer.y" { if (reading_predef) { yylval.expr = new_string(yytext); return yy__Symbol; } else lexical_error(); @@ -2550,7 +2555,7 @@ YY_RULE_SETUP YY_BREAK case 155: YY_RULE_SETUP -#line 841 "lexer.y" +#line 846 "lexer.y" { if (reading_predef) { yylval.expr = new_string(yytext); return yy__symbol; } else lexical_error(); @@ -2558,7 +2563,7 @@ YY_RULE_SETUP YY_BREAK case 156: YY_RULE_SETUP -#line 845 "lexer.y" +#line 850 "lexer.y" { if (reading_predef) { yylval.expr = new_string(yytext); return yy__symbol; } else lexical_error(); @@ -2566,35 +2571,35 @@ YY_RULE_SETUP YY_BREAK case 157: YY_RULE_SETUP -#line 849 "lexer.y" +#line 854 "lexer.y" { } YY_BREAK case 158: YY_RULE_SETUP -#line 850 "lexer.y" +#line 855 "lexer.y" { } YY_BREAK case 159: YY_RULE_SETUP -#line 851 "lexer.y" +#line 856 "lexer.y" { lexical_error(); } YY_BREAK case 160: YY_RULE_SETUP -#line 852 "lexer.y" +#line 857 "lexer.y" { if (gindex) store_external_comment('\n'); } YY_BREAK case 161: YY_RULE_SETUP -#line 853 "lexer.y" +#line 858 "lexer.y" { if (gindex) store_external_comment(yytext[0]); } YY_BREAK case 162: YY_RULE_SETUP -#line 854 "lexer.y" +#line 859 "lexer.y" ECHO; YY_BREAK -#line 2598 "lex.yy.c" +#line 2603 "lex.yy.c" case YY_END_OF_BUFFER: { @@ -3484,7 +3489,7 @@ int main() return 0; } #endif -#line 854 "lexer.y" +#line 859 "lexer.y" diff --git a/anubis_dev/compiler/src/lexer.y b/anubis_dev/compiler/src/lexer.y index 06cfd5b..81df687 100644 --- a/anubis_dev/compiler/src/lexer.y +++ b/anubis_dev/compiler/src/lexer.y @@ -137,6 +137,7 @@ char *path_prefix(char *name) int i = strlen(name); if (i >= path_prefix_length-5) { + err_line_col(linecol()); fprintf(errfile, msgtext_file_path_too_long[language], name); @@ -194,6 +195,7 @@ FILE *fopensrc(char *name) /* if path is absolute do not try the libraries */ if (name[0] == '/' || name[0] == '\\') { + err_line_col(linecol()); fprintf(errfile, msgtext_cannot_find_src_file[language], name, @@ -232,6 +234,7 @@ FILE *fopensrc(char *name) } /* The file is not found: send a message and exit */ + err_line_col(linecol()); fprintf(errfile, msgtext_cannot_find_src_file[language], name, @@ -338,6 +341,7 @@ void add_to_already_included(char *name) //printf("Adding '%s'.\n",name); if (next_already_included == max_already_included) { + err_line_col(linecol()); fprintf(errfile, msgtext_too_many_files[language]); anb_exit(1); @@ -762,6 +766,7 @@ static char *rec_name(char *s, int del) else { if (include_stack_ptr >= max_include) { + err_line_col(linecol()); fprintf(errfile, msgtext_include_too_deeply[language], max_include); diff --git a/anubis_dev/compiler/src/predef.aux b/anubis_dev/compiler/src/predef.aux index 321e26c..169c5be 100644 --- a/anubis_dev/compiler/src/predef.aux +++ b/anubis_dev/compiler/src/predef.aux @@ -4117,10 +4117,12 @@ nil), cons(cons(cons(type_struct_ptr, new_integer(3)), new_string("handle")), -nil), +cons(cons(type_Int32, +new_string("max")), +nil)), no_term); -new_op_scheme(18832910,0, +new_op_scheme(18833934,0, new_string("Bool"), cons(new_string("£default_host_window_handling"), nil), @@ -4163,7 +4165,7 @@ new_string("event_handler")), nil))))), no_term); -new_op_scheme(18839566,0, +new_op_scheme(18840590,0, new_string("One"), cons(new_string("£begin_paint"), nil), @@ -4172,7 +4174,7 @@ new_string("window")), nil), no_term); -new_op_scheme(18843150,0, +new_op_scheme(18844174,0, new_string("One"), cons(new_string("£end_paint"), nil), @@ -4181,7 +4183,7 @@ new_string("window")), nil), no_term); -new_op_scheme(18847246,0, +new_op_scheme(18848270,0, new_string("Bool"), cons(new_string("£handle_host_window_events"), nil), @@ -4219,7 +4221,7 @@ new_string("eh")), nil)))), no_term); -new_op_scheme(18864142,0, +new_op_scheme(18865166,0, new_string("Bool"), cons(new_string("£default_host_window_handling"), nil), @@ -4262,7 +4264,7 @@ new_string("event_handler")), nil))))), no_term); -new_op_scheme(18875420,1, +new_op_scheme(18876444,1, new_string("One"), cons(new_string("generic_host_window_handler"), nil), @@ -4320,7 +4322,7 @@ nil), nil), nil)),0,1); -new_op_scheme(18890766,0, +new_op_scheme(18891790,0, cons(app_ts, cons(new_string("Maybe"), cons(cons(app_ts, @@ -4348,7 +4350,7 @@ new_string("sort")), nil)))))), no_term); -new_op_scheme(18898972,1, +new_op_scheme(18899996,1, cons(app_ts, cons(new_string("Maybe"), cons(new_string("HostWindow"), @@ -4402,7 +4404,7 @@ new_string("compress")), nil)))))), no_term); -new_op_scheme(18915356,1, +new_op_scheme(18916380,1, new_string("One"), cons(new_string("show"), nil), @@ -4411,7 +4413,7 @@ new_string("win")), nil), no_term); -new_op_scheme(18919452,1, +new_op_scheme(18920476,1, new_string("One"), cons(new_string("hide"), nil), @@ -4420,7 +4422,7 @@ new_string("win")), nil), no_term); -new_op_scheme(18929180,1, +new_op_scheme(18930204,1, new_string("Bool"), cons(new_string("queue_event"), nil), @@ -4434,7 +4436,7 @@ new_string("e")), nil)), no_term); -new_op_scheme(18939420,1, +new_op_scheme(18940444,1, new_string("One"), cons(new_string("paint_rectangle"), nil), @@ -4447,7 +4449,7 @@ new_string("color")), nil))), no_term); -new_op_scheme(18945052,1, +new_op_scheme(18946076,1, new_string("One"), cons(new_string("paint_rectangle"), nil), @@ -4460,7 +4462,7 @@ new_string("color")), nil))), no_term); -new_op_scheme(18962460,1, +new_op_scheme(18963484,1, new_string("One"), cons(new_string("paint_image"), nil), @@ -4477,7 +4479,7 @@ new_string("image")), nil))))), no_term); -new_op_scheme(18969116,1, +new_op_scheme(18970140,1, new_string("One"), cons(new_string("paint_image"), nil), @@ -4494,7 +4496,7 @@ new_string("image")), nil))))), no_term); -new_op_scheme(18975260,1, +new_op_scheme(18976284,1, new_string("One"), cons(new_string("map_to_host_window"), nil), diff --git a/anubis_dev/compiler/src/show.c b/anubis_dev/compiler/src/show.c index d01e02b..486dc64 100644 --- a/anubis_dev/compiler/src/show.c +++ b/anubis_dev/compiler/src/show.c @@ -822,9 +822,10 @@ void show_symbol_ambiguity(FILE *fp, case operation: /* (operation . ) */ opid = integer_value(third(car(interp))); + show_type(fp,type_from_interpretation(car(interp),cdr(interp)),cdr(interp)); sprintf(buf,msgtext_file_line[language],string_content(operations[opid].file_name), integer_value(operations[opid].line)); - fprintf(fp,"%s (%s)\n",string_content(forth(car(interp))),buf); + fprintf(fp," %s (%s)\n",string_content(forth(car(interp))),buf); break; default: internal_error("not a symbol interpretation",interp); diff --git a/anubis_dev/library/examples/paint.anubis b/anubis_dev/library/examples/paint.anubis index 68d56f4..da256da 100644 --- a/anubis_dev/library/examples/paint.anubis +++ b/anubis_dev/library/examples/paint.anubis @@ -63,7 +63,7 @@ define (HostWindow hw, HostWindowEvent(One) e) -> List(Rectangle) pointer_entering then [], pointer_leaving then [], key_down(ks,kk) then [], - mouse_move(ks,x,y) then with r = rect(x,y,x+1,y+1), + mouse_move(ks,x,y) then with r = rect(x,y,x+4,y+2), (if mouse_left(ks) then paint_rectangle(buffer,r,rgb(255,255,0)) else unique); @@ -77,7 +77,7 @@ define (HostWindow hw, HostWindowEvent(One) e) -> List(Rectangle) Finally, here is our program. It first creates the buffer, then opens the host - window. If the host windows has been opened, it must be shown by 'show', otherwise it + window. If the host window has been opened, it must be shown by 'show', otherwise it remains invisible. global define One diff --git a/anubis_dev/library/examples/runge_kutta.anubis b/anubis_dev/library/examples/runge_kutta.anubis index 47fbdc4..6b05c24 100644 --- a/anubis_dev/library/examples/runge_kutta.anubis +++ b/anubis_dev/library/examples/runge_kutta.anubis @@ -34,20 +34,29 @@ read tools/maybefloat.anubis +define Maybe(Float) _1 = success(1). +define Maybe(Float) _2 = success(2). +define Maybe(Float) _3 = success(3). + define Maybe(Float) f ( Maybe(Float) x, Maybe(Float) y - ) = + ) = + abs(x)^y. + x+y*y*y. + x*x + y. + x*x - y*y. x - y*y. - success(1)/y. + x - y*x*y. + _3 * ((y*y)^(_1/_3)). sin(x*y). - success(3)* ((y*y)^(success(1)/success(3))). - + success(1)/y. sin(y). + The Runge-Kutta method allows the computation of points on the graph of a solution starting at some point of this graph. Starting at a point (x_0,y_0) in the domain of diff --git a/anubis_dev/library/network/dns.anubis b/anubis_dev/library/network/dns.anubis index 6a08b5b..224e682 100644 --- a/anubis_dev/library/network/dns.anubis +++ b/anubis_dev/library/network/dns.anubis @@ -284,6 +284,9 @@ global define One *** The command line tool. --------------------------------------------------------------------------------------- + +read tools/basis.anubis +read tools/connections.anubis //define One debogge(String s) = print(s). define One debogge(String s) = unique. diff --git a/anubis_dev/library/predefined.anubis b/anubis_dev/library/predefined.anubis index 10b82d5..df2b471 100644 --- a/anubis_dev/library/predefined.anubis +++ b/anubis_dev/library/predefined.anubis @@ -3999,11 +3999,13 @@ define HostWindowEvent($E) define List(HostWindowEvent($E)) £get_host_window_events ( - £StructPtr(HostWindow) handle + £StructPtr(HostWindow) handle, + Int32 max // maximal number of events to get ) = + if max =< 0 then [] else if £window_event_pending(handle) then with first = £get_host_window_event(handle), - others = £get_host_window_events(handle), + others = £get_host_window_events(handle,max-1), [first . others] else [ ]. @@ -4103,7 +4105,7 @@ public define One (HostWindow,HostWindowEvent($E)) -> List(Rectangle) event_handler, List(HostWindowEvent($E)) -> List(HostWindowEvent($E)) compress ) = - if £handle_host_window_events(compress(£get_host_window_events(£handle(window))), + if £handle_host_window_events(compress(£get_host_window_events(£handle(window),200)), window,paint_method,event_handler) then unique else wait_for_event; diff --git a/anubis_dev/library/tools/sdbms.anubis b/anubis_dev/library/tools/sdbms.anubis index 85ece35..822dcb3 100644 --- a/anubis_dev/library/tools/sdbms.anubis +++ b/anubis_dev/library/tools/sdbms.anubis @@ -9,7 +9,7 @@ Author: Alain Prouté - Last revision: November 2005. + Last revision: January 2005. @@ -17,27 +17,17 @@ ----------------------------------- Contents ------------------------------------------ *** (1) Overview. - *** (2) Data base and table structure. - *** (2.1) Tables. - *** (2.2) Data bases. - *** (2.3) Initializing a data base and its tables. - *** (2.4) Handling changes in table formats. - *** (2.5) Row identifiers. - *** (3) Using the data base. - *** (3.1) Errors. - *** (3.2) Adding rows to a table. - *** (3.3) Getting rows from a table. - *** (3.4) Testing a number of rows. - *** (3.5) Updating rows in a table. - *** (3.6) Deleting rows from a table. - *** (3.7) Multi-tables queries. - *** (4) Indexing. - *** (4.1) Principles of indexing. - *** (4.2) Main indexing. - *** (4.3) Families of tables. - *** (4.4) Secondary indexing. - *** (4.5) Handling changes of your indexing methods. - *** (5) Using it with 'web/making_a_web_site.anubis'. + *** (2) Errors. + *** (3) Tables and data bases. + *** (4) Managing changes in table formats. + *** (5) Row identifiers. + *** (6) Adding rows to a table. + *** (7) Getting rows from a table. + *** (8) Testing a number of rows. + *** (9) Updating rows in a table. + *** (10) Deleting rows from a table. + *** (11) Multi-tables queries. + *** (12) Using it with 'web/making_a_web_site.anubis'. --------------------------------------------------------------------------------------- @@ -53,8 +43,8 @@ - restructuring commands, like adding columns in a table, deleting columns, etc... - Normally, if a data base is restructured, programs using that database must themself be - restructured, while the transformation of the data base by utilization commands does + Normally, if a data base is restructured, programs using that database must themselves + be restructured, while the transformation of the data base by utilization commands does not require any modification of the programs which are using the data base. Our aim is to provide a data base mecanism, which is as much as possible in the spirit @@ -73,39 +63,48 @@ because it puts de facto at different levels aspects of data base management which are otherwise consider of the same kind. - - *** (2) Data base and table structure. + + *** (2) Errors. + + Errors may occur during the normal management of the data base. + +public type DBError: + file_not_found (String path), + file_reading_problem (String path), + file_type_problem (String path), + cannot_open_file (String path), + cannot_write_file (String path), + record_not_found. + + + + - *** (2.1) Tables. + *** (3) Tables and data bases. The type of the rows of a table is up to you (but must be serializable). However, this data base management system includes a mecanism for handling the possible changes in the definition of the types of the rows of a table, in such a way that updating the actual tables files on the disk is automatic. For this reason, the type scheme - 'DBTable($Row,$HRow)' representing tables has two type parameters. The first one - represents the current type of the rows of the table, the second one represents the - history of all successive types of the rows of the table. With this mecanism, and if - you repects some principles explained below, your program will always be able to read - old tables saved in old formats. When tables are written to disk, they are always - written in the most recent format. - - So, tables are represented by the following opaque type scheme, where the parameter - '$Row' is the current type of the rows of the table, and '$HRow' ('H' like 'History') - is a type containing the history of all successive formats of rows in the table: + 'DBTable' representing tables has two type parameters. The first one represents the + current type of the rows of the table, the second one represents the history of all + successive types of the rows of the table. With this mecanism, and if you repects some + principles explained below, your program will always be able to read old tables saved + in old formats. + + Tables are represented by the following opaque type scheme, where the parameter '$Row' + is the current type of the rows of the table, and '$HRow' ('H' like 'History') is a + type containing the history of all the successive formats of rows in the table: public type DBTable($Row,$HRow):... - - - *** (2.2) Data bases. - The first thing you have to do if you want to use this data base management system, is - to define the type of your data base (you may have several of them). Since a data base - is essentially a set of tables, the type of your data base should be something like - this (at the beginning, you can use the same type for '$Row' and '$HRow'): + to define the type of your data base. Since a data base is essentially a set of + tables, the type of your data base should be something like this (at the beginning, you + can use the same type for '$Row' and '$HRow'): type MY_DB: my_db(DBTable(Client,Client) clients, @@ -128,12 +127,7 @@ public type DBTable($Row,$HRow):... public type DB($MY_DB):... (again an opaque type scheme) where the parameter '$MY_DB' may be instantiated to your type 'MY_DB'. Several - instantiations allow the creation of several data bases (in general of different - types). - - - - *** (2.3) Initializing a data base and its tables. + instantiations allow the creation of several data bases. Now, when you start your program (may be a web site), you need to start the data base manager. This amounts to initialize each table, and use the initialized tables to @@ -145,7 +139,7 @@ public type DB($MY_DB):... (again an opaque type scheme) init_dbtable ("suppliers", compare, identity, identity), ...)), - where 'init_db' is: + where 'init_db' is declared as: public define DB($MY_DB) init_db @@ -154,7 +148,7 @@ public define DB($MY_DB) $MY_DB tables ). - and where 'init_dbtable' is: + and where 'init_dbtable' is declared as: public define DBTable($Row,$HRow) init_dbtable @@ -167,9 +161,9 @@ public define DBTable($Row,$HRow) Notice that the name 'table_name' above becomes part of the names of the files containing the tables (located in the directory 'data_base_directory' above). - 'init_dbtable' does not load the table from the disk. A table is loaded only when + 'init_table' does not load the table from the disk. A table is loaded only when needed. If the table does not exist on the disk, 'init_table' creates an empty table - of the right type. 'init_dbtable' should be executed only once per table, when your + of the right type. 'init_table' should be executed only once per table, when your program starts. The 'compare' function is used for sorting the rows of the table. The table is sorted @@ -182,7 +176,7 @@ public define DBTable($Row,$HRow) ordering the table. You can always reorder the result of 'get_rows' for any purpose. The 'update' and 'store' functions are used for automating the changes of format of the - tables. This is explained below in the section 'Handling changes in table formats'. At + tables. This is explained below in the section 'Managing changes in table formats'. At the beginning, while the two types '$Row' and '$HRow' are identical, you can use the function 'identity' (actually a scheme of function, defined in 'tools/basis.anubis') for both 'update' and 'store'. @@ -194,39 +188,11 @@ public define DBTable($Row,$HRow) dynamically typed) data base system. Of course, you can use several such data bases in a program. - For security reasons, you may want to protect some informations in a table so that this - information cannot be modified. For example, in a 'products' table the reference of the - product should perhaps not be modified. In order to ensure this, you just have to - provide one more argument to 'init_dbtable': - -public define DBTable($Row,$HRow) - init_dbtable - ( - String table_name, - ($Row,$Row) -> Bool compare, - $HRow -> $Row update, - $Row -> $HRow store, - $Row -> $L locked - ). - The new argument 'locked' is of type '$Row -> $L' where $L is any serializable - type. When the system is on the point to update a row in a table it first checks if - 'locked(r1) = locked(r2)', where 'r1' and 'r2' are the previous and new values of the - row. If this check fails the row is not updated. - For example, for initializing your table of products, locking the two fields 'supplier' - and 'name', you just have to provide a function of type: - Product -> (String,String) - extracting the supplier name and product name from the row. - - - - - - - *** (2.4) Handling changes in table formats. + *** (4) Managing changes in table formats. Up to here the types used as instantiations of '$Row' and '$HRow' are the same one. That's OK, and you have begun to distribute your program and your users have created @@ -275,7 +241,7 @@ public define DBTable($Row,$HRow) String address), version_2(String name, String address, - Int32 age). + Int32 age). Important notice: When a change intervenes in the type of rows of the table, the alternative representing the new type of rows must be added at the end of the type @@ -319,7 +285,7 @@ public define DBTable($Row,$HRow) Of course, in the case of a row in the old format (version_1), we need a default value for the age. We have chosen 0 in the example, but you may choose anything. In any traditional data base system, when you add a column to a table, you need to fill this - column with a default value, even if this default value is 'NULL'. + column with a default value, even if this default value is 'NULL' (not filled at all). The other function does not require default values in principle, because it is mainly a conversion between the current version and the latest one (i.e. essentially the same @@ -336,12 +302,12 @@ public define DBTable($Row,$HRow) The type used for storing the data on the disk is always 'HClient' (hence the name of the function 'store'), and according to the above function they are stored as 'version_2(...)'. The type 'Client' is used only internally. This is required for - being able to handle file containing different versions of the tables. + being able of handling file containing different versions of the tables. - *** (2.5) Row identifiers. + *** (5) Row identifiers. The system generates an identifier for each newly created row in a table. It is warranted that not two rows in the same table can have the same row identifier. Row @@ -354,8 +320,8 @@ public type DBRowId($Row):... between tables. The type scheme 'DBRowId' should be considered as absolutely opaque. The author - reserves the possibility of modifying the definition this type in the future. Hence, - use only the public interface for manipulating data of this type. + reserves the possibility of modifying this type in the future. Hence, use only the + public interface for manipulating data of this type. It would have been possible to define the type 'DBRowId' without the parameter, but in this case, identifiers corresponding to tables of different types could not be @@ -366,27 +332,8 @@ public type DBRowId($Row):... - - - - *** (3) Using the data base. - - *** (3.1) Errors. - - Errors may occur during the normal management of the data base. - -public type DBError: - file_not_found (String path), - file_reading_problem (String path), - file_type_problem (String path), - cannot_open_file (String path), - cannot_write_file (String path), - record_not_found. - - - - *** (3.2) Adding rows to a table. + *** (6) Adding rows to a table. Adding rows to a table is performed by the following function: @@ -398,9 +345,6 @@ public define Result(DBError,List(DBRowId($Row))) List($Row) new_rows ). - Notice that the argument 'the_table' above is supposed to be an implicit destructor of - the type which instantiates '$MY_DB'. This is why it is a function. - For example, you may write: add_rows(my_data_base,clients,[row1,row2,row3]) @@ -421,19 +365,19 @@ public define Result(DBError,DBRowId($Row)) - *** (3.3) Getting rows from a table. + *** (7) Getting rows from a table. In order to get rows from a table, you have two methods: (1) you know the row identifiers of the rows you want, - (2) you want to get all the rows satisfying some condition. + (2) you want to get all rows satisfying some condition. You get rows in the form of: public type DBRow($Row): dbrow(DBRowId($Row) row_id, Int32 version, - $Row row). + $Row row). A datum of type DBRow($Row) contains the identifier of the row, a version number and the row itself. The version number is 0 when the row is created (by 'add_row' or @@ -448,9 +392,8 @@ public define Result(DBError,DBRow($Row)) DBRowId($Row) row_id ). - If no error occurs, the function returns the row whose row identifier is given. The - next one can be used for getting several rows by their row identifiers. With the - variant below you can get several rows in one operation. + If no error occurs, the function returns the wanted row. The next one can be used for + getting several rows. public define Result(DBError,List(DBRow($Row))) get_rows @@ -460,9 +403,6 @@ public define Result(DBError,List(DBRow($Row))) List(DBRowId($Row)) row_ids ). - Now, if you prefer to use a condition instead of a list of row identifiers, use this - one: - public define Result(DBError,List(DBRow($Row))) get_rows ( @@ -488,7 +428,7 @@ public define Result(DBError,List(DBRow($Row))) - *** (3.4) Testing a number of rows. + *** (8) Testing a number of rows. It may be useful to get the number of rows satisfying some condition in a table. It may also be useful to know if a table contains more rows satisfying a condition than a @@ -524,7 +464,7 @@ public define Result(DBError,Bool) - *** (3.5) Updating rows in a table. + *** (9) Updating rows in a table. Updating rows in a table is generally the consequence of a previous reading of these rows. Indeed, interactive programs will first show the row to be updated to a human @@ -595,7 +535,7 @@ public define Result(DBError,List(DBUpdateResult($Row))) - *** (3.6) Deleting rows from a table. + *** (10) Deleting rows from a table. public define Result(DBError,$Row) delete_row @@ -625,7 +565,7 @@ public define Result(DBError,List($Row)) - *** (3.7) Multi-tables queries. + *** (11) Multi-tables queries. The SQL language has an important feature which is the 'SELECT' command. We need something analogous to the SQL query: @@ -656,7 +596,7 @@ public define Result(DBError,List($Row)) (essentially the security provided by the strong typing mecanism), and the flexibility of usual data bases could be realized only with a more elaborate version of Anubis, including meta-programming, and maybe another virtual machine able to optimize on the - fly. All these new features are part of the Anubis2 project. + fly. All these new features are part of the Anubis2/Paradize project. For the time being, we content ourself with the following recursive querying mecanism. Actually, the recursion is on the number of tables concerned by the query. @@ -700,285 +640,68 @@ public define List($Out) named "Yoyo". Here is the query (which returns a list of type 'List(String)'): query(my_data_base, - clients, // begin with the table 'clients' - (DBRow(Client) clt) |-> country(row(clt)) = "France", // consider only French clients - (DBRow(Client) clt, One u) |-> name(row(clt)), // keep only the name of the client - (DBRow(Client) clt) |-> // the subquery for this client 'clt' - query(my_data_base, - commands, // continue with the table 'commands' - (DBRow(Command) cmd) |-> row_id(clt) = client_id(row(cmd)), - (DBRow(Command) cmd, One u) |-> u, // do not keep anything from 'commands' - (DBRow(Command) cmd) |-> - query(my_data_base, - products, - (DBRow(Product) prd) |-> row_id(prd) = product_id(cmd) & - name(row(prd)) = "Yoyo", - (DBRow(Product) prd, One u) |-> u, - (DBRow(Product) prd) |-> unique))) - - This is actually not more complicated than: + clients, // begin with the table 'clients' + (DBRow(Client) clt) |-> // consider only French clients + country(row(clt)) = "France", + (DBRow(Client) clt, One u) |-> // keep only the name of the client + name(row(clt)), + (DBRow(Client) clt) |-> // the subquery for this client 'clt' + query(my_data_base, + commands, // continue with the table 'commands' + (DBRow(Command) cmd) |-> + row_id(clt) = client_id(row(cmd)), + (DBRow(Command) cmd, One u) |-> u, // do not keep anything from 'commands' + (DBRow(Command) cmd) |-> + query(my_data_base, + products, + (DBRow(Product) prd) |-> + row_id(prd) = product_id(cmd) & + name(row(prd)) = "Yoyo", + (DBRow(Product) prd, One u) |-> u, + (DBRow(Product) prd) |-> unique))) + + Of course, this is more complicated than: SELECT clients.name FROM clients, commands_2005, products WHERE clients.id = commands_2005.client_id AND commands_2005.product_id = products.id AND - products.name = "Yoyo" AND - client.country = "France" + products.name = "Yoyo" - just a little heavier to write down, but meta-programming could automatically transform - the later into the former (in the future). + but meta-programming could automatically transform the later into the former (in the + future). Now, the query may also be more efficient (computed faster) if the tables are put in a - different order. - - - - - - - - - - *** (4) Indexing. - - In order to speed up the access to the data, especially when dealing with big tables, - and also to optimize memory usage, you may need an indexing system. - - - - *** (4.1) Principles of indexing. - - An index is some way of reaching rows of a table more quickly than by just having a - look at all rows until the wanted rows are found. This is similar to a dictionary. When - you are looking for a word in a dictionary, you don't start at the beginning and check - all words until you find yours. You have some more efficient process. - - Notice however that this process is specific to the method used for selecting items - from the dictionary. In this case, the items you want to find are selected 'by - name'. Since items in the dictionary are ordered by name, the search may be performed - quickly. However, what about finding all words, the definition of which contains the - word 'dog' ? This is much more difficult, and requires a priori a complete search item - by item. - - In order to solve this problem, the dictionary could have an 'index' (in general it - does not have one), within which you should easily find the word 'dog', and get a list - of pages or item numbers or item names whose definition contains the word 'dog'. You - may also want to find all words of containing the string 'gap' in their name. In this - case, you need yet another kind of index. - - From this dictionary story, we should remember two things: - - 1. The dictionary is ordered in such a way that finding an item 'by name' is easy, - 2. If we want to find items according to other criteria, we need an extra index - (maybe one for each criterium). - - The way the dictionary is ordered could be called the 'main indexing'. Each extra - index may be calld a 'secondary indexing'. The same principles apply to data bases, - except that instead of ordering items by alphabetical order we use 'hashing methods'. - - - *** (4.2) Main indexing. - - Given a table in your data base, you may ignore indexing or decide some 'main' way of - indexing the table. We begin by an example. Consider the 'clients' table. Each client - has a name, and several other data attached to him. In general, when you are looking - for a client, you got the name of that client (alternatively you may have a 'client - number'). Hence searching a client is most often performed 'by name'. So, you may - decide that the main indexing of the 'clients' table should speed up the search 'by - name'. In that case the 'search criterion' will be represented by the function mapping - a row of the table to the 'name' field. This function may be for example the implicit - destructor 'name', which is of type: - - Client -> String - - In order to take this 'main indexing' into account, use the following variant of - 'init_dbtable', for initializing your table: - -public define DBTable($Row,$HRow) - init_dbtable - ( - String table_name, - ($Row,$Row) -> Bool compare, - $HRow -> $Row update, - $Row -> $HRow store, - $Row -> $L locked, - $Row -> $N main_search_criterion - ). - - Actually, you have a big freedom in choosing the type '$N'. The only constraint is that - it must be serializable. - - Now, when you want to get rows from that table using this main indexing, you have to - use variants 'get_rows'. Indeed, instead of giving row identifiers or search - criterions, you give a datum of type 'DBIndexVal($N)'. Notice that unlike the search by - row identifiers, the search by index may produce several rows for the same index - value. Hence, 'get_row' is not concerned by indexing. Only 'get_rows' (and similarly - 'update_rows' and 'delete_rows') is concerned. - - -public type DBIndexVal($Row,$N):... (an opaque type) - -public define Result(DBError,List(DBRow($Row))) - get_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBIndexVal($Row,$N) main_index_value - ). - - - The same principles apply to other utilization functions: - -public define Result(DBError,Int32) - get_number_of_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBIndexVal($Row,$N) main_index_value - ). - -public define Result(DBError,Bool) - has_more_rows_than - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBIndexVal($Row,$N) main_index_value, - Int32 n - ). - -public define Result(DBError,List(DBUpdateResult($Row))) - update_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBIndexVal($Row,$N) main_index_value, - DBRow($Row) -> Maybe($Row) how - ). - -public define Result(DBError,$Row) - delete_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBIndexVal($Row,$N) main_index_value - ). - + different order. For example, we may consider the following equivalent query (with the + tables in reverse order): + query(my_data_base, + products, + (DBRow(Product) prd) |-> name(row(prd)) = "Yoyo", + (DBRow(Product) prd, String cltname) |-> cltname, + (DBRow(Product) prd) |-> + query(my_data_base, + commands, + (DBRow(Command) cmd) |-> + row_id(prd) = product_id(row(cmd)), + (DBRow(Command) cmd, String cltname) |-> cltname, + (DBRow(Command) cmd) |-> + query(my_data_base, + clients, + (DBRow(Client) clt) |-> + country(row(clt)) = "France" & + row_id(clt) = client_id(row(cmd)), + (DBRow(Client) clt, One u) |-> name(row(clt)), + (DBRow(Client) clt) |-> unique))) + + This one may be more efficient than the first one. The order into which the tables are + looked up may be important for performances. - *** (4.3) Families of tables. - It may be the case that you prefer to use a family of small tables (of the same type) - instead of a big unique table. For example, if you are managing products in a shop, you - may have a very big number of products, each one with many informations, and want (say) - one table by product. - - Actually, this is just a matter of performances and memory management. The only thing - you have to do in order to split your big table into a lot of small tables is just to - have a main index. The table will be automatically split into a familly of smaller - tables. The number of these tables may change during exploitation of your table, - depending on the number of rows. In any case, it is automatic. - - Nevertheless, the type '$N' of main index values must be such that rows are - sufficiently separated from each other. At the limit, if you choose 'One' for this - type, all rows will have the same index value, and the system will not be able to - create more than one file for the whole table. So, 'One' is a very bad choice. In - practice, for a table of products, the name of the product, or the supplier reference - for that product should provide a good main indexation, which will allow the system to - split the table into almost as many files as there are possible values for this index. - - - - *** (4.4) Secondary indexing. - - Now, you may also have to perform searches according to several criteria in a given - table. Hence, you may need 'secondary' indexing methods. - - Secondary indexes are not part of the table (unlike main indexing, they have no - influence on how the table is implemented). Hence, secondary indexes must be - initialized by themself. Secondary indexes for a table whose row type is '$Row' are - data of the following type: - -public type DBSecondaryIndex($Row,$S):... (an opaque type) - - - A secondary index is initialized by: - -public define DBSecondaryIndex($Row,$S) - init_dbindex - ( - String index_name, // used as part of a file name - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - $Row -> $S secondary_search_criterion - ). - - As for tables, the initialization of an index must be performed only once when your - program starts. - - Now, you also need variants of 'get_rows', etc... for searching according to secondary - indexing. Here they are. - -public define Result(DBError,List(DBRow($Row))) - get_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBSecondaryIndex($Row,$S) index, - DBIndexVal($Row,$S) secondary_index_value - ). - -public define Result(DBError,Int32) - get_number_of_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBSecondaryIndex($Row,$S) index, - DBIndexVal($Row,$S) secondary_index_value - ). - -public define Result(DBError,Bool) - has_more_rows_than - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBSecondaryIndex($Row,$S) index, - DBIndexVal($Row,$S) secondary_index_value, - Int32 n - ). - -public define Result(DBError,List(DBUpdateResult($Row))) - update_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBSecondaryIndex($Row,$S) index, - DBIndexVal($Row,$S) secondary_index_value, - DBRow($Row) -> Maybe($Row) how - ). - -public define Result(DBError,$Row) - delete_rows - ( - DB($MY_DB) the_data_base, - $MY_DB -> DBTable($Row,$HRow) the_table, - DBSecondaryIndex($Row,$S) index, - DBIndexVal($Row,$S) secondary_index_value - ). - - - - - - - *** (4.5) Handling changes of your indexing methods. - - - - - *** (5) Using it with 'web/making_a_web_site.anubis'. + *** (12) Using it with 'web/making_a_web_site.anubis'. You may want to use this data base manager in conjunction with our web site making method. You should proceed as follows in order to start the data base and the web @@ -1066,204 +789,33 @@ public define Result(DBError,$Row) --- That's all for the public part ! -------------------------------------------------- - ----------------------------------- Contents ------------------------------------------ - - *** [1] Opaque types. - *** [1.1] Row identifiers. - *** [1.2] Tables. - *** [1.2.1] Type 1 (simple tables). - *** [1.2.2] Type 2 (hash-split tables). - *** [1.3] All tables. - *** [1.4] Data bases. - *** [1.5] Indexing. - - *** [2] Tools. - *** [2.1] Comparing rows. - *** [2.2] Removing an element from a list. - *** [2.3] Inserting elements in a sorted list. - *** [2.4] Removing repeated elements from a list. - *** [2.5] Flattening a list of lists. - - *** [3] Loading and saving table chunks. - *** [3.1] Loading a table chunk. - *** [3.1.1] Type 1 tables. - *** [3.1.2] Type 2 tables. - *** [3.2] Saving a table chunk. - *** [3.2.1] Type 1 tables. - *** [3.2.2] Type 2 tables. - - *** [4] Initializing a data base. - - *** [5] Utilization commands. - *** [5.1] Accessing a table. - *** [5.1.1] Type 1 tables. - *** [5.1.2] Type 2 tables. - *** [5.2] Adding rows to a table. - *** [5.2.1] Type 1 tables. - *** [5.2.2] Type 2 tables. - *** [5.3] Getting rows from a table. - *** [5.3.1] Type 1 tables. - *** [5.3.2] Type 2 tables. - *** [5.4] Getting a number of rows. - *** [5.4.1] Type 1 tables. - *** [5.4.2] Type 2 tables. - *** [5.5] Updating rows. - *** [5.5.1] Type 1 tables. - *** [5.5.2] Type 2 tables. - *** [5.6] Deleting rows. - *** [5.6.1] Type 1 tables. - *** [5.6.2] Type 2 tables. - *** [5.7] Multi-tables queries. - - *** [6] Indexing. - - --------------------------------------------------------------------------------------- - -read tools/basis.anubis required for 'qsort' -read tools/hashtable.anubis because hashtables are better than lists for performances - - - - - *** [1] Opaque types. - - - *** [1.1] Row identifiers. - Row identifiers are just integers, but this may change in the future, because integers may not be enough. public type DBRowId($Row): dbrowid(Int32 index). - - - - *** [1.2] Tables. - - There are several types of tables (different implementations in memory and on disk). A - type 1 table is saved into a single file. A type 2 table is split into chunks and each - chunk is saved into a separate file. - - Tables or table chunks are loaded into memory only on demand. During the loading the - format is changed to some format appropriate for the memory. - - - *** [1.2.1] Type 1 (simple tables). - - Type 1 (simple tables) is good for small tables (tables with very few rows). The table - is saved into a single file. It is loaded only on demand. - When a table is loaded, a boolean variable 'changed' indicates if it has to be saved. A integer variable contains the counter for creating identifiers for new rows. Another variable contains the list of all loaded rows. -type DBLoadedTable1($Row,$HRow): +type DBLoadedTable($Row,$HRow): table(String table_name, (DBRow($Row),DBRow($Row)) -> Bool compare, $Row -> $HRow store, Var(Bool) changed_v, Var(Int32) counter_v, Var(List(DBRow($Row))) rows_v). - - Now, tables in the data base are either loaded or not. - -type DBTableState1($Row,$HRow): - not_loaded (String table_name, - ($Row,$Row) -> Bool compare, - $HRow -> $Row update, - $Row -> $HRow store), - loaded (DBLoadedTable1($Row,$HRow)). - - Now a table is just a variable containing either a non loaded table or a loaded table. - - - - - - - - - *** [1.2.2] Type 2 (hash-split tables). - - split_table(String table_name, - (DBRow($Row),DBRow($Row)) -> Bool compare, - Var(Bool) changed_v, - Var(Int32) counter_v, - HashTable(Int32,List(DBRow($Row))) rows). - - + Of course, tables are not saved on disk as 'DBLoadedTable($Row,$HRow)' because variable + types (and functional types for the time being) are not serializable, and also because + data of type '$Row' are not saved on the disk. Tables are saved as data of type + 'DBSavedTable($HRow)'. +type DBSavedTable($HRow): + myversion_1(Int32 counter, + List(DBRow($HRow)) rows). - *** [1.3] Table file formats. - - Of course, tables are not saved on disk as 'DBLoadedTable1($Row,$HRow)' because - variable types (and functional types for the time being) are not serializable, and also - because data of type '$Row' are not saved on the disk. Tables are saved as data of - type 'TableFileContent($HRow)'. - -type TableFileContent($HRow): - type_1(Int32 counter, - List(DBRow($HRow)) rows). - - - - - - *** [1.3] All tables. - -public type DBTable($Row,$HRow): - db_table_1(Var(DBTableState1($Row,$HRow))). - db_table_2(Var(DBTableState2($Row,$HRow))). - - - - *** [1.4] Data bases. - - The data base itself is made of the directory path for table files, and the set of - tables (loaded or not). - -public type DB($MY_DB): - db(String directory, - $MY_DB tables). - - - - - - *** [1.5] Indexing. - -public type DBIndexVal($Row,$N): - index_val($N). - - -type DBLoadedIndex($Row,$S): - index(String index_name, - String directory_name, - Var(Bool) changed_v, - Var(HashTable(Int32,List(Int32))) entries). - -type IndexState($Row,$S): - not_loaded(String index_name, - String directory_name, - $Row -> $S secondary_search_criterion), - loaded(DBLoadedIndex($Row,$S)). - -public type DBSecondaryIndex($Row,$N): - secondary_index(Var(IndexState($Row,$N))). - - - - - - - - *** [2] Tools. - - - - *** [2.1] Comparing rows. We will have to compare the rows of a loaded table. The function able to compare loaded rows must be constructed from the function comparing nude rows. This is the job of the @@ -1279,116 +831,23 @@ define (DBRow($Row),DBRow($Row)) -> Bool if dbr2 is dbrow(id2,v2,r2) then compare(r1,r2). - - - - *** [2.2] Removing an element from a list. - -define List($T) - remove - ( - $T x, - List($T) l - ) = - if l is - { - [ ] then [ ], - [h . t] then - if x = h - then t - else [h . remove(x,t)] - }. - - - - - *** [2.3] Inserting elements in a sorted list. - - The next function receives two lists (of the same type) which are both already sorted. - It inserts the elements of the first list at the right place in the second list. - -define List($T) - insert // insert elements of l1 into l2 - ( - List($T) l1, - List($T) l2, - ($T,$T) -> Bool less - ) = - if l1 is - { - [ ] then l2, - [h1 . t1] then - if l2 is - { - [ ] then l1, - [h2 . t2] then - if less(h1,h2) - then [h1 . insert(t1,l2,less)] - else [h2 . insert(l1,t2,less)] - } - }. - - - - - - *** [2.4] Removing repeated elements from a list. - -define List($Out) - remove_repetitions - ( - List($Out) l - ) = - if l is - { - [ ] then [ ], - [h . t] then - if member(t,h) - then remove_repetitions(t) - else [h . remove_repetitions(t)] - }. - - - - *** [2.5] Flattening a list of lists. - -define List($T) - flatten - ( - List(List($T)) l - ) = - if l is - { - [ ] then [ ], - [h . t] then h + flatten(t) - }. - - - - - - *** [3] Loading and saving table chunks. - - - *** [3.1] Loading a table chunk. - - - *** [3.1.1] Type 1 tables. Tables need to be installed (which involves installing all rows). -define DBLoadedTable1($Row,$HRow) +read tools/basis.anubis required for 'qsort' + +define DBLoadedTable($Row,$HRow) install_table ( String table_name, ($Row,$Row) -> Bool compare, $HRow -> $Row update, $Row -> $HRow store, - TableFileContent($HRow) st + DBSavedTable($HRow) st ) = if st is { - type_1(count,rows) then + myversion_1(count,rows) then with db_compare = dbrow(compare), updated_rows = map((DBRow($HRow) dbr) |-> if dbr is dbrow(row_id,version,row) @@ -1404,15 +863,6 @@ define DBLoadedTable1($Row,$HRow) }. - - *** [3.1.2] Type 2 tables. - - - - *** [3.2] Saving a table chunk. - - - *** [3.2.1] Type 1 tables. Saving a table on the disk (the table also persists in memory). @@ -1420,11 +870,11 @@ define Result(DBError,One) save_table ( String dbdir, - DBLoadedTable1($Row,$HRow) t + DBLoadedTable($Row,$HRow) t ) = if t is table(table_name,compare,store,changed_v,counter_v,rows_v) then with path = dbdir+"/"+table_name, - if protect save((TableFileContent($HRow))type_1(*counter_v, + if protect save((DBSavedTable($HRow))myversion_1(*counter_v, map((DBRow($Row) dbr) |-> if dbr is dbrow(row_id,version,row) then if row_id is dbrowid(i) then @@ -1439,15 +889,29 @@ define Result(DBError,One) - *** [3.2.2] Type 2 tables. + Tables in the data base are either loaded or not. + +type DBTableState($Row,$HRow): + not_loaded (String table_name, + ($Row,$Row) -> Bool compare, + $HRow -> $Row update, + $Row -> $HRow store), + loaded (DBLoadedTable($Row,$HRow)). + Now a table is just a variable containing either a non loaded table or a loaded table. +public type DBTable($Row,$HRow): + db_table(Var(DBTableState($Row,$HRow))). + The data base itself is made of the directory path for table files, and the set of + tables (loaded or not). - - *** [4] Initializing a data base. +public type DB($MY_DB): + db(String directory, + $MY_DB tables). + public define DB($MY_DB) init_db ( @@ -1457,9 +921,6 @@ public define DB($MY_DB) forget(make_directory(data_base_directory,default_directory_mode)); db(data_base_directory,tables). - - - public define DBTable($Row,$HRow) init_dbtable ( @@ -1468,41 +929,28 @@ public define DBTable($Row,$HRow) $HRow -> $Row update, $Row -> $HRow store ) = - db_table_1(var(not_loaded(table_name,compare,update,store))). - - - - - + db_table(var(not_loaded(table_name,compare,update,store))). - *** [5] Utilization commands. - - - - *** [5.1] Accessing a table. Given a data base and a table in the form of a destructor of type MY_DB, the next function returns the table in the form of a loaded table. - - *** [5.1.1] Type 1 tables. - -define Result(DBError,DBLoadedTable1($Row,$HRow)) +define Result(DBError,DBLoadedTable($Row,$HRow)) get_table ( DB($MY_DB) the_data_base, $MY_DB -> DBTable($Row,$HRow) the_table, ) = if the_data_base is db(dbdir,my_db) then - if the_table(my_db) is db_table_1(table_v) then + if the_table(my_db) is db_table(table_v) then if *table_v is { not_loaded(table_name,compare,update,store) then with file_path = dbdir+"/"+table_name, - if (RetrieveResult(TableFileContent($HRow)))retrieve(file_path) is + if (RetrieveResult(DBSavedTable($HRow)))retrieve(file_path) is { cannot_find_file then - with new_table = install_table(table_name,compare,update,store,type_1(0,[])), + with new_table = install_table(table_name,compare,update,store,myversion_1(0,[])), table_v <- loaded(new_table); ok(new_table), @@ -1519,21 +967,37 @@ define Result(DBError,DBLoadedTable1($Row,$HRow)) }. + Inserting rows. The next function receives two lists (of the same type) which are both + already sorted. It inserts the elements of the first list at the right place in the + second list. +define List($T) + insert // insert elements of l1 into l2 + ( + List($T) l1, + List($T) l2, + ($T,$T) -> Bool less + ) = + if l1 is + { + [ ] then l2, + [h1 . t1] then + if l2 is + { + [ ] then l1, + [h2 . t2] then + if less(h1,h2) + then [h1 . insert(t1,l2,less)] + else [h2 . insert(l1,t2,less)] + } + }. + - *** [5.1.2] Type 2 tables. - - - *** [5.2] Adding rows to a table. - - *** [5.2.1] Type 1 tables. - - - The table contains a list of 'DBRow($Row)' which is already sorted. The new rows arrive - as data of type '$Row', and need first to be sorted. Then they are transformed into - 'DBRow($Row)' and inserted in the table. Hence, we need to compare data of type - 'DBRow($Row)'. + Adding rows. The table contains a list of 'DBRow($Row)' which is already sorted. The + new rows arrive as data of type '$Row', and need first to be sorted. Then they are + transformed into 'DBRow($Row)' and inserted in the table. Hence, we need to compare + data of type 'DBRow($Row)'. The next function gets a list of nude rows (of type '$Row'), and transforms it into a list of loaded rows, creating a new identifier for each row. This function is run @@ -1611,20 +1075,10 @@ public define Result(DBError,DBRowId($Row)) } }. - - - - *** [5.2.2] Type 2 tables. - - - - *** [5.3] Getting rows from a table. - *** [5.3.1] Type 1 tables. - Getting a row by its identifier. define Result(DBError,DBRow($Row)) @@ -1663,6 +1117,21 @@ public define Result(DBError,DBRow($Row)) Getting rows by their identifiers. +define List($T) + remove + ( + $T x, + List($T) l + ) = + if l is + { + [ ] then [ ], + [h . t] then + if x = h + then t + else [h . remove(x,t)] + }. + define Result(DBError,List(DBRow($Row))) get_rows ( @@ -1750,15 +1219,6 @@ public define Result(DBError,List(DBRow($Row))) (DBRow($Row) r) |-> which(row(r))). - *** [5.3.2] Type 2 tables. - - - - - *** [5.4] Getting a number of rows. - - - *** [5.4.1] Type 1 tables. Getting a number of rows. @@ -1829,16 +1289,8 @@ public define Result(DBError,Bool) }. - *** [5.4.2] Type 2 tables. - - - *** [5.5] Updating rows. - - - *** [5.5.1] Type 1 tables. - Updating rows. Rows which satisfy the 'which' condition are first extracted from the table and updated by 'how'. Then the list of updated rows is sorted, and reinserted into the table. Subsequently, the table is saved. @@ -1994,16 +1446,8 @@ public define Result(DBError,List(DBUpdateResult($Row))) } }. - - - *** [5.5.2] Type 2 tables. - *** [5.6] Deleting rows. - - - *** [5.6.1] Type 1 tables. - Deleting rows. @@ -2097,12 +1541,37 @@ public define Result(DBError,List($Row)) - *** [5.6.2] Type 2 tables. - *** [5.7] Multi-tables queries. + + Multi-tables queries. +define List($Out) + remove_repetitions + ( + List($Out) l + ) = + if l is + { + [ ] then [ ], + [h . t] then + if member(t,h) + then remove_repetitions(t) + else [h . remove_repetitions(t)] + }. + + +define List($T) + flatten + ( + List(List($T)) l + ) = + if l is + { + [ ] then [ ], + [h . t] then h + flatten(t) + }. public define List($Out) query @@ -2125,9 +1594,6 @@ public define List($Out) - - *** [6] Indexing. - diff --git a/anubis_dev/library/tools/sdbms1.anubis b/anubis_dev/library/tools/sdbms1.anubis new file mode 100644 index 0000000..822dcb3 --- /dev/null +++ b/anubis_dev/library/tools/sdbms1.anubis @@ -0,0 +1,1599 @@ + + + The Anubis/Paradize Project. + + A Simple Data Base Management System. + + Copyright (c) Alain Prouté 2004-2005. + + + Author: Alain Prouté + + Last revision: January 2005. + + + + + ----------------------------------- Contents ------------------------------------------ + + *** (1) Overview. + *** (2) Errors. + *** (3) Tables and data bases. + *** (4) Managing changes in table formats. + *** (5) Row identifiers. + *** (6) Adding rows to a table. + *** (7) Getting rows from a table. + *** (8) Testing a number of rows. + *** (9) Updating rows in a table. + *** (10) Deleting rows from a table. + *** (11) Multi-tables queries. + *** (12) Using it with 'web/making_a_web_site.anubis'. + + --------------------------------------------------------------------------------------- + + + + + *** (1) Overview. + + Relational data base management systems provide two kinds of commands: + + - utilization commands, like adding, updating or deleting rows in a table, or + querying using the SQL command 'SELECT'. + + - restructuring commands, like adding columns in a table, deleting columns, etc... + + Normally, if a data base is restructured, programs using that database must themselves + be restructured, while the transformation of the data base by utilization commands does + not require any modification of the programs which are using the data base. + + Our aim is to provide a data base mecanism, which is as much as possible in the spirit + of Anubis. Our first demand is that cells in a table should be able to contain data of + any (serializable) Anubis type (one type per column of the table of course). More + precisely, if T is a type, we want to consider tables whose rows are data of type T. A + consequence of this is that adding a column to a table requires a modification of an + Anubis type, hence a modification of the program using the data base. But this is + precisely in the spirit of Anubis, because this will force a corresponding modification + of the program, so ensuring more security. + + This data base management program is highly experimental. Indeed, the obligation of + deciding everything about types of data at compile time, introduces a rigidity which + renders the mimicking of the usual data base manipulations quite difficult (at least as + far as the SELECT command is concerned). Nevertheless, it's an interesting adventure + because it puts de facto at different levels aspects of data base management which are + otherwise consider of the same kind. + + + + + *** (2) Errors. + + Errors may occur during the normal management of the data base. + +public type DBError: + file_not_found (String path), + file_reading_problem (String path), + file_type_problem (String path), + cannot_open_file (String path), + cannot_write_file (String path), + record_not_found. + + + + + + + *** (3) Tables and data bases. + + The type of the rows of a table is up to you (but must be serializable). However, this + data base management system includes a mecanism for handling the possible changes in + the definition of the types of the rows of a table, in such a way that updating the + actual tables files on the disk is automatic. For this reason, the type scheme + 'DBTable' representing tables has two type parameters. The first one represents the + current type of the rows of the table, the second one represents the history of all + successive types of the rows of the table. With this mecanism, and if you repects some + principles explained below, your program will always be able to read old tables saved + in old formats. + + Tables are represented by the following opaque type scheme, where the parameter '$Row' + is the current type of the rows of the table, and '$HRow' ('H' like 'History') is a + type containing the history of all the successive formats of rows in the table: + +public type DBTable($Row,$HRow):... + + The first thing you have to do if you want to use this data base management system, is + to define the type of your data base. Since a data base is essentially a set of + tables, the type of your data base should be something like this (at the beginning, you + can use the same type for '$Row' and '$HRow'): + + type MY_DB: + my_db(DBTable(Client,Client) clients, + DBTable(Product,Product) products, + DBTable(Supplier,Supplier) suppliers, + ...). + + where the types 'Client', 'Product', ... have been previously defined by you. Notice + that a datum of type 'Client' is just an entire row in the 'clients' table. Hence, + 'Client' is probably a type with just one alternative and the components needed for a + 'client', like 'name', 'address', etc... + + What if the type $Row has several alternatives ? This means that your table has several + sorts of rows. But in this case, you should perhaps better use several + tables. Nevertheless, this makes no problem. + + Actually, a datum of type MY_DB is not the whole data base. The system requires more + informations. This is the reason why the actual type of the data base is: + +public type DB($MY_DB):... (again an opaque type scheme) + + where the parameter '$MY_DB' may be instantiated to your type 'MY_DB'. Several + instantiations allow the creation of several data bases. + + Now, when you start your program (may be a web site), you need to start the data base + manager. This amounts to initialize each table, and use the initialized tables to + initialize the data base itself: + + with my_data_base = init_db(my_data_base_directory, + my_db(init_dbtable ("clients", compare, identity, identity), + init_dbtable ("products", compare, identity, identity), + init_dbtable ("suppliers", compare, identity, identity), + ...)), + + where 'init_db' is declared as: + +public define DB($MY_DB) + init_db + ( + String data_base_directory, + $MY_DB tables + ). + + and where 'init_dbtable' is declared as: + +public define DBTable($Row,$HRow) + init_dbtable + ( + String table_name, + ($Row,$Row) -> Bool compare, + $HRow -> $Row update, + $Row -> $HRow store + ). + + Notice that the name 'table_name' above becomes part of the names of the files + containing the tables (located in the directory 'data_base_directory' above). + 'init_table' does not load the table from the disk. A table is loaded only when + needed. If the table does not exist on the disk, 'init_table' creates an empty table + of the right type. 'init_table' should be executed only once per table, when your + program starts. + + The 'compare' function is used for sorting the rows of the table. The table is sorted + when it is read from the disk, and whenever a row is added or updated, it is placed at + the right position in the table according to the order defined by the 'compare' + function. More precisely, if 'r1' and 'r2' are rows in the table, and if + 'compare(r1,r2)' is 'true', 'r1' is placed before 'r2' in the table. When you get rows + from the table (using 'get_rows' declared below) you get your rows in the order defined + by the 'compare' function. Of course, the 'compare' function is just a default way of + ordering the table. You can always reorder the result of 'get_rows' for any purpose. + + The 'update' and 'store' functions are used for automating the changes of format of the + tables. This is explained below in the section 'Managing changes in table formats'. At + the beginning, while the two types '$Row' and '$HRow' are identical, you can use the + function 'identity' (actually a scheme of function, defined in 'tools/basis.anubis') + for both 'update' and 'store'. + + At that point you have the datum 'my_data_base' at hand, which is of type 'DB($MY_DB)', + and which represents the whole data base. The preparation above determines the whole + structure of the data base. With a datum of type 'DB($MY_DB)' at hand, the Anubis + compiler is able to detect many errors that would not be detected by an untyped (or + dynamically typed) data base system. Of course, you can use several such data bases in + a program. + + + + + + *** (4) Managing changes in table formats. + + Up to here the types used as instantiations of '$Row' and '$HRow' are the same one. + That's OK, and you have begun to distribute your program and your users have created + tables whose types are your types 'Client', 'Product' etc... If you simply modify the + definition of (say) the type 'Client', your new program will not work with the tables + created by your users, which is obviously a catastrophe. Fortunately, there is a + remedy, and here it is. + + Assume that you want to change the definition of the type 'Client', because you need + more columns in the table of clients. As an example, assume that your type 'Client' + was first defined as: + + type Client: + client(String name, + String address). + + and you want to add a new component (column of the table): + + Int32 age + + + --- Step 1: + + The first thing to do is to make two copies of your original type, because from now on, + the types '$Row' and '$HRow' are no more identical: + + type Client: // current type of rows of 'clients' table + client(String name, + String address). + + type HClient: // history of type of rows of 'clients' table + client(String name, + String address). + + + --- Step 2: + + The first alternative of 'HClient' should never be changed, because it represents an + 'historical' way of representing clients. The only thing that you can do is change the + name of the alternative. We recommend to name it 'version_1', and to use the name + 'version_2' for the second alternative representing the new type of rows. Hence, the + type 'HClient' becomes: + + type HClient: + version_1(String name, + String address), + version_2(String name, + String address, + Int32 age). + + Important notice: When a change intervenes in the type of rows of the table, the + alternative representing the new type of rows must be added at the end of the type + 'HClient' (or any other instance of '$HRow'). This is because in serialized data the + alternative names are lost, and replaced by their number in the list of + alternatives. Also notice that another method will be required if you create more than + 256 versions. + + The type 'Client' must also be changed in order to reflect the new format: + + type Client: + client(String name, + String address, + Int32 age). + + + + + --- Step 3: + + Now, we need 'conversion' functions in both directions between 'Client' and + 'HClient'. Presisely, we need the functions: + + (HClient -> Client)update + (Client -> HClient)store + + The function 'update' gets an 'historical' row (hence of type 'HClient') and must + transform it into a row in the current format. In our example, it could be: + + define Client + update + ( + HClient hc + ) = + if hc is + { + version_1(name,addr) then client(name,addr,0), + version_2(name,addr,age) then client(name,addr,age) + }. + + Of course, in the case of a row in the old format (version_1), we need a default value + for the age. We have chosen 0 in the example, but you may choose anything. In any + traditional data base system, when you add a column to a table, you need to fill this + column with a default value, even if this default value is 'NULL' (not filled at all). + + The other function does not require default values in principle, because it is mainly a + conversion between the current version and the latest one (i.e. essentially the same + one): + + define HClient + store + ( + Client c + ) = + if c is client(name,addr,age) then version_2(name,addr,age). + + + The type used for storing the data on the disk is always 'HClient' (hence the name of + the function 'store'), and according to the above function they are stored as + 'version_2(...)'. The type 'Client' is used only internally. This is required for + being able of handling file containing different versions of the tables. + + + + + *** (5) Row identifiers. + + The system generates an identifier for each newly created row in a table. It is + warranted that not two rows in the same table can have the same row identifier. Row + identifiers are of type 'DBRowId($Row)' (an opaque type scheme). + +public type DBRowId($Row):... + + (where '$Row' is the type of rows of the table). It is possible to use it as a + component in the type of rows in another table. This is how you can create 'links' + between tables. + + The type scheme 'DBRowId' should be considered as absolutely opaque. The author + reserves the possibility of modifying this type in the future. Hence, use only the + public interface for manipulating data of this type. + + It would have been possible to define the type 'DBRowId' without the parameter, but in + this case, identifiers corresponding to tables of different types could not be + distinguished by the compiler. The role of the parameter '$Row' is to identify the + type of the table to which the identifier refers. Introducing this parameter enhances + the security because it forbids the use of a row identifier with a table to which it + cannot actually apply. + + + + + *** (6) Adding rows to a table. + + Adding rows to a table is performed by the following function: + +public define Result(DBError,List(DBRowId($Row))) + add_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + List($Row) new_rows + ). + + For example, you may write: + + add_rows(my_data_base,clients,[row1,row2,row3]) + + where 'row1','row2' and 'row3' are of type 'Client'. The function returns (if no error + occurs) the list of the row identifiers of the newly created rows. + + The following variant is more convenient for adding just one row. + +public define Result(DBError,DBRowId($Row)) + add_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row new_row + ). + + + + + *** (7) Getting rows from a table. + + In order to get rows from a table, you have two methods: + + (1) you know the row identifiers of the rows you want, + (2) you want to get all rows satisfying some condition. + + You get rows in the form of: + +public type DBRow($Row): + dbrow(DBRowId($Row) row_id, + Int32 version, + $Row row). + + A datum of type DBRow($Row) contains the identifier of the row, a version number and + the row itself. The version number is 0 when the row is created (by 'add_row' or + 'add_rows' above) and is incremented by 1 each time the row is updated. This may be + used to detect the fact that the row has been modified between two operations. + +public define Result(DBError,DBRow($Row)) + get_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRowId($Row) row_id + ). + + If no error occurs, the function returns the wanted row. The next one can be used for + getting several rows. + +public define Result(DBError,List(DBRow($Row))) + get_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + List(DBRowId($Row)) row_ids + ). + +public define Result(DBError,List(DBRow($Row))) + get_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row -> Bool which + ). + + This function returns the list of all rows from the table which satisfy the given + condition 'which'. + + Now, the test 'which' may also be performed on the complete row: + +public define Result(DBError,List(DBRow($Row))) + get_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRow($Row) -> Bool which + ). + + + + + + *** (8) Testing a number of rows. + + It may be useful to get the number of rows satisfying some condition in a table. It may + also be useful to know if a table contains more rows satisfying a condition than a + given number. + +public define Result(DBError,Int32) + get_number_of_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRow($Row) -> Bool which + ). + + This function returns the number of rows satisfying the condition 'which'. + +public define Result(DBError,Bool) + has_more_rows_than + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRow($Row) -> Bool which, + Int32 n + ). + + This function returns 'ok(true)' if the table has strictly more than 'n' rows satifying + the condition, 'ok(false)' if it has at most 'n' rows satisfying the condition. + + Note: of course, these functions could be simulated using 'get_rows', but they may be + much more efficient, especially the last one when 'n' is much less than the number of + rows satisfying the condition, because it does not need to examine the whole table. + + + + + + *** (9) Updating rows in a table. + + Updating rows in a table is generally the consequence of a previous reading of these + rows. Indeed, interactive programs will first show the row to be updated to a human + user (phase 1). The user is supposed to modify the data by hand. When the data are + modified, they may be put in the table in order to replace the previous values (phase + 2). + + This makes a problem because, the same data may have been modified by another human + user (or in some automatic way) in the meantime (i.e. between phase 1 and phase 2). + Hence, phase 2 should perhaps not be performed if the data have been modified in the + meantime. For this reason, when you want to update one or several rows in a table, you + do not provide the new values, but a function (denoted 'how' below) of type: + + DBRow($Row) -> Maybe($Row) + + This function is supposed to remember the version number of the row got during phase 1. + It receives as its argument the new row as it stands in the data base when phase 2 + begins. It should compare the version number with the one it remembers and decide if + the row must be updated or not. If the row must not be updated, the function 'how' + must return 'failure'. If on the contrary, the row must be updated, it must return the + value 'success(r)', where 'r' is the new value of the row. + + Now, if no error occurs, the 'update_row' function returns a datum of type: + +public type DBUpdateResult($Row): + not_updated (DBRow($Row) the_row), + updated (DBRow($Row) the_row). + + i.e. the result is either 'not_updated(r)' or 'updated(r)'. It is 'not_updated(r)' + when the row has not been updated, i.e. when the 'how' function has returned + 'failure', and it is 'updated(r)' if the row has been updated. In both cases, 'r' is + the new current value of the row as recorded in the table. + + Again, you may just want to update the row(s) the id(s) of which you have at hand, or + update all rows satisfying some condition. + +public define Result(DBError,DBUpdateResult($Row)) + update_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRowId($Row) row_id, + DBRow($Row) -> Maybe($Row) how + ). + +public define Result(DBError,List(DBUpdateResult($Row))) + update_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + List(DBRowId($Row)) row_ids, + DBRow($Row) -> Maybe($Row) how + ). + +public define Result(DBError,List(DBUpdateResult($Row))) + update_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row -> Bool which, + DBRow($Row) -> Maybe($Row) how + ). + + For the last function 'update_rows', only the rows which satify the 'which' condition + are eventually updated. Of course the function returns a list of + 'DBUpdateResult($Row)', one for each row satisfying the 'which' condition. + + + + + *** (10) Deleting rows from a table. + +public define Result(DBError,$Row) + delete_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRowId($Row) row_id + ). + + If no error occurs, the row is deleted from the table, and returned by the function as + 'ok(row)'. You may forget this result, but you may also use it for archiving purpose + for example. + +public define Result(DBError,List($Row)) + delete_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row -> Bool which + ). + + The rows satisfying the condition 'which' are deleted from the table. Of course, the + result, if no error occurs, is the list of the deleted rows. + + + + + + + *** (11) Multi-tables queries. + + The SQL language has an important feature which is the 'SELECT' command. We need + something analogous to the SQL query: + + SELECT column_1 ... column_k + FROM table_1 ... table_n + WHERE condition + + From a purely mathematical viewpoint, the above query means the following. Consider the + tables 'table_1' ... 'table_n' as relations between data types (i.e subsets of + cartesian products of data types, where each row of the table is precisely an element + of the relation, and each column is a data type). Make the cartesian products 'P' of + these relations. Then consider the subset of 'P' of those elements which satisfy the + 'condition'. Finally, apply the canonical projection towards the cartesian product of + the data types represented by 'column_1' ... 'column_k'. This is the result of the + query. + + Of course, the above description, even if probably the simplest possible one from a + theoretical viewpoint, is not efficient from the computational viewpoint. This is + mainly because the cartesian product of the relations 'table_1' ... 'table_n' may be + very big. This big product is first reduced by applying the condition, and further + reduced by the canonical projection. The question is how can we do for computing + without these big intermediate data. + + Usual data bases first optimize the query before it is executed. With Anubis, we cannot + optimize the query, except if it is given in a symbolic form. This is not the way we + have chosen, at least for the time being. Actually, combining the advantages of Anubis + (essentially the security provided by the strong typing mecanism), and the flexibility + of usual data bases could be realized only with a more elaborate version of Anubis, + including meta-programming, and maybe another virtual machine able to optimize on the + fly. All these new features are part of the Anubis2/Paradize project. + + For the time being, we content ourself with the following recursive querying + mecanism. Actually, the recursion is on the number of tables concerned by the query. + +public define List($Out) + query + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_first_table, + DBRow($Row) -> Bool the_first_condition, + (DBRow($Row),$In) -> $Out the_first_selection, + DBRow($Row) -> List($In) the_subquery + ). + + The parameter '$Row' represents the type of the rows of the first table (we assume + there is at least one table concerned by the query). This table (the first table) will + be looked at first, and only the rows satisfying the condition 'the_first_condition' + are considered. For each such row of the first table, a subquery ('the_subquery') is + executed. Notice that the subquery takes that row as an argument. The subquery + concernes only the remaining tables (i.e. all the tables concerned by the original + query, except the first one). It returns a list of results of type 'List($In)'. Now, + the row of the first table is paired to each element of this list, and the projection + 'the_first_projection' is applied. This yields a list of type 'List($Out)', one for + each selected row of the first table. Finally, all these lists are merged together (and + repetitions are removed). + + Well, an example is probably required. Here it is. We consider the tables 'clients', + 'commands' and 'products' , whose types of rows are respectively: 'Client', 'Command' + and 'Product'. It is assumed that the type 'Command' has (among others) the + components: + + client_id of type DBRowId(Client) + product_id of type DBRowId(Product) + + so that to each command (probably only a 'command item') corresponds a client and a + product. Also, the types 'Client' and 'Product' have a component 'name' (of type + String), giving the name of the client or of the product. The type 'Client' also has a + component 'country'. + + We want to get the list of all clients living in France, who have bought the product + named "Yoyo". Here is the query (which returns a list of type 'List(String)'): + + query(my_data_base, + clients, // begin with the table 'clients' + (DBRow(Client) clt) |-> // consider only French clients + country(row(clt)) = "France", + (DBRow(Client) clt, One u) |-> // keep only the name of the client + name(row(clt)), + (DBRow(Client) clt) |-> // the subquery for this client 'clt' + query(my_data_base, + commands, // continue with the table 'commands' + (DBRow(Command) cmd) |-> + row_id(clt) = client_id(row(cmd)), + (DBRow(Command) cmd, One u) |-> u, // do not keep anything from 'commands' + (DBRow(Command) cmd) |-> + query(my_data_base, + products, + (DBRow(Product) prd) |-> + row_id(prd) = product_id(cmd) & + name(row(prd)) = "Yoyo", + (DBRow(Product) prd, One u) |-> u, + (DBRow(Product) prd) |-> unique))) + + Of course, this is more complicated than: + + SELECT clients.name + FROM clients, commands_2005, products + WHERE clients.id = commands_2005.client_id AND + commands_2005.product_id = products.id AND + products.name = "Yoyo" + + but meta-programming could automatically transform the later into the former (in the + future). + + Now, the query may also be more efficient (computed faster) if the tables are put in a + different order. For example, we may consider the following equivalent query (with the + tables in reverse order): + + query(my_data_base, + products, + (DBRow(Product) prd) |-> name(row(prd)) = "Yoyo", + (DBRow(Product) prd, String cltname) |-> cltname, + (DBRow(Product) prd) |-> + query(my_data_base, + commands, + (DBRow(Command) cmd) |-> + row_id(prd) = product_id(row(cmd)), + (DBRow(Command) cmd, String cltname) |-> cltname, + (DBRow(Command) cmd) |-> + query(my_data_base, + clients, + (DBRow(Client) clt) |-> + country(row(clt)) = "France" & + row_id(clt) = client_id(row(cmd)), + (DBRow(Client) clt, One u) |-> name(row(clt)), + (DBRow(Client) clt) |-> unique))) + + This one may be more efficient than the first one. The order into which the tables are + looked up may be important for performances. + + + + + + *** (12) Using it with 'web/making_a_web_site.anubis'. + + You may want to use this data base manager in conjunction with our web site making + method. You should proceed as follows in order to start the data base and the web + site. + + You should not define the actions of your web site at the top level, like this: + + define State + act_do_something + ( + HTTP_Info http_info, + List(Web_arg) lwa, + State previous + )= ... + + What you should do (for each action) is defining a function which constructs the above + one using the data base, like this: + + define (HTTP_Info http_info, + List(Web_arg) lwa, + State previous) -> State + make_act_do_something + ( + DB(MY_DB) my_data_base + ) = + (HTTP_Info http_info, + List(Web_arg) lwa, + State previous) |-> + ... here the body of the function may use the data base ... + + + Now, when defining the list of all actions of your web site, you need to provide the + data base: + + define List(Web_Action(State)) + make_actions + ( + DB(MY_DB) my_data_base + ) = + [ + http_action("do_something", + make_allow_something(my_data_base), + make_act_do_something(my_data_base)), + ...etc... + ]. + + + Finally, when you start the whole stuff, do as follows. First start the data base: + + with my_data_base = (DB(MY_DB))init_db(...), + + Next, construct the list of actions of the web site: + + with actions = make_actions(my_data_base), + + finally, start the web sites: + + start_web_sites + (0, + 80, + 443, + "georges", + [ + make_web_site_description + (common_name, + default_state, + actions, // here the actions constructed above are used (hence the data base) + compute_page, + ... etc...) + ]) + + Of course, if you have several web sites, you may also have several data bases, but the + method is essentially the same one. You may also need several data bases for a single + web site. + + Recall that the function 'compute_page' should perhaps not use the data base, because + otherwise, the page sent to the client may be incoherent with the state saved on the + server's disk. + + + + + + + --- That's all for the public part ! -------------------------------------------------- + + + Row identifiers are just integers, but this may change in the future, because integers + may not be enough. + +public type DBRowId($Row): + dbrowid(Int32 index). + + When a table is loaded, a boolean variable 'changed' indicates if it has to be saved. A + integer variable contains the counter for creating identifiers for new rows. Another + variable contains the list of all loaded rows. + +type DBLoadedTable($Row,$HRow): + table(String table_name, + (DBRow($Row),DBRow($Row)) -> Bool compare, + $Row -> $HRow store, + Var(Bool) changed_v, + Var(Int32) counter_v, + Var(List(DBRow($Row))) rows_v). + + Of course, tables are not saved on disk as 'DBLoadedTable($Row,$HRow)' because variable + types (and functional types for the time being) are not serializable, and also because + data of type '$Row' are not saved on the disk. Tables are saved as data of type + 'DBSavedTable($HRow)'. + +type DBSavedTable($HRow): + myversion_1(Int32 counter, + List(DBRow($HRow)) rows). + + + We will have to compare the rows of a loaded table. The function able to compare loaded + rows must be constructed from the function comparing nude rows. This is the job of the + following 'function maker': + +define (DBRow($Row),DBRow($Row)) -> Bool + dbrow + ( + ($Row,$Row) -> Bool compare + ) = + (DBRow($Row) dbr1, DBRow($Row) dbr2) |-> + if dbr1 is dbrow(id1,v1,r1) then + if dbr2 is dbrow(id2,v2,r2) then + compare(r1,r2). + + + Tables need to be installed (which involves installing all rows). + +read tools/basis.anubis required for 'qsort' + +define DBLoadedTable($Row,$HRow) + install_table + ( + String table_name, + ($Row,$Row) -> Bool compare, + $HRow -> $Row update, + $Row -> $HRow store, + DBSavedTable($HRow) st + ) = + if st is + { + myversion_1(count,rows) then + with db_compare = dbrow(compare), + updated_rows = map((DBRow($HRow) dbr) |-> + if dbr is dbrow(row_id,version,row) + then if row_id is dbrowid(i) + then dbrow(dbrowid(i),version,update(row)), + rows), + table(table_name, + db_compare, + store, + var(false), + var(count), + var(qsort(updated_rows,db_compare))) + }. + + + + Saving a table on the disk (the table also persists in memory). + +define Result(DBError,One) + save_table + ( + String dbdir, + DBLoadedTable($Row,$HRow) t + ) = + if t is table(table_name,compare,store,changed_v,counter_v,rows_v) then + with path = dbdir+"/"+table_name, + if protect save((DBSavedTable($HRow))myversion_1(*counter_v, + map((DBRow($Row) dbr) |-> + if dbr is dbrow(row_id,version,row) then + if row_id is dbrowid(i) then + dbrow(dbrowid(i),version,store(row)), + *rows_v)), + path) is + { + cannot_open_file then error(cannot_open_file(path)), + write_error then error(cannot_write_file(path)) + ok then changed_v <- false; ok(unique) + }. + + + + Tables in the data base are either loaded or not. + +type DBTableState($Row,$HRow): + not_loaded (String table_name, + ($Row,$Row) -> Bool compare, + $HRow -> $Row update, + $Row -> $HRow store), + loaded (DBLoadedTable($Row,$HRow)). + + Now a table is just a variable containing either a non loaded table or a loaded table. + +public type DBTable($Row,$HRow): + db_table(Var(DBTableState($Row,$HRow))). + + + The data base itself is made of the directory path for table files, and the set of + tables (loaded or not). + +public type DB($MY_DB): + db(String directory, + $MY_DB tables). + + +public define DB($MY_DB) + init_db + ( + String data_base_directory, + $MY_DB tables + ) = + forget(make_directory(data_base_directory,default_directory_mode)); + db(data_base_directory,tables). + +public define DBTable($Row,$HRow) + init_dbtable + ( + String table_name, + ($Row,$Row) -> Bool compare, + $HRow -> $Row update, + $Row -> $HRow store + ) = + db_table(var(not_loaded(table_name,compare,update,store))). + + + Given a data base and a table in the form of a destructor of type MY_DB, the next + function returns the table in the form of a loaded table. + +define Result(DBError,DBLoadedTable($Row,$HRow)) + get_table + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + ) = + if the_data_base is db(dbdir,my_db) then + if the_table(my_db) is db_table(table_v) then + if *table_v is + { + not_loaded(table_name,compare,update,store) then + with file_path = dbdir+"/"+table_name, + if (RetrieveResult(DBSavedTable($HRow)))retrieve(file_path) is + { + cannot_find_file then + with new_table = install_table(table_name,compare,update,store,myversion_1(0,[])), + table_v <- loaded(new_table); + ok(new_table), + + read_error then error(file_reading_problem(file_path)), + type_error then error(file_type_problem(file_path)), + ok(t) then + with new_table = install_table(table_name,compare,update,store,t), + table_v <- loaded(new_table); + ok(new_table) + }, + + loaded(t) then + ok(t) + }. + + + Inserting rows. The next function receives two lists (of the same type) which are both + already sorted. It inserts the elements of the first list at the right place in the + second list. + +define List($T) + insert // insert elements of l1 into l2 + ( + List($T) l1, + List($T) l2, + ($T,$T) -> Bool less + ) = + if l1 is + { + [ ] then l2, + [h1 . t1] then + if l2 is + { + [ ] then l1, + [h2 . t2] then + if less(h1,h2) + then [h1 . insert(t1,l2,less)] + else [h2 . insert(l1,t2,less)] + } + }. + + + + Adding rows. The table contains a list of 'DBRow($Row)' which is already sorted. The + new rows arrive as data of type '$Row', and need first to be sorted. Then they are + transformed into 'DBRow($Row)' and inserted in the table. Hence, we need to compare + data of type 'DBRow($Row)'. + + The next function gets a list of nude rows (of type '$Row'), and transforms it into a + list of loaded rows, creating a new identifier for each row. This function is run + 'protected'. + +define DBRow($Row) + load_row + ( + $Row row, + Var(Int32) counter_v + ) = + protect + with c1 = *counter_v, + counter_v <- c1+1; + dbrow(dbrowid(c1),0,row). + +define List(DBRow($Row)) + load_rows + ( + List($Row) rows, + Var(Int32) counter_v + ) = + map(($Row r) |-> load_row(r,counter_v), + rows). + + + Finally, in order to add rows to a table, we have to load them (using 'load_rows' + above), to sort them, and to insert them in the table. The table is subsequently + saved. + +public define Result(DBError,List(DBRowId($Row))) + add_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + List($Row) new_rows + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then protect + if loaded_table is table(table_name,compare,store,changed_v,counter_v,rows_v) then + with new_loaded_rows = qsort(load_rows(new_rows,counter_v),compare), + new_table_rows = insert(new_loaded_rows,*rows_v,compare), + changed_v <- true; + rows_v <- new_table_rows; + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(map(row_id,new_loaded_rows)) + } + }. + + +public define Result(DBError,DBRowId($Row)) + add_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row new_row + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then protect + if loaded_table is table(table_name,compare,store,changed_v,counter_v,rows_v) then + with new_loaded_row = load_row(new_row,counter_v), + new_table_rows = insert([new_loaded_row],*rows_v,compare), + changed_v <- true; + rows_v <- new_table_rows; + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(row_id(new_loaded_row)) + } + }. + + + + + + Getting a row by its identifier. + +define Result(DBError,DBRow($Row)) + get_row + ( + List(DBRow($Row)) rows, + DBRowId($Row) row_id + ) = + if rows is + { + [ ] then error(record_not_found), + [row1 . others] then if row1 is dbrow(id,ver,row) then + if row_id = id + then ok(row1) + else get_row(others,row_id) + }. + + +public define Result(DBError,DBRow($Row)) + get_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRowId($Row) row_id + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then + get_row(*rows_v,row_id) + }. + + + + Getting rows by their identifiers. + + +define List($T) + remove + ( + $T x, + List($T) l + ) = + if l is + { + [ ] then [ ], + [h . t] then + if x = h + then t + else [h . remove(x,t)] + }. + +define Result(DBError,List(DBRow($Row))) + get_rows + ( + List(DBRow($Row)) rows, + List(DBRowId($Row)) row_ids, + List(DBRow($Row)) so_far + ) = + if rows is + { + [ ] then ok(reverse(so_far)), + [row1 . others] then + if row_ids is + { + [ ] then ok(reverse(so_far)), + [_._] then + if row1 is dbrow(id,_,_) then + if member(row_ids,id) + then get_rows(others,remove(id,row_ids),[row1 . so_far]) + else get_rows(others,row_ids,so_far) + } + }. + +public define Result(DBError,List(DBRow($Row))) + get_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + List(DBRowId($Row)) row_ids + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then + get_rows(*rows_v,row_ids,[]) + }. + + + + + Getting rows satifying a condition. + + + +define Result(DBError,List(DBRow($Row))) + get_rows + ( + List(DBRow($Row)) rows, + DBRow($Row) -> Bool which, + List(DBRow($Row)) so_far + ) = + if rows is + { + [ ] then ok(reverse(so_far)), + [row1 . others] then + if which(row1) + then get_rows(others,which,[row1 . so_far]) + else get_rows(others,which,so_far) + }. + +public define Result(DBError,List(DBRow($Row))) + get_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRow($Row) -> Bool which + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then + get_rows(*rows_v,which,[]) + }. + + +public define Result(DBError,List(DBRow($Row))) + get_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row -> Bool which + ) = + get_rows(the_data_base,the_table, + (DBRow($Row) r) |-> which(row(r))). + + + + Getting a number of rows. + +define Result(DBError,Int32) + count_rows + ( + List(DBRow($Row)) rows, + DBRow($Row) -> Bool which, + Int32 so_far + ) = + if rows is + { + [ ] then ok(so_far), + [h . t] then + if which(h) + then count_rows(t,which,so_far+1) + else count_rows(t,which,so_far) + }. + +public define Result(DBError,Int32) + get_number_of_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRow($Row) -> Bool which + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then + count_rows(*rows_v,which,0) + }. + + +define Result(DBError,Bool) + has_more_rows_than + ( + List(DBRow($Row)) rows, + DBRow($Row) -> Bool which, + Int32 n + ) = + if rows is + { + [ ] then ok(false), + [h . t] then + if which(h) + then if n =< 0 + then ok(true) + else has_more_rows_than(t,which,n-1) + else has_more_rows_than(t,which,n) + }. + +public define Result(DBError,Bool) + has_more_rows_than + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRow($Row) -> Bool which, + Int32 n + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then + has_more_rows_than(*rows_v,which,n) + }. + + + + + Updating rows. Rows which satisfy the 'which' condition are first extracted from the + table and updated by 'how'. Then the list of updated rows is sorted, and reinserted + into the table. Subsequently, the table is saved. + + The next function receives a list and a test applicable to elements of that list. It + separates the list into the list of those elements which satisfy the test, and the list + of those element which do not satisfy the test. + +define ( + List($T), // those satisfying the test + List($T) // those not satisfying the test + ) + separ_list + ( + List($T) l, + $T -> Bool test + ) = + if l is + { + [ ] then ([],[]), + [h . t] then if separ_list(t,test) is (others_yes,others_no) then + if test(h) + then ([h . others_yes],others_no) + else (others_yes,[h . others_no]) + }. + + The same one for separating only one element. + +define ( + Maybe($T), // separated element + List($T) // other elements + ) + separ_element + ( + List($T) l, + $T -> Bool test + ) = + if l is + { + [ ] then (failure,[]), + [h . t] then + if test(h) + then (success(h),t) + else if separ_element(t,test) is (mb_e,t1) then (mb_e,[h . t1]) + }. + + + + +public define Result(DBError,DBUpdateResult($Row)) + update_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRowId($Row) row_id, + DBRow($Row) -> Maybe($Row) how + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then protect + changed_v <- true; + if separ_element(*rows_v,(DBRow($Row) r) |-> + if r is dbrow(rid,ver,row) then rid = row_id) + is (mb_extracted_row,other_rows) then + if mb_extracted_row is + { + failure then error(record_not_found), + success(idrow) then + if how(idrow) is + { + failure then ok(not_updated(idrow)), + success(new_row) then + with new_idrow = dbrow(row_id,version(idrow)+1,new_row), + rows_v <- insert([new_idrow],other_rows,compare); + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(updated(new_idrow)) + } + } + } + }. + + +public define Result(DBError,List(DBUpdateResult($Row))) + update_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + List(DBRowId($Row)) row_ids, + DBRow($Row) -> Maybe($Row) how + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then protect + changed_v <- true; + if separ_list(*rows_v,(DBRow($Row) r) |-> + if r is dbrow(row_id,ver,row) then member(row_ids,row_id)) + is (extracted_rows,other_rows) then + with updated_rows = qsort(map((DBRow($Row) r) |-> + if how(r) is + { + failure then not_updated(r) + success(new_r) then updated(dbrow(row_id(r),version(r)+1,new_r)) + }, + extracted_rows), + (DBUpdateResult($Row) ur1, DBUpdateResult($Row) ur2) |-> + compare(the_row(ur1),the_row(ur2))), + rows_v <- insert(map(the_row,updated_rows),other_rows,compare); + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(updated_rows) + } + }. + + +public define Result(DBError,List(DBUpdateResult($Row))) + update_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row -> Bool which, + DBRow($Row) -> Maybe($Row) how + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then protect + changed_v <- true; + if separ_list(*rows_v,(DBRow($Row) r) |-> + if r is dbrow(row_id,ver,row) then which(row)) + is (extracted_rows,other_rows) then + with updated_rows = qsort(map((DBRow($Row) r) |-> + if how(r) is + { + failure then not_updated(r), + success(new_r) then updated(dbrow(row_id(r),version(r)+1,new_r)) + }, + extracted_rows), + (DBUpdateResult($Row) ur1, DBUpdateResult($Row) ur2) |-> + compare(the_row(ur1),the_row(ur2))), + rows_v <- insert(map(the_row,updated_rows),other_rows,compare); + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(updated_rows) + } + }. + + + + Deleting rows. + + +define Result(DBError, + ($Row, // the deleted row + List(DBRow($Row))) // the remaining rows + ) + delete_row + ( + List(DBRow($Row)) l, + DBRowId($Row) row_id + ) = + if l is + { + [ ] then error(record_not_found), + [h . t] then if h is dbrow(i,v,r) then + if i = row_id + then ok((row(h),t)) + else if delete_row(t,row_id) is + { + error(msg) then error(msg), + ok(result1) then if result1 is (deleted,remaining) then + ok((deleted,[h . remaining])) + } + }. + +public define Result(DBError,$Row) + delete_row + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + DBRowId($Row) row_id + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then protect + if delete_row(*rows_v,row_id) is + { + error(msg) then error(msg), + ok(d_rows) then if d_rows is (deleted,rows) then + changed_v <- true; + rows_v <- rows; + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(deleted) + } + } + }. + +define (List($Row),List(DBRow($Row))) // (deleted rows, remaining rows) + delete_rows + ( + List(DBRow($Row)) rows, + $Row -> Bool which + ) = + if rows is + { + [ ] then ([ ],[ ]), + [row1 . others] then + if delete_rows(others,which) is (deleted_others,remaining_others) then + if row1 is dbrow(id,ver,row) then + if which(row) + then ([row . deleted_others],remaining_others) + else (deleted_others,[row1 . remaining_others]) + }. + +public define Result(DBError,List($Row)) + delete_rows + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_table, + $Row -> Bool which + ) = + if get_table(the_data_base,the_table) is + { + error(msg) then error(msg), + ok(loaded_table) then if loaded_table is + table(table_name,compare,store,changed_v,counter_v,rows_v) then protect + if delete_rows(*rows_v,which) is (deleted,remaining) then + changed_v <- true; + rows_v <- remaining; + if save_table(directory(the_data_base),loaded_table) is + { + error(msg) then error(msg), + ok(_) then ok(deleted) + } + }. + + + + + + + Multi-tables queries. + + +define List($Out) + remove_repetitions + ( + List($Out) l + ) = + if l is + { + [ ] then [ ], + [h . t] then + if member(t,h) + then remove_repetitions(t) + else [h . remove_repetitions(t)] + }. + + +define List($T) + flatten + ( + List(List($T)) l + ) = + if l is + { + [ ] then [ ], + [h . t] then h + flatten(t) + }. + +public define List($Out) + query + ( + DB($MY_DB) the_data_base, + $MY_DB -> DBTable($Row,$HRow) the_first_table, + DBRow($Row) -> Bool the_first_condition, + (DBRow($Row),$In) -> $Out the_first_selection, + DBRow($Row) -> List($In) the_subquery + ) = + if get_rows(the_data_base,the_first_table,the_first_condition) is + { + error(msg) then [ ], + ok(rows) then + remove_repetitions(flatten( + map((DBRow($Row) row) |-> + map(($In i) |-> the_first_selection(row,i),the_subquery(row)), + rows))) + }. + + + + + + diff --git a/anubis_dev/library/tools/sdbms2.anubis b/anubis_dev/library/tools/sdbms2.anubis index 6cb7702..0232d1a 100644 --- a/anubis_dev/library/tools/sdbms2.anubis +++ b/anubis_dev/library/tools/sdbms2.anubis @@ -49,9 +49,9 @@ *** (2) Data base and table structure. *** (2.1) Data base roots. - *** (2.2) Tables. - *** (2.3) Indexing. - *** (2.4) Errors. + *** (2.2) Errors and informations. + *** (2.3) Tables. + *** (2.4) Indexing. *** (2.5) Initializing a table. *** (2.6) Initializing an index. *** (2.7) Data bases. @@ -118,11 +118,11 @@ public type DB2Info:... (see below) In order to create a data base root, use the following tool: public define DB2Root - root + make_root ( String directory, // where the files will be located Int32 kilo_bytes, // maximal number of kilo bytes in memory - Int32 delay, // maximal synchronization delay between memory and files + Int32 delay, // maximal synchronisation delay between memory and files Var(Bool) shutdown_v, // shutdown flag DB2Error -> One warn, // warning function for administrator DB2Info -> One inform // information function for administrator @@ -131,19 +131,19 @@ public define DB2Root Create as many data base roots as you have data bases in your project. Choose distinct directories for all data base roots. - 'kilo_bytes' determines the maximal number of kilo bytes the data base may occupy in + 'kilo_bytes' determines the maximal number of kilobytes the data base may occupy in memory (RAM). When the memory occupied by the data base becomes greater than this value, those chunks and tables which have been used the less recently are unloaded from memory. 'delay' is the maximal number of seconds which may elapse between a modification of a - table in the memory and the synchronization with the data on the disk. + table in the memory and the synchronisation with the data on the disk. The dynamic variable 'shutdown_v' is the 'shutdown flag'. When the tables are initialized this variable receives the value 'false'. As soon as you put the value 'true' in this variable, all the table which have been initialized with this root are shutdown, which means that no more utilization command may work, and that the data on - the disk are synchronized with the content of the memory. You should probably create + the disk are synchronised with the content of the memory. You should probably create only one such variable for all the data bases of your program, i.e. even if you create several data base roots, you should provide the same shutdown flag to all these roots. Nevertheless, if you want to shutdown your data bases at distinct times, use @@ -184,16 +184,49 @@ public type DB2Info: loading_chunk (String path), unloading_chunk (String path), beginning_chunk_synchronisation (String path), - ending_chunk_synchronisation (String path), - beginning_table_move (String path, Int32 old_bits, Int32 new_bits), - ending_table_move (String path, Int32 old_bits, Int32 new_bits). + ending_chunk_synchronisation (String path). Data of these types are transmitted to the functions 'warn' and 'inform', that you must provide when you create your data base root. + + For your convenience we provide the following formating functions transforming data of + type DB2Error and DB2Info into English sentences: +public define String + en_format + ( + DB2Error e + ) = + if e is + { + file_not_found(p) then "File not found: '"+p+"'", + file_reading_problem(p) then "Cannot read file: '"+p+"'", + file_type_problem(p) then "Incompatible type: '"+p+"'", + cannot_open_file(p) then "Cannot open file: '"+p+"'", + cannot_write_file(p) then "Cannot write into file: '"+p+"'", + cannot_find_index(d,t,i) then "Cannot find index: '"+d+"/t_"+t+"["+i+"]" + }. +public define String + en_format + ( + DB2Info i + ) = + if i is + { + creating_table(p) then "Creating table: '"+p+"'", + loading_table(p) then "Loading table: '"+p+"'", + unloading_table(p) then "Unloading table: '"+p+"'", + beginning_table_synchronisation(p) then "Beginning table synchronisation: '"+p+"'", + ending_table_synchronisation(p) then "Ending table synchronisation: '"+p+"'", + creating_chunk(p) then "Creating chunk: '"+p+"'", + loading_chunk(p) then "Loading chunk: '"+p+"'", + unloading_chunk(p) then "Unloading chunk: '"+p+"'", + beginning_chunk_synchronisation(p) then "Beginning chunk synchronisation: '"+p+"'", + ending_chunk_synchronisation(p) then "Ending chunk synchronisation: '"+p+"'" + }. *** (2.3) Tables. @@ -749,41 +782,50 @@ public define List(Result(DB2Error,$Row)) *** [1.1] Chunks. *** [1.2] Secondary indexes. *** [1.3] Tables. - *** [2] Initializing. *** [2.1] Roots. *** [2.2] Tables. *** [2.3] Indexes. - *** [3] Tools for getting tables, chunks and indexes. + *** [3.5] Loading a secondary index. *** [3.1] Loading a table. *** [3.2] Getting a table. *** [3.3] Loading a chunk. *** [3.4] Getting a chunk. - *** [3.5] Loading a secondary index. *** [3.6] Getting a secondary index. - *** [4] Adding rows. - *** [4.1] Computing the size of a row. *** [4.2] Adding a set of rows. *** [4.3] The public tool 'add_rows'. *** [4.4] The public tool 'add_row'. - *** [5] Row selection. - *** [6] Acting on rows. *** [6.1] Acting on selected rows of a chunk. - *** [6.2] Acting on a chunk. - *** [6.3] Acting by condition. - *** [6.4] Acting by primary index. - *** [6.5] Acting by secondary index. - + *** [6.2] Waiting for the end of chunk synchronisation. + *** [6.3] Acting on a chunk. + *** [6.4] Acting by condition. + *** [6.5] Acting by primary index. + *** [6.6] Acting by secondary index. + *** [6.7] Waiting for the end of table synchronisation. + *** [6.8] Acting in general. *** [7] Utilization commands. *** [7.1] 'get_rows'. *** [7.2] 'update_rows'. *** [7.3] 'delete_rows'. - - *** [8] Synchronizing memory with disk. + *** [8] Synchronising memory with disk. + *** [8.1] Synchronising a table file. + *** [8.2] Synchronising chunk files. + *** [8.2.1] One chunk. + *** [8.2.2] One 'new' chunks (moving table). + *** [8.2.2.1] 'Increasing' case. + *** [8.2.2.2] 'Decreasing' case. + *** [8.2.2.3] Both cases. + *** [8.2.3] All chunks. + *** [8.4] Synchronising everything. + *** [8.5] Asking for delayed synchronisation. + *** [9] Changing the number of bits of hash. + *** [9.1] Hashing. + *** [9.2] Deciding to change the number of bits of hash. + *** [9.3] Performing a change. --------------------------------------------------------------------------------------- @@ -834,7 +876,7 @@ type Chunk($Row): because, when memory becomes low, we need to unload some chunks. We unload those chunks which have not been used since the longuest time. - The 'changed_v' flag means when 'true' that the chunk is not synchronized with its file + The 'changed_v' flag means when 'true' that the chunk is not synchronised with its file on the disk. The flag 'transfered_v' is used when the table is moving (see below). Of course, the component 'rows_v' contains the list of all rows in this chunk. Normally a very short list. @@ -1213,7 +1255,7 @@ define Result(DB2Error,LoadedTable($Row,$HRow)) store, locked, var(unchanged), // changed_v - 0, // bits_of_hash + 10, // bits_of_hash mvar(1,not_loaded), // only 1 chunk index_name(primary_index), hash_row(primary_index), @@ -1300,7 +1342,7 @@ define Result(DB2Error,LoadedChunk($Row)) cannot_find_file then with c = (LoadedChunk($Row))lchunk( var(now), // last used - var(changed), // not synchronized with disk + var(changed), // not synchronised with disk var(false), // transfered var([])), // rows chunks(hash) <- loaded(c); @@ -1315,7 +1357,7 @@ define Result(DB2Error,LoadedChunk($Row)) ok(cf) then if cf is chunk(rows) then with c = (LoadedChunk($Row))lchunk( var(now), // last used - var(unchanged), // synchronized with disk + var(unchanged), // synchronised with disk var(false), // transfered var(map(update,rows))), chunks(hash) <- loaded(c); @@ -1480,13 +1522,19 @@ public define Result(DB2Error,One) *** [5] Row selection. + + Rows in a table may be selected (regardless of the type of action) via several + different methods. -public type DB2Select($Row): - cond($Row -> Bool which), - prim(ByteArray serialized_model), - secd(String secondary_index_name, +public type DB2Select($Row): // an opaque type + cond($Row -> Bool which), // by condition + prim(ByteArray serialized_model), // by primary index + secd(String secondary_index_name, // by secondary index ByteArray serialized_model). + + Interface constructors for this opaque type. + public define DB2Select($Row) condition ( @@ -1495,6 +1543,9 @@ public define DB2Select($Row) cond(which). + Of course, the types $Primary and $Secondary must disappear from the selection + method. This is why we serialize the given model. + public define DB2Select($Row) primary ( @@ -1515,6 +1566,7 @@ public define DB2Select($Row) + *** [6] Acting on rows. We need to be able to act in several different ways on a set of rows, selected by a @@ -1525,16 +1577,18 @@ public define DB2Select($Row) - deleting rows. - The type below records what can happen to a row: + The type below records what can happen to a row after the action is performed: type NewRow($Row): - deleted, - unchanged, - changed($Row new_row). + deleted, // the row has been deleted + unchanged, // the row did not change + changed($Row new_row). // the value of the row has been changed, + // and here is the new value + The function which acts on a row has type: - $Row -> (NewRow($Row),Maybe($Result)) + $Row -> (NewRow($Row),Maybe($Result)) regardless of the sort of action performed. It returns eventually a new row (this is considered as a change of value). @@ -1545,9 +1599,19 @@ type NewRow($Row): define One ask_for_delayed_synchronisation // defined below in this file ( - LoadedTable($Row,$HRow) the_table, + LoadedTable($Row,$HRow) the_table ). +define One + ask_for_delayed_synchronisation // defined below in this file + ( + LoadedChunk($Row) the_chunk + ). + + This function starts a virtual machine for delayed synchronisation, except if one is + already started. + + @@ -1603,8 +1667,30 @@ define (List($Row), // new rows of chunk + *** [6.2] Waiting for the end of chunk synchronisation. - *** [6.2] Acting on a chunk. + The next 'waiting' function is used to forbid operation on a chunk while it is + synchronising. + +define One + wait_for_end_of_synchronisation + ( + Var(ChunkSynState) st_v + ) = + if *st_v is + { + unchanged then unique, + changed then unique, + synchronising then + checking every 1 millisecond, + wait for *st_v /= synchronising then + unique + }. + + + + + *** [6.3] Acting on a chunk. This is the interface to the previous function. We are given a chunk and an action. The chunk is updated. @@ -1616,40 +1702,21 @@ define List($Result) LoadedChunk($Row) c, $Row -> (NewRow($Row),Maybe($Result)) act ) = + protect if c is lchunk(lu_v,st_v,tr_v,rows_v) then - wait_for_end_of_synchronization(st_v); - if act_on_chunk_rows(*rows_v,act) is (new_rows,result,changed) then + wait_for_end_of_synchronisation(st_v); + if act_on_chunk_rows(*rows_v,act) is (new_rows,result,ch) then rows_v <- new_rows; lu_v <- now; + (if ch then st_v <- changed else unique); + result. - - if changed - then - ( - - ) - else - ( - ) - if *st_v is - { - unchanged then - changed then - synchronizing then - }; - result. - - - then unique else - (ch_v <- changed); - ask_for_delayed_synchronisation(the_table)); - result. - + - *** [6.3] Acting by condition. + *** [6.4] Acting by condition. We want to act on all rows of a table (actually we apply 'act' to all rows, but 'act' may not select some rows). We have to work on all chunks. Hence, the function is a loop @@ -1708,7 +1775,7 @@ define List(Result(DB2Error,$Result)) - *** [6.4] Acting by primary index. + *** [6.5] Acting by primary index. We are given the serialization 'serialized_model' of the primary datum to be searched for. From this serialization and the number of bits of hash, we compute the hash of the @@ -1741,7 +1808,7 @@ define List(Result(DB2Error,$Result)) - *** [6.5] Acting by secondary index. + *** [6.6] Acting by secondary index. We are again given a serialization of a secondary datum to be searched for. We first hash this datum so as to get a secondary hash 's_hash'. We are also given the name of @@ -1788,9 +1855,9 @@ define List(Result(DB2Error,$Result)) - *** [6.7] Waiting for the end of the synchronisation. + *** [6.7] Waiting for the end of table synchronisation. - The newt 'waiting' function is used to forbid operations on a table while it is + The next 'waiting' function is used to forbid operations on a table while it is synchronising. define One @@ -1853,6 +1920,14 @@ define List(Result(DB2Error,$Result)) + + + + + + + + *** [7] Utilization commands. @@ -1947,6 +2022,7 @@ public define List(Result(DB2Error,$Row)) *** [8.1] Synchronising a table file. + Getting the list of all entries from a multiple variable. define List($T) to_list @@ -1999,10 +2075,11 @@ define One *** [8.2] Synchronising chunk files. - - + *** [8.2.1] One chunk. + Synchronising a single chunk file. + define One synchronise_chunk_file ( -- libgit2 0.21.4