diff --git a/harbour/ChangeLog b/harbour/ChangeLog index d4ae0b707a..261aa55b8a 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -17,6 +17,33 @@ past entries belonging to these authors: Viktor Szakats. */ +2009-05-15 12:36 UTC+0200 Viktor Szakats (harbour.01 syenar hu) + * utils/hbmk2/hbmk2.prg + % Changed default way of updating .po files. New method supports + incremental updates, thus much faster than previous solution. + As a side-effect, removed from source and changed translatable + strings won't automatically disappear from .po files. + This new method is also much friendlier to version control + systems, as .po entry order won't change on each minor update + (and generate a huge commit diff). + + Added -rebuildpo option to activate a complete rebuild of .po + files, clearing all no more existing entries. This operation + will take more time, will create big diffs and it's recommended + to be done when doing a complete rebuild (maybe this will + be automatically enforced later on). + ; With this, hbmk2 seems feature complete. (maybe C++/C mode + switching could be added yet, it's on the TODO list) + + * bin/hb-func.sh + * Addition of libs= entry in hbmk.cfg file will now be + initiated by setting envvar HB_USER_LIBS_DEF (was HB_USER_LIBS), + to avoid potential binary build problems where locally + set HB_USER_LIBS envvar could create an unusuable (on other + systems) builds (hbmk2 setup). + + + contrib/rddsql/readme.txt + + Added readme (posted on the list). Work of Mindaugas. + 2009-05-15 12:03 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/contrib/rddads/ads1.c ! fixed typo in recent modification which caused that character fields diff --git a/harbour/bin/hb-func.sh b/harbour/bin/hb-func.sh index bd85a0fa44..711d025b05 100755 --- a/harbour/bin/hb-func.sh +++ b/harbour/bin/hb-func.sh @@ -252,8 +252,8 @@ mk_hbtools() if [ -n "${CC_HB_USER_LDFLAGS}" ]; then echo "ldflags=${CC_HB_USER_LDFLAGS}">> ${hb_hbmkcfg} fi - if [ -n "${HB_USER_LIBS}" ]; then - echo "libs=${HB_USER_LIBS}">> ${hb_hbmkcfg} + if [ -n "${HB_USER_LIBS_DEF}" ]; then + echo "libs=${HB_USER_LIBS_DEF}">> ${hb_hbmkcfg} fi if [ "${HB_GPM_MOUSE}" = "yes" ]; then echo "libs=gpm">> ${hb_hbmkcfg} diff --git a/harbour/contrib/rddsql/readme.txt b/harbour/contrib/rddsql/readme.txt new file mode 100644 index 0000000000..d91e1b5459 --- /dev/null +++ b/harbour/contrib/rddsql/readme.txt @@ -0,0 +1,133 @@ +/* + * $Id$ + */ + + Simple SQL Interface for Harbour + + + +1. Introduction + + Simple SQL interface implements accessing SQL query result via RDD +interface. It is not intended to be replacement for "transparent" move of +DBFCDX application to SQL world. + + I want to discuss this in more detail. Many current RDDs for SQL servers +(ex. SQLRDD from xHarbour.com) tries to make a feeling you are working with +DBF file, but not with SQL database. SQL server does not support many +features, ex. RECNO(), deleted flag, file locks, record locks. These RDDs +are emulating these features to make feeling of DBF. DELETED() function is +emulated by creating additional table columns to store delete flag. Some +"hidden system" tables are used to register locking operations and emulate +record and file locks in DBF style. The idea of SQL query is also lost. If +you do a simple loop + + DBUSEAREA(, "select * from my_table") + DO WHILE ! EOF() + somefunc( FIELD->some_sql_field ) + DBSKIP() + ENDDO + +RDD usualy will read SQL rows in portions, let's say 100 records per query. +So, hidden queries are generated. If you are using indexes these queries +are really complicated. Let's have index on FIELD1 + STR(FIELD2). A seek to +value cValue1 + STR(nValue2) will generate a query like: + + SELECT * FROM my_table + WHERE (FIELD1 == cValue1 and FIELD2 >= nValue2) or FIELD1 > cValue1 + ORDER BY FIELD1, FIELD2, _RECNO + LIMIT 100 + +After evaluation of first 100 cached records, next query will be generated: + + SELECT * FROM my_table + WHERE (FIELD1 == cLastField1 and FIELD2 == nLastValue2 and _RECNO > nLastRecno) or + (FIELD1 == cLastField1 and FIELD2 > nLastValue2) or + FIELD1 > cLastValue1 + ORDER BY FIELD1, FIELD2, _RECNO + LIMIT 100 + +To optimize these queries the SQL index expresion should be +"FIELD1,FIELD2,_RECNO", but not "FIELD1,FIELD2" as written in INDEX ON +command. + + "Simple SQL interface" is too long to repeat every time I want to +address this library. I'll also use acronym "SSI" to address it. + + The idea of SSI is different. It does not make hidden queries. All +queries should be made explicitly by programmer. SSI gives access to query +result via RDD interface, it does not tries to emulate DBF and be +"plug-and-play" solution for DBF to SQL migration. If you do + + DBUSEAREA(, "select * from my_table") + +all query (it could contain millions of records!) will be cached. + + The features of SSI approach are: + +- It's possible to access SQL database of other applications. Other + applications usualy does not follow agreement of "plug-and-play" SQL drivers + about additional DELETED column, _RECNO in the end of index expression, etc. + Access of SQL database of other applications is sometimes not possible. + +- It's query oriented. That means a simple DO WHILE ! EOF() loop will iterate + each records once and only once. This is not true for "plug-and-play" SQL + drivers, if indexing is used. Just like in the case of loop over DBF file. + It is not guaranteed that all records are included! Yes! If key value of the + first record in index is changed to be the last record in index during the + phase of record processing, DO WHILE ! EOF() loop will iterate only this + single records even if the database contains millions of records. Your sould + do FLOCK() on DBF to guarantee the records are not changed. Do you use FLOCK() + before readonly DO WHILE ! EOF() loops? :) + + + +2. Architecture + + + +-------------+ + | | + | SQLMIX RDD | + | | + +-------------+ + | ^ + V | + +-------------+ +---------+ + | |--->| | + | SQLBASE RDD | | SDD | + | |<---| | + +-------------+ +---------+ + + + SQLBASE RDD implements basic functionality for accessing SQL query result +via RDD interface. This RDD could be used, if indexing of query result is not +necessary or all indexing is done by SQL server (by using ORDER BY clause). + + SQLMIX RDD implements indexing of query result. This indexing is not +related to SQL server ORDER BY clause. SQLMIX do indexing of the query on the +client side. + + SDD is acronym for Sql Database Driver. RDD is used to implement access +of different database formats like DBF, SDF, etc. SDD is used to implement +access of different SQL databases. Every SQL server (MySQL, PostgreSQL, etc.) +has a corresponding SDD. SDD driver implements a specific part of data +exchange interface between SQLBASE and SQL server. + + A few additional functions are also implemented, ex. HB_SQLCONNECT(). +Usualy these functions are just a shorter version of corresponding RDDINFO() +call. + + + +3. Modifying database + + SSI presents a query result via RDD interface and generates no hidden +SQL queries. So, how database can be changed? Does DBAPPEND() and FIELDPUT() +works, or is it readonly SQL interface? + DBAPPEND(), FIELDPUT() and other similiar functions work on cached query +result, i.e. query can be appended by new rows and field values can be +changed, but SQL database is not changed. DBCREATE() function can also be +used to create an "empty query result" but no table is created on SQL server. +So, SSI can also be used as implementation of "array RDD". + The programmer must call SQL command explicitly to modify SQL tables. +SSI provides a method to detect which cached rows was changed or appended. diff --git a/harbour/utils/hbmk2/hbmk2.prg b/harbour/utils/hbmk2/hbmk2.prg index 00b6f31b23..b9ad83318f 100644 --- a/harbour/utils/hbmk2/hbmk2.prg +++ b/harbour/utils/hbmk2/hbmk2.prg @@ -35,7 +35,7 @@ * bash script with similar purpose for gcc family. * entry point override method and detection code for gcc. * rtlink/blinker link script parsers. - * POTMerge(), LoadPOTFiles(), LoadPOTFilesAsHash(), GenHbl() and AutoTrans(). + * POTMerge(), LoadPOTFilesAsHash(), GenHbl() and AutoTrans(). * (with local modifications by hbmk author) * * See COPYING for licensing terms. @@ -95,7 +95,6 @@ /* TODO: Further clean hbmk context var usage (hbmk2 scope, project scope, adding rest of variables). */ /* TODO: Finish C++/C mode selection. */ -/* TODO: Incremental .pot to .po merge. */ /* TODO: Add a way to fallback to stop if required headers couldn't be found. This needs a way to spec what key headers to look for. */ @@ -183,21 +182,22 @@ REQUEST hbmk_KEYW #define _HBMK_nCOMPR 37 #define _HBMK_lRUN 38 #define _HBMK_lINC 39 +#define _HBMK_lREBUILDPO 40 -#define _HBMK_aPO 40 -#define _HBMK_cHBL 41 -#define _HBMK_aLNG 42 -#define _HBMK_cPO 43 +#define _HBMK_aPO 41 +#define _HBMK_cHBL 42 +#define _HBMK_aLNG 43 +#define _HBMK_cPO 44 -#define _HBMK_lDEBUGTIME 44 -#define _HBMK_lDEBUGINC 45 -#define _HBMK_lDEBUGSTUB 46 -#define _HBMK_lDEBUGI18N 47 +#define _HBMK_lDEBUGTIME 45 +#define _HBMK_lDEBUGINC 46 +#define _HBMK_lDEBUGSTUB 47 +#define _HBMK_lDEBUGI18N 48 -#define _HBMK_cCCPATH 48 -#define _HBMK_cCCPREFIX 49 +#define _HBMK_cCCPATH 49 +#define _HBMK_cCCPREFIX 50 -#define _HBMK_MAX_ 49 +#define _HBMK_MAX_ 50 PROCEDURE Main( ... ) LOCAL aArgs := hb_AParams() @@ -445,6 +445,7 @@ FUNCTION hbmk( aArgs ) hbmk[ _HBMK_nCOMPR ] := _COMPR_OFF hbmk[ _HBMK_lRUN ] := .F. hbmk[ _HBMK_lINC ] := .F. + hbmk[ _HBMK_lREBUILDPO ] := .F. hbmk[ _HBMK_lDEBUGTIME ] := .F. hbmk[ _HBMK_lDEBUGINC ] := .F. @@ -1099,6 +1100,7 @@ FUNCTION hbmk( aArgs ) CASE cParamL == "-beep-" .OR. ; cParamL == "-nobeep" ; s_lBEEP := .F. CASE cParamL == "-rebuild" ; hbmk[ _HBMK_lINC ] := .T. ; hbmk[ _HBMK_lREBUILD ] := .T. + CASE cParamL == "-rebuildpo" ; hbmk[ _HBMK_lREBUILDPO ] := .T. CASE cParamL == "-clean" ; hbmk[ _HBMK_lINC ] := .T. ; s_lCLEAN := .T. CASE cParamL == "-inc" ; hbmk[ _HBMK_lINC ] := .T. CASE cParamL == "-inc-" .OR. ; @@ -2873,8 +2875,14 @@ FUNCTION hbmk( aArgs ) s_aRESSRC_TODO := s_aRESSRC ENDIF - IF ! Empty( hbmk[ _HBMK_cPO ] ) .AND. ! Empty( s_aPRG ) .AND. Len( s_aPRG_TODO ) > 0 - MakePO( hbmk, ListDirExt( s_aPRG, cWorkDir, ".pot" ) ) + IF hbmk[ _HBMK_lREBUILDPO ] + IF ! Empty( hbmk[ _HBMK_cPO ] ) .AND. ! Empty( s_aPRG ) + RebuildPO( hbmk, ListDirExt( s_aPRG, cWorkDir, ".pot" ) ) + ENDIF + ELSE + IF ! Empty( hbmk[ _HBMK_cPO ] ) .AND. Len( s_aPRG_TODO ) > 0 + UpdatePO( hbmk, ListDirExt( s_aPRG_TODO, cWorkDir, ".pot" ) ) + ENDIF ENDIF IF Len( hbmk[ _HBMK_aPO ] ) > 0 .AND. hbmk[ _HBMK_cHBL ] != NIL .AND. ! s_lCLEAN @@ -5081,9 +5089,9 @@ STATIC FUNCTION rtlnk_process( hbmk, cCommands, cFileOut, aFileList, aLibList, ; RETURN .T. -/* .hbl generation */ +/* .po generation */ -STATIC PROCEDURE MakePO( hbmk, aPOTIN ) +STATIC PROCEDURE RebuildPO( hbmk, aPOTIN ) LOCAL cLNG LOCAL fhnd LOCAL cPOTemp @@ -5098,10 +5106,10 @@ STATIC PROCEDURE MakePO( hbmk, aPOTIN ) IF fhnd != F_ERROR FClose( fhnd ) IF hbmk[ _HBMK_lDEBUGI18N ] - hbmk_OutStd( hb_StrFormat( "MakePO: file .pot list: %1$s", ArrayToList( aPOTIN, ", " ) ) ) - hbmk_OutStd( hb_StrFormat( "MakePO: temp unified .po: %1$s", cPOTemp ) ) + hbmk_OutStd( hb_StrFormat( "RebuildPO: file .pot list: %1$s", ArrayToList( aPOTIN, ", " ) ) ) + hbmk_OutStd( hb_StrFormat( "RebuildPO: temp unified .po: %1$s", cPOTemp ) ) ENDIF - POTMerge( hbmk, aPOTIN, cPOTemp ) + POTMerge( hbmk, aPOTIN, NIL, cPOTemp ) ELSE hbmk_OutStd( I_( "Error: Cannot create temporary unified .po file." ) ) ENDIF @@ -5109,13 +5117,13 @@ STATIC PROCEDURE MakePO( hbmk, aPOTIN ) cPOCooked := StrTran( hbmk[ _HBMK_cPO ], _LNG_MARKER, cLNG ) IF hb_FileExists( cPOCooked ) IF hbmk[ _HBMK_lDEBUGI18N ] - hbmk_OutStd( hb_StrFormat( "MakePO: updating unified .po: %1$s", cPOCooked ) ) + hbmk_OutStd( hb_StrFormat( "RebuildPO: updating unified .po: %1$s", cPOCooked ) ) ENDIF AutoTrans( hbmk, cPOTemp, { cPOCooked }, cPOCooked ) AAdd( aUpd, cLNG ) ELSE IF hbmk[ _HBMK_lDEBUGI18N ] - hbmk_OutStd( hb_StrFormat( "MakePO: creating unified .po: %1$s", cPOCooked ) ) + hbmk_OutStd( hb_StrFormat( "RebuildPO: creating unified .po: %1$s", cPOCooked ) ) ENDIF hb_FCopy( cPOTemp, cPOCooked ) AAdd( aNew, cLNG ) @@ -5135,14 +5143,36 @@ STATIC PROCEDURE MakePO( hbmk, aPOTIN ) ENDIF IF ! Empty( aUpd ) IF Empty( hbmk[ _HBMK_aLNG ] ) - hbmk_OutStd( hb_StrFormat( I_( "Updated .po file '%1$s'" ), hbmk[ _HBMK_cPO ] ) ) + hbmk_OutStd( hb_StrFormat( I_( "Rebuilt .po file '%1$s'" ), hbmk[ _HBMK_cPO ] ) ) ELSE - hbmk_OutStd( hb_StrFormat( I_( "Updated .po file '%1$s' for language(s): %2$s" ), hbmk[ _HBMK_cPO ], ArrayToList( aUpd, "," ) ) ) + hbmk_OutStd( hb_StrFormat( I_( "Rebuilt .po file '%1$s' for language(s): %2$s" ), hbmk[ _HBMK_cPO ], ArrayToList( aUpd, "," ) ) ) ENDIF ENDIF RETURN +/* .po update */ + +STATIC PROCEDURE UpdatePO( hbmk, aPOTIN ) + LOCAL cLNG + + LOCAL aUpd := {} + + FOR EACH cLNG IN iif( Empty( hbmk[ _HBMK_aLNG ] ) .OR. !( _LNG_MARKER $ hbmk[ _HBMK_cPO ] ), { _LNG_MARKER }, hbmk[ _HBMK_aLNG ] ) + POTMerge( hbmk, aPOTIN, StrTran( hbmk[ _HBMK_cPO ], _LNG_MARKER, cLNG ), StrTran( hbmk[ _HBMK_cPO ], _LNG_MARKER, cLNG ) ) + AAdd( aUpd, cLNG ) + NEXT + + IF Empty( hbmk[ _HBMK_aLNG ] ) + hbmk_OutStd( hb_StrFormat( I_( "Updated .po file '%1$s'" ), hbmk[ _HBMK_cPO ] ) ) + ELSE + hbmk_OutStd( hb_StrFormat( I_( "Updated .po file '%1$s' for language(s): %2$s" ), hbmk[ _HBMK_cPO ], ArrayToList( aUpd, "," ) ) ) + ENDIF + + RETURN + +/* .hbl generation */ + STATIC PROCEDURE MakeHBL( hbmk, cHBL ) LOCAL cPO LOCAL tPO @@ -5192,12 +5222,20 @@ STATIC PROCEDURE MakeHBL( hbmk, cHBL ) RETURN -STATIC FUNCTION LoadPOTFiles( hbmk, aFiles, lIgnoreError ) - LOCAL aTrans := {}, aTrans2 +STATIC FUNCTION LoadPOTFiles( hbmk, aFiles, cFileBase, lIgnoreError ) + LOCAL aTrans, aTrans2 LOCAL hIndex LOCAL cErrorMsg LOCAL cFileName + IF ! Empty( cFileBase ) + aTrans := __i18n_potArrayLoad( cFileBase, @cErrorMsg ) + ENDIF + + IF aTrans == NIL + aTrans := {} + ENDIF + FOR EACH cFileName IN aFiles aTrans2 := __i18n_potArrayLoad( cFileName, @cErrorMsg ) IF aTrans2 != NIL @@ -5237,9 +5275,9 @@ STATIC FUNCTION LoadPOTFilesAsHash( hbmk, aFiles ) RETURN hTrans -STATIC PROCEDURE POTMerge( hbmk, aFiles, cFileOut ) +STATIC PROCEDURE POTMerge( hbmk, aFiles, cFileBase, cFileOut ) LOCAL cErrorMsg - LOCAL aTrans := LoadPOTFiles( hbmk, aFiles, .T. ) + LOCAL aTrans := LoadPOTFiles( hbmk, aFiles, cFileBase, .T. ) IF aTrans != NIL IF !__i18n_potArraySave( cFileOut, aTrans, @cErrorMsg ) @@ -5253,7 +5291,7 @@ STATIC PROCEDURE AutoTrans( hbmk, cFileIn, aFiles, cFileOut ) LOCAL cErrorMsg IF !__I18N_potArraySave( cFileOut, ; - __I18N_potArrayTrans( LoadPOTFiles( hbmk, { cFileIn }, .F. ), ; + __I18N_potArrayTrans( LoadPOTFiles( hbmk, {}, cFileIn, .F. ), ; LoadPOTFilesAsHash( hbmk, aFiles ) ), @cErrorMsg ) hbmk_OutErr( hb_StrFormat( I_( "Error: %1$s" ), cErrorMsg ) ) ENDIF @@ -5567,6 +5605,7 @@ STATIC PROCEDURE ShowHelp( lLong ) { "-hbl[=]" , I_( "output .hbl filename. ${lng} macro is accepted in filename" ) },; { "-lng=" , I_( "list of languages to be replaced in ${lng} macros in .pot/.po filenames and output .hbl/.po filenames. Comma separared list:\n-lng=en-EN,hu-HU,de" ) },; { "-po=" , I_( "create/update .po file from source. Merge it with previous .po file of the same name" ) },; + { "-rebuildpo" , I_( "recreate .po file, thus removing all obsolete entries in it" ) },; NIL,; { "-hbrun" , I_( "run target" ) },; { "-hbcmp|-clipper" , I_( "stop after creating the object files\ncreate link/copy hbmk to hbcmp/clipper for the same effect" ) },;