diff -r f5f0877e4bdf acinclude.m4 --- a/acinclude.m4 Sat Jul 26 21:51:54 2008 +0200 +++ b/acinclude.m4 Sun Jul 27 00:19:09 2008 +0200 @@ -254,6 +254,75 @@ ]) dnl --------------------------------------------------------------------------- +dnl Macro: AC_CHECK_FIREBIRD +dnl First check for custom Firebird RDBMS paths in --with-firebird-* options. +dnl If some paths are missing, check if fb_config exists. +dnl --------------------------------------------------------------------------- + +AC_DEFUN([AC_CHECK_FIREBIRD],[ + +# Check for custom includes path +if test [ -z "$ac_cv_firebird_includes" ] +then + AC_ARG_WITH([firebird-includes], + AC_HELP_STRING([--with-firebird-includes], [path to Firebird RDBMS header files]), + [ac_cv_firebird_includes=$withval]) +fi +if test [ -n "$ac_cv_firebird_includes" ] +then + AC_CACHE_CHECK([Firebird RDBMS includes], [ac_cv_firebird_includes], [ac_cv_firebird_includes=""]) + FIREBIRD_CFLAGS="-I$ac_cv_firebird_includes" +fi + +# Check for custom library path +if test [ -z "$ac_cv_firebird_libs" ] +then + AC_ARG_WITH([firebird-libs], + AC_HELP_STRING([--with-firebird-libs], [path to Firebird RDBMS libraries]), + [ac_cv_firebird_libs=$withval]) +fi +if test [ -n "$ac_cv_firebird_libs" ] +then + AC_CACHE_CHECK([Firebird RDBMS libraries], [ac_cv_firebird_libs], [ac_cv_firebird_libs=""]) + FIREBIRD_LIBS="-L$ac_cv_firebird_libs -lfbclient" +fi + +# If some path is missing, try to autodetermine with fb_config +if test [ -z "$ac_cv_firebird_includes" -o -z "$ac_cv_firebird_libs" ] +then + if test [ -z "$fbconfig" ] + then + AC_PATH_PROG(fbconfig,fb_config) + fi + if test [ -z "$fbconfig" ] + then + AC_MSG_ERROR([fb_config executable not found +******************************************************************************** +ERROR: cannot find Firebird RDBMS libraries. If you want to compile with + Firebird RDBMS support, you must either specify file locations explicitly + using --with-firebird-includes and --with-firebird-libs options, or make + sure path to fb_config is listed in your PATH environment variable. If you + want to disable Firebird RDBMS support, use --without-firebird option. +******************************************************************************** +]) + else + if test [ -z "$ac_cv_firebird_includes" ] + then + AC_MSG_CHECKING(Firebird RDBMS C flags) + FIREBIRD_CFLAGS="`${fbconfig} --cflags`" + AC_MSG_RESULT($FIREBIRD_CFLAGS) + fi + if test [ -z "$ac_cv_firebird_libs" ] + then + AC_MSG_CHECKING(Firebird RDBMS linker flags) + FIREBIRD_LIBS="`${fbconfig} --libs`" + AC_MSG_RESULT($FIREBIRD_LIBS) + fi + fi +fi +]) + +dnl --------------------------------------------------------------------------- dnl Macro: SPHINX_CONFIGURE_PART dnl dnl Tells what stage is ./configure running now, nicely formatted diff -r f5f0877e4bdf configure.ac --- a/configure.ac Sat Jul 26 21:51:54 2008 +0200 +++ b/configure.ac Sun Jul 27 00:19:09 2008 +0200 @@ -123,7 +123,6 @@ fi AM_CONDITIONAL(USE_MYSQL, test x$ac_cv_use_mysql != xno) - # check if we should compile with PostgreSQL support AC_ARG_WITH([pgsql], AC_HELP_STRING([--with-pgsql], [compile with PostgreSQL support (default is disabled)]), @@ -141,9 +140,25 @@ fi AM_CONDITIONAL(USE_PGSQL, test x$ac_cv_use_pgsql != xno) +# Check if we should compile with Firebird support +AC_ARG_WITH([firebird], + AC_HELP_STRING([--with-firebird], [compile with Firebird RDMS support (default is disabled)]), + [ac_cv_use_firebird=$withval], [ac_cv_use_firebird=no] +) +AC_MSG_CHECKING([whether to compile with Firebird RDMS support]) +if test x$ac_cv_use_firebird != xno; then + AC_MSG_RESULT([yes]) + AC_CHECK_FIREBIRD([$ac_cv_use_firebird]) + AC_DEFINE(USE_FIREBIRD,1,[Define to 1 if you want to compile with Firebird RDMS support]) + AC_SUBST([FIREBIRD_LIBS]) + AC_SUBST([FIREBIRD_CFLAGS]) +else + AC_MSG_RESULT([no]) +fi +AM_CONDITIONAL(USE_FIREBIRD, test x$ac_cv_use_firebird != xno) # we can now set preprocessor flags for both C and C++ compilers -CPPFLAGS="$CPPFLAGS $MYSQL_CFLAGS $PGSQL_CFLAGS" +CPPFLAGS="$CPPFLAGS $MYSQL_CFLAGS $PGSQL_CFLAGS $FIREBIRD_CFLAGS" dnl --- diff -r f5f0877e4bdf src/Makefile.am --- a/src/Makefile.am Sat Jul 26 21:51:54 2008 +0200 +++ b/src/Makefile.am Sun Jul 27 00:19:09 2008 +0200 @@ -27,7 +27,7 @@ AM_CPPFLAGS = -DSYSCONFDIR="\"$(sysconfdir)\"" endif -COMMON_LIBS = libsphinx.a $(LIBSTEMMER_LIBS) $(MYSQL_LIBS) $(PGSQL_LIBS) +COMMON_LIBS = libsphinx.a $(LIBSTEMMER_LIBS) $(MYSQL_LIBS) $(PGSQL_LIBS) $(FIREBIRD_LIBS) LDADD = $(COMMON_LIBS) EXTRA_DIST = indexer.vcproj libsphinx.vcproj searchd.vcproj search.vcproj diff -r f5f0877e4bdf src/indexer.cpp --- a/src/indexer.cpp Sat Jul 26 21:51:54 2008 +0200 +++ b/src/indexer.cpp Sun Jul 27 00:19:09 2008 +0200 @@ -506,6 +506,28 @@ #endif // USE_MYSQL +#if USE_FIREBIRD +CSphSource * SpawnSourceFBSQL ( const CSphConfigSection & hSource, const char * sSourceName ) +{ + assert ( hSource["type"]=="firebird" ); + + CSphSourceParams_FBSQL tParams; + if ( !SqlParamsConfigure ( tParams, hSource, sSourceName ) ) + return NULL; + + LOC_GETS ( tParams.m_sCharset, "sql_charset" ); + LOC_GETS ( tParams.m_sRole, "sql_role" ); + + CSphSource_FBSQL * pSrcFBSQL = new CSphSource_FBSQL ( sSourceName ); + if ( !pSrcFBSQL->Setup ( tParams ) ) + SafeDelete ( pSrcFBSQL ); + + return pSrcFBSQL; +} +#endif // USE_FIREBIRD + + + CSphSource * SpawnSourceXMLPipe ( const CSphConfigSection & hSource, const char * sSourceName, bool bUTF8 ) { assert ( hSource["type"]=="xmlpipe" || hSource["type"]=="xmlpipe2" ); @@ -571,6 +593,12 @@ if ( hSource["type"]=="mysql") return SpawnSourceMySQL ( hSource, sSourceName ); #endif + + #if USE_FIREBIRD + if ( hSource["type"]=="firebird") + return SpawnSourceFBSQL ( hSource, sSourceName ); + #endif + if ( hSource["type"]=="xmlpipe" || hSource["type"]=="xmlpipe2" ) return SpawnSourceXMLPipe ( hSource, sSourceName, bUTF8 ); diff -r f5f0877e4bdf src/sphinx.cpp --- a/src/sphinx.cpp Sat Jul 26 21:51:54 2008 +0200 +++ b/src/sphinx.cpp Sun Jul 27 00:19:09 2008 +0200 @@ -79,6 +79,11 @@ #if ( USE_WINDOWS && USE_PGSQL ) #pragma comment(linker, "/defaultlib:libpq.lib") #pragma message("Automatically linking with libpq.lib") +#endif + +#if ( USE_WINDOWS && USE_FIREBIRD ) + #pragma comment(linker, "/defaultlib:fbclient_ms.lib") + #pragma message("Automatically linking with fbclient.lib") #endif #if ( USE_WINDOWS && USE_LIBSTEMMER ) @@ -1960,7 +1965,7 @@ #endif -#if USE_WINDOWS || !HAVE_F_SETLKW +#if USE_WINDOWS bool sphLockEx ( int iFile, bool bWait ) { @@ -16978,6 +16983,423 @@ #endif // USE_PGSQL + +#if USE_FIREBIRD + +CSphSourceParams_FBSQL::CSphSourceParams_FBSQL () : + CSphSourceParams_SQL () +{ +} + +CSphSource_FBSQL::CSphSource_FBSQL ( const char * sName ) : + CSphSource_SQL ( sName ), + m_database ( NULL ), + m_transaction ( NULL ), + m_statement ( NULL ), + m_record ( NULL ), + m_recsize ( 0 ), + m_selectable ( false ), + m_blob ( NULL ), + m_blobsize ( 0 ) +{ + memset(m_status, 0, sizeof(m_status)); + m_error[0] = 0; + + const short DEF_SQLVARS = 4; + m_xsqlda = (XSQLDA *) new char[XSQLDA_LENGTH(DEF_SQLVARS)]; + m_xsqlda->version = 1; + m_xsqlda->sqln = DEF_SQLVARS; +} + +CSphSource_FBSQL::~CSphSource_FBSQL () +{ + delete[] (char *) m_xsqlda; + delete[] m_record; + delete[] m_blob; +} + +bool CSphSource_FBSQL::Setup ( const CSphSourceParams_FBSQL & pParams ) +{ + if ( !CSphSource_SQL::Setup ( pParams ) ) + return false; + + m_sCharset = pParams.m_sCharset; + m_sRole = pParams.m_sRole; + + return true; +} + +void CSphSource_FBSQL::SqlDismissResult () +{ + if ( !m_statement ) + return; + + if ( m_selectable ) { + isc_dsql_free_statement ( m_status, &m_statement, DSQL_close ); + } +} + +bool CSphSource_FBSQL::SqlQuery ( const char * sQuery ) +{ + if ( !m_statement ) { + if ( isc_dsql_allocate_statement ( m_status, &m_database, &m_statement ) ) + return false; + } + + if ( isc_dsql_prepare ( m_status, &m_transaction, &m_statement, 0, sQuery, + SQL_DIALECT_CURRENT, NULL ) ) + { + return false; + } + + m_selectable = false; + + // get statement type + const char stmt_info[] = { isc_info_sql_stmt_type }; + char info_buff[16]; + if ( isc_dsql_sql_info ( m_status, &m_statement, sizeof(stmt_info), stmt_info, + sizeof(info_buff), info_buff ) ) + { + return false; + } + if ( info_buff[0] != stmt_info[0] ) + return false; + + { + const int len = isc_vax_integer ( &info_buff[1], 2 ); + const int stmt_type = isc_vax_integer ( &info_buff[3], (short) len ); + + m_selectable = ( stmt_type == isc_info_sql_stmt_select || + stmt_type == isc_info_sql_stmt_select_for_upd ); + } + + + if ( m_selectable ) + { + if ( isc_dsql_describe ( m_status, &m_statement, SQLDA_VERSION1, m_xsqlda ) ) + return false; + + if ( m_xsqlda->sqld > m_xsqlda->sqln ) + { + const short len = m_xsqlda->sqld; + delete[] (char*) m_xsqlda; + + m_xsqlda = (XSQLDA*) new char[XSQLDA_LENGTH ( len )]; + m_xsqlda->sqln = len; + m_xsqlda->version = 1; + + if ( isc_dsql_describe ( m_status, &m_statement, SQLDA_VERSION1, m_xsqlda ) ) + return false; + } + + const size_t recsize = parseSQLDA ( m_xsqlda, NULL ); + if ( recsize > m_recsize ) + { + delete[] m_record; + + m_recsize = recsize; + m_record = new char[m_recsize]; + } + parseSQLDA ( m_xsqlda, m_record ); + } + else { + m_xsqlda->sqld = 0; + } + + if ( isc_dsql_execute ( m_status, &m_transaction, &m_statement, SQLDA_VERSION1, NULL ) ) + return false; + + return true; +} + +bool CSphSource_FBSQL::SqlIsError () +{ + return ( m_status[1] != 0 ); +} + +const char * CSphSource_FBSQL::SqlError () +{ + char * p = m_error, * const end = m_error + sizeof(m_error); + const ISC_STATUS * s = m_status; + while ( fb_interpret ( p, end - p, &s ) ) + { + p += strlen ( p ); + if ( p < end - 1 ) + *p++ = '\n'; + *p = 0; + } + + return m_error; +} + +bool CSphSource_FBSQL::SqlConnect () +{ + char dpb[256]; + char *p = dpb; + + *p++ = isc_dpb_version1; + p = putSphStringInDPB ( p, isc_dpb_user_name, m_tParams.m_sUser ); + p = putSphStringInDPB ( p, isc_dpb_password, m_tParams.m_sPass ); + p = putSphStringInDPB ( p, isc_dpb_sql_role_name, m_sRole ); + p = putSphStringInDPB ( p, isc_dpb_lc_ctype, m_sCharset ); + + if ( isc_attach_database ( m_status, 0, m_tParams.m_sDB.cstr(), &m_database, + (short) (p - dpb), dpb ) ) + { + return false; + } + + //char tpb[] = {isc_tpb_version3, isc_tpb_read, isc_tpb_read_committed, + // isc_tpb_rec_version, isc_tpb_wait}; + + char tpb[] = { isc_tpb_version3, isc_tpb_write, isc_tpb_concurrency, isc_tpb_wait }; + + if ( isc_start_transaction ( m_status, &m_transaction, 1, &m_database, + sizeof ( tpb ), tpb ) ) + { + ISC_STATUS_ARRAY temp = { 0 }; + isc_detach_database ( temp, &m_database ); + return false; + } + + return true; +} + +void CSphSource_FBSQL::SqlDisconnect () +{ + if ( m_transaction ) { + if ( isc_commit_transaction ( m_status, &m_transaction ) ) + { + ISC_STATUS_ARRAY temp = {0}; + isc_rollback_transaction ( temp, &m_transaction ); + } + } + + if ( m_statement ) + isc_dsql_free_statement ( m_status, &m_statement, DSQL_drop ); + + if ( m_database ) + isc_detach_database ( m_status, &m_database ); +} + +int CSphSource_FBSQL::SqlNumFields () +{ + if ( !m_xsqlda ) + return 0; + + return m_xsqlda->sqld; +} + +bool CSphSource_FBSQL::SqlFetchRow () +{ + if ( !m_statement ) + return false; + + if ( isc_dsql_fetch ( m_status, &m_statement, SQLDA_VERSION1, m_xsqlda ) == 100 ) + return false; + + if ( m_status[1] ) + return false; + + // make returned strings NULL-terminated + XSQLVAR *var = m_xsqlda->sqlvar; + for ( int i = 0; i < m_xsqlda->sqld; var++, i++ ) + { + if ( *var->sqlind == 0 && (var->sqltype & (~1)) != SQL_BLOB ) + { + short len = * (short *) var->sqldata; + var->sqldata[len + sizeof(short)] = 0; + } + } + + return true; +} + +const char * CSphSource_FBSQL::SqlColumn ( int iIndex ) +{ + if (!m_xsqlda) + return 0; + + XSQLVAR &var = m_xsqlda->sqlvar[iIndex]; + if ( *var.sqlind != 0 ) + return NULL; + + if ( (var.sqltype & (~1)) != SQL_BLOB ) + return var.sqldata + sizeof(short); + + + // blob to string + ISC_QUAD * blob_id = (ISC_QUAD *) var.sqldata; + + // empty (NULL) blob + if ( !blob_id->gds_quad_high && !blob_id->gds_quad_low ) + return NULL; + + isc_blob_handle blob = {0}; + if ( isc_open_blob ( m_status, &m_database, &m_transaction, &blob, blob_id ) ) + return NULL; + + const char info[] = {isc_info_blob_total_length}; + char resp[16]; + if ( isc_blob_info ( m_status, &blob, sizeof(info), info, sizeof(resp), resp ) ) + return NULL; + + if ( info[0] != resp[0] ) + return NULL; + + int len = isc_vax_integer ( &resp[1], 2 ); + len = isc_vax_integer ( &resp[3], (short) len ); + + if (len + 4 > m_blobsize) + { + delete[] m_blob; + + m_blobsize = len + 4; + m_blob = new char[m_blobsize]; + } + + char *p = m_blob; + while ( true ) + { + const unsigned short MAX_SEGMENT = 64*1024 - 3; + unsigned short reads = 0; + + const ISC_STATUS ret = isc_get_segment ( m_status, &blob, &reads, + (unsigned short) Min(len, MAX_SEGMENT), p); + + if ( ret == 0 || ret == isc_segment ) + { + p += reads; + len -= reads; + } + else + { + if ( ret != isc_segstr_eof ) + p = NULL; + + break; + } + } + isc_close_blob ( m_status, &blob ); + + if ( !p ) + return NULL; + + // make string NULL-terminated for multi-byte encodings too + const char * end = p + 4; + while ( p < end && p < m_blob + m_blobsize ) + *p++ = 0; + + return m_blob; +} + +const char * CSphSource_FBSQL::SqlFieldName ( int iIndex ) +{ + if ( !m_xsqlda ) + return 0; + + return m_xsqlda->sqlvar[iIndex].aliasname; +} + +char * CSphSource_FBSQL::putSphStringInDPB ( char * dpb, char clump, CSphString &str ) +{ + if ( str.IsEmpty() ) + return dpb; + + *dpb++ = clump; + + const size_t len = strlen( str.cstr() ); + *dpb++ = (char) len; + + memcpy( dpb, str.cstr(), len ); + + return dpb + len; +} + +size_t CSphSource_FBSQL::parseSQLDA ( XSQLDA * xsqlda, char * buff ) +{ + // on the first pass (buff == NULL) convert all SQL_xxx into + // SQL_VARYING and make room for NULL-terminated string + + size_t offset = 0; + int i = 0; + XSQLVAR* var = xsqlda->sqlvar; + for ( ; i < xsqlda->sqld; var++, i++ ) + { + // round up to sizeof(short) + offset = (offset + 1) & ~(1); + + short length = var->sqllen; + const int type = var->sqltype & (~1); + switch ( type ) + { + case SQL_TEXT: + case SQL_VARYING: + if ( !buff ) + length += sizeof(short) + 1; + break; + + case SQL_SHORT: + case SQL_LONG: + case SQL_INT64: + case SQL_FLOAT: + case SQL_DOUBLE: + length = 26; + break; + + case SQL_TIMESTAMP: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + length = 34; + break; + + case SQL_BLOB: + { + // round up to sizeof(ISC_QUAD) + const int quad_size = sizeof(ISC_QUAD) - 1; + offset = (offset + quad_size) & ~(quad_size); + } + break; + + default: + break; + } + + if ( type != SQL_BLOB ) + { + var->sqltype = SQL_VARYING | 1; + var->sqllen = length; + } + else + { + var->sqltype = SQL_BLOB | 1; + } + if ( buff ) { + var->sqldata = &buff[offset]; + } + offset += length; + } + + // round up to sizeof(short) + offset = (offset + 1) & ~(1); + + // room for null-indicators (short's) + if ( buff ) { + for ( i = 0, var = xsqlda->sqlvar; i < xsqlda->sqld; var++, i++ ) + { + var->sqlind = (short*) (&buff[offset]); + offset += sizeof(short); + } + } + else { + offset += sizeof(short) * xsqlda->sqld; + } + + return offset; +} + +#endif // USE_FIREBIRD + ///////////////////////////////////////////////////////////////////////////// // XMLPIPE ///////////////////////////////////////////////////////////////////////////// diff -r f5f0877e4bdf src/sphinx.h --- a/src/sphinx.h Sat Jul 26 21:51:54 2008 +0200 +++ b/src/sphinx.h Sun Jul 27 00:19:09 2008 +0200 @@ -17,11 +17,12 @@ ///////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 - #define USE_MYSQL 1 /// whether to compile MySQL support - #define USE_LIBEXPAT 1 /// whether to compile libexpat support - #define USE_LIBICONV 1 /// whether to compile iconv support + #define USE_MYSQL 0 /// whether to compile MySQL support + #define USE_LIBEXPAT 0 /// whether to compile libexpat support + #define USE_LIBICONV 0 /// whether to compile iconv support #define USE_LIBXML 0 /// whether to compile libxml support #define USE_WINDOWS 1 /// whether to compile for Windows + #define USE_FIREBIRD 1 /// whether to compile Firebird support #else #define USE_WINDOWS 0 /// whether to compile for Windows #endif @@ -41,6 +42,10 @@ #if USE_PGSQL #include +#endif + +#if USE_FIREBIRD +#include #endif #if USE_WINDOWS @@ -1163,6 +1168,65 @@ }; #endif // USE_PGSQL +#if USE_FIREBIRD +/// Firebird specific source params +struct CSphSourceParams_FBSQL : CSphSourceParams_SQL +{ + CSphString m_sCharset; + CSphString m_sRole; + CSphSourceParams_FBSQL (); +}; + + +/// Firebird source implementation +/// multi-field plain-text documents fetched from given query +struct CSphSource_FBSQL : CSphSource_SQL +{ + CSphSource_FBSQL ( const char * sName ); + virtual ~CSphSource_FBSQL (); + bool Setup ( const CSphSourceParams_FBSQL & pParams ); + +protected: + /// config values + CSphString m_sCharset; + CSphString m_sRole; + + /// API handles + ISC_STATUS_ARRAY m_status; + isc_db_handle m_database; + isc_tr_handle m_transaction; + isc_stmt_handle m_statement; + + // error and record holders + static const int ERROR_BUFFER_SIZE = 1024; + char m_error[ERROR_BUFFER_SIZE]; + + XSQLDA * m_xsqlda; + char * m_record; + size_t m_recsize; + bool m_selectable; + char * m_blob; + size_t m_blobsize; + +protected: + virtual void SqlDismissResult (); + virtual bool SqlQuery ( const char * sQuery ); + virtual bool SqlIsError (); + virtual const char * SqlError (); + virtual bool SqlConnect (); + virtual void SqlDisconnect (); + virtual int SqlNumFields (); + virtual bool SqlFetchRow (); + virtual const char * SqlColumn ( int iIndex ); + virtual const char * SqlFieldName ( int iIndex ); + +private: + char * putSphStringInDPB ( char * dpb, char clump, CSphString &str ); + static size_t parseSQLDA ( XSQLDA * xsqlda, char * buff ); +}; + +#endif // USE_FIREBIRD + /// XML pipe source implementation class CSphSource_XMLPipe : public CSphSource diff -r f5f0877e4bdf src/sphinxutils.cpp --- a/src/sphinxutils.cpp Sat Jul 26 21:51:54 2008 +0200 +++ b/src/sphinxutils.cpp Sun Jul 27 00:19:09 2008 +0200 @@ -109,6 +109,10 @@ { "sql_group_column", KEY_LIST | KEY_DEPRECATED, "sql_attr_uint" }, { "sql_date_column", KEY_LIST | KEY_DEPRECATED, "sql_attr_timestamp" }, { "sql_str2ordinal_column", KEY_LIST | KEY_DEPRECATED, "sql_attr_str2ordinal" }, +#if USE_FIREBIRD + { "sql_charset", 0, NULL }, + { "sql_role", 0, NULL }, +#endif { NULL, 0, NULL } };