diff --git a/libs/db/CMakeLists.txt b/libs/db/CMakeLists.txt index 6de23f8bd2..dfe4fe5680 100644 --- a/libs/db/CMakeLists.txt +++ b/libs/db/CMakeLists.txt @@ -1,121 +1,122 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44000) # To hide all the warnings from embedded 3rd party software like icu if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) add_definitions(-Wno-unused-local-typedefs) add_definitions(-Wno-sign-compare) add_definitions(-Wno-unused-function) add_definitions(-Wno-unused-parameter) endif () option(CALLIGRADB_DEBUG_GUI "Debugging GUI for CalligraDB" OFF) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-calligradb.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-calligradb.h ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/parser ${KOPLUGIN_INCLUDES} ${KDE4_INCLUDES} ${ICU_INCLUDE_DIRS} ) add_subdirectory( drivers ) add_definitions( -D__KEXIDB__= -DYYERROR_VERBOSE=1 ) ########### generate parser/lexer files ############### # as described at http://public.kitware.com/pipermail/cmake/2002-September/003028.html # Create target for the parser add_custom_target(parser echo "Creating parser/lexer files") # Create custom command for flex/lex (note the outputs) add_custom_command( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/parser/generate_parser_code.sh TARGET parser OUTPUTS ${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlparser.h ${CMAKE_CURRENT_SOURCE_DIR}/sqlparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlscanner.h ${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlscanner.cpp) # mark files as generated set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlparser.h GENERATED) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlparser.cpp GENERATED) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlscanner.h GENERATED) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/parser/sqlscanner.cpp GENERATED) set(calligradbparser_STAT_SRCS parser/sqlscanner.cpp parser/sqlparser.cpp parser/parser.cpp parser/parser_p.cpp ) set(calligradb_LIB_SRCS ${calligradbparser_STAT_SRCS} pluginloader.cpp drivermanager.cpp driver.cpp driver_p.cpp connection.cpp keywords.cpp object.cpp field.cpp utils.cpp expression.cpp connectiondata.cpp RecordData.cpp fieldlist.cpp tableschema.cpp cursor.cpp transaction.cpp indexschema.cpp queryschema.cpp + queryschema_p.cpp queryschemaparameter.cpp schemadata.cpp global.cpp relationship.cpp roweditbuffer.cpp msghandler.cpp preparedstatement.cpp dbproperties.cpp admin.cpp lookupfieldschema.cpp simplecommandlineapp.cpp tableviewdata.cpp tableviewcolumn.cpp validator.cpp ) kde4_add_library(calligradb SHARED ${calligradb_LIB_SRCS}) target_link_libraries(calligradb koplugin ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${ICU_I18N_LIBRARY}) target_link_libraries(calligradb LINK_INTERFACE_LIBRARIES koplugin ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS}) set_target_properties(calligradb PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) if(MSVC) # avoid LNK1169 errors set_target_properties(calligradb PROPERTIES LINK_FLAGS /FORCE:MULTIPLE) endif() install(TARGETS calligradb ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES calligradb_driver.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) if(FALSE) # TODO: install when we move to independent place install( FILES connection.h connectiondata.h cursor.h driver.h drivermanager.h error.h expression.h field.h fieldlist.h global.h object.h schemadata.h tableschema.h queryschema.h queryschemaparameter.h indexschema.h relationship.h parser/parser.h parser/sqlparser.h transaction.h preparedstatement.h RecordData.h utils.h calligradb_export.h calligradb_global.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligradb COMPONENT Devel) endif() add_subdirectory( tests ) diff --git a/libs/db/parser/sqlparser.cpp b/libs/db/parser/sqlparser.cpp index 31713fbdbf..0fa6ea9ce0 100644 --- a/libs/db/parser/sqlparser.cpp +++ b/libs/db/parser/sqlparser.cpp @@ -1,2557 +1,2558 @@ /* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.4" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Copy the first part of user declarations. */ #line 413 "sqlparser.y" /* yacc.c:339 */ #include #include #include #include #include #include //TODO OK? #ifdef Q_WS_WIN //workaround for bug on msvc # undef LLONG_MIN #endif #ifndef LLONG_MAX # define LLONG_MAX 0x7fffffffffffffffLL #endif #ifndef LLONG_MIN # define LLONG_MIN 0x8000000000000000LL #endif #ifndef LLONG_MAX # define ULLONG_MAX 0xffffffffffffffffLL #endif #ifdef _WIN32 # include #endif #include #include #include #include #include #include #include +#include #include #include #include "parser.h" #include "parser_p.h" #include "sqltypes.h" #ifdef Q_OS_SOLARIS #include #endif int yylex(); using namespace KexiDB; #define YY_NO_UNPUT #define YYSTACK_USE_ALLOCA 1 #define YYMAXDEPTH 255 extern "C" { int yywrap() { return 1; } } -#line 129 "sqlparser.cpp" /* yacc.c:339 */ +#line 130 "sqlparser.cpp" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* In a future release of Bison, this section will be replaced by #include "sqlparser.tab.h". */ #ifndef YY_YY_SQLPARSER_TAB_H_INCLUDED # define YY_YY_SQLPARSER_TAB_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 1 #endif #if YYDEBUG extern int yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { SQL_TYPE = 258, AS = 259, ASC = 260, AUTO_INCREMENT = 261, BIT = 262, BITWISE_SHIFT_LEFT = 263, BITWISE_SHIFT_RIGHT = 264, BY = 265, CHARACTER_STRING_LITERAL = 266, CONCATENATION = 267, CREATE = 268, DESC = 269, DISTINCT = 270, DOUBLE_QUOTED_STRING = 271, FROM = 272, JOIN = 273, KEY = 274, LEFT = 275, LESS_OR_EQUAL = 276, SQL_NULL = 277, SQL_IS = 278, SQL_IS_NULL = 279, SQL_IS_NOT_NULL = 280, ORDER = 281, PRIMARY = 282, SELECT = 283, INTEGER_CONST = 284, REAL_CONST = 285, RIGHT = 286, SQL_ON = 287, DATE_CONST = 288, DATETIME_CONST = 289, TIME_CONST = 290, TABLE = 291, IDENTIFIER = 292, IDENTIFIER_DOT_ASTERISK = 293, QUERY_PARAMETER = 294, VARCHAR = 295, WHERE = 296, SCAN_ERROR = 297, UNION = 298, EXCEPT = 299, INTERSECT = 300, OR = 301, AND = 302, XOR = 303, NOT = 304, GREATER_OR_EQUAL = 305, NOT_EQUAL = 306, NOT_EQUAL2 = 307, SQL_IN = 308, LIKE = 309, NOT_LIKE = 310, ILIKE = 311, SIMILAR_TO = 312, NOT_SIMILAR_TO = 313, SIMILAR = 314, BETWEEN = 315, NOT_BETWEEN = 316, UMINUS = 317 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 476 "sqlparser.y" /* yacc.c:355 */ +#line 477 "sqlparser.y" /* yacc.c:355 */ QString* stringValue; qint64 integerValue; bool booleanValue; struct realType realValue; KexiDB::Field::Type colType; KexiDB::Field *field; KexiDB::BaseExpr *expr; KexiDB::NArgExpr *exprList; KexiDB::ConstExpr *constExpr; KexiDB::QuerySchema *querySchema; SelectOptionsInternal *selectOptions; OrderByColumnInternal::List *orderByColumns; QVariant *variantValue; -#line 248 "sqlparser.cpp" /* yacc.c:355 */ +#line 249 "sqlparser.cpp" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; int yyparse (void); #endif /* !YY_YY_SQLPARSER_TAB_H_INCLUDED */ /* Copy the second part of user declarations. */ -#line 265 "sqlparser.cpp" /* yacc.c:358 */ +#line 266 "sqlparser.cpp" /* yacc.c:358 */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif #if !defined _Noreturn \ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) # if defined _MSC_VER && 1200 <= _MSC_VER # define _Noreturn __declspec (noreturn) # else # define _Noreturn YY_ATTRIBUTE ((__noreturn__)) # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 7 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 190 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 86 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 30 /* YYNRULES -- Number of rules. */ #define YYNRULES 97 /* YYNSTATES -- Number of states. */ #define YYNSTATES 159 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 317 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 46, 75, 83, 48, 80, 81, 73, 71, 45, 72, 82, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 44, 57, 56, 58, 47, 43, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 78, 2, 79, 76, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 84, 2, 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 49, 50, 51, 52, 53, 54, 55, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 77 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 565, 565, 575, 579, 580, 595, 693, 700, 705, - 711, 717, 726, 736, 742, 748, 755, 765, 774, 783, - 793, 801, 813, 819, 826, 833, 837, 844, 849, 854, - 858, 863, 868, 872, 876, 880, 884, 889, 894, 899, - 903, 907, 911, 915, 919, 923, 930, 938, 943, 947, - 952, 957, 961, 966, 971, 976, 980, 984, 988, 993, - 998, 1002, 1006, 1011, 1017, 1021, 1025, 1029, 1033, 1041, - 1047, 1054, 1061, 1068, 1074, 1091, 1097, 1102, 1110, 1114, - 1121, 1126, 1134, 1179, 1184, 1192, 1220, 1231, 1247, 1253, - 1262, 1271, 1276, 1285, 1297, 1341, 1350, 1359 + 0, 566, 566, 576, 580, 581, 596, 694, 701, 706, + 712, 718, 727, 737, 743, 749, 756, 766, 775, 784, + 794, 802, 814, 820, 827, 834, 838, 845, 850, 855, + 859, 864, 869, 873, 877, 881, 885, 890, 895, 900, + 904, 908, 912, 916, 920, 924, 931, 939, 944, 948, + 953, 958, 962, 967, 972, 977, 981, 985, 989, 994, + 999, 1003, 1007, 1012, 1018, 1022, 1026, 1030, 1034, 1042, + 1048, 1055, 1062, 1069, 1075, 1092, 1098, 1103, 1111, 1115, + 1122, 1127, 1135, 1180, 1185, 1193, 1221, 1232, 1248, 1254, + 1263, 1272, 1277, 1286, 1298, 1342, 1351, 1360 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "SQL_TYPE", "AS", "ASC", "AUTO_INCREMENT", "BIT", "BITWISE_SHIFT_LEFT", "BITWISE_SHIFT_RIGHT", "BY", "CHARACTER_STRING_LITERAL", "CONCATENATION", "CREATE", "DESC", "DISTINCT", "DOUBLE_QUOTED_STRING", "FROM", "JOIN", "KEY", "LEFT", "LESS_OR_EQUAL", "SQL_NULL", "SQL_IS", "SQL_IS_NULL", "SQL_IS_NOT_NULL", "ORDER", "PRIMARY", "SELECT", "INTEGER_CONST", "REAL_CONST", "RIGHT", "SQL_ON", "DATE_CONST", "DATETIME_CONST", "TIME_CONST", "TABLE", "IDENTIFIER", "IDENTIFIER_DOT_ASTERISK", "QUERY_PARAMETER", "VARCHAR", "WHERE", "SCAN_ERROR", "'@'", "';'", "','", "'$'", "'?'", "'\\''", "UNION", "EXCEPT", "INTERSECT", "OR", "AND", "XOR", "NOT", "'='", "'<'", "'>'", "GREATER_OR_EQUAL", "NOT_EQUAL", "NOT_EQUAL2", "SQL_IN", "LIKE", "NOT_LIKE", "ILIKE", "SIMILAR_TO", "NOT_SIMILAR_TO", "SIMILAR", "BETWEEN", "NOT_BETWEEN", "'+'", "'-'", "'*'", "'/'", "'%'", "'^'", "UMINUS", "'['", "']'", "'('", "')'", "'.'", "'&'", "'|'", "'~'", "$accept", "TopLevelStatement", "StatementList", "Statement", "SelectStatement", "Select", "SelectOptions", "WhereClause", "OrderByClause", "OrderByColumnId", "OrderByOption", "aExpr", "aExpr2", "aExpr3", "aExpr4", "aExpr5", "aExpr6", "aExpr7", "aExpr8", "aExpr9", "aExpr10", "aExprList", "aExprList2", "Tables", "FlatTableList", "FlatTable", "ColViews", "ColItem", "ColExpression", "ColWildCard", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 64, 59, 44, 36, 63, 39, 298, 299, 300, 301, 302, 303, 304, 61, 60, 62, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 43, 45, 42, 47, 37, 94, 317, 91, 93, 40, 41, 46, 38, 124, 126 }; # endif #define YYPACT_NINF -62 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-62))) #define YYTABLE_NINF -1 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { -13, -62, 40, -62, 9, -62, -1, -62, -13, -62, -15, 32, -62, -62, -62, -47, -62, 94, 94, 94, -62, 94, 94, -62, -62, 44, -9, 120, -62, 51, 18, -30, -62, -62, 1, -62, 19, -62, -62, 70, 21, 31, -62, 82, -18, -62, -14, -62, -62, -62, 5, -62, 94, 94, 94, 94, 94, 94, 94, 94, -62, -62, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 73, 94, 2, -62, 54, 11, 57, -62, 7, 69, -62, 32, -62, 46, 29, -62, -62, 80, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, 65, 67, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -3, -62, -62, 112, -62, -62, -62, -62, -62, 94, -62, 94, 94, -62, 50, 102, 6, -3, -62, -62, -62, 109, -62, -62, -62, -3, 103, -62, -62, -62, -3, -62 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 12, 0, 2, 4, 6, 0, 1, 5, 73, 0, 0, 72, 74, 75, 68, 69, 0, 0, 0, 96, 0, 0, 94, 27, 31, 37, 47, 50, 53, 59, 63, 76, 9, 7, 89, 90, 91, 3, 0, 85, 82, 84, 0, 0, 70, 68, 67, 65, 64, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 13, 8, 0, 93, 0, 0, 86, 0, 79, 81, 0, 71, 97, 0, 77, 29, 28, 30, 35, 36, 34, 32, 33, 38, 39, 42, 40, 41, 43, 44, 0, 0, 51, 52, 55, 54, 56, 57, 58, 61, 60, 62, 0, 17, 88, 0, 11, 92, 95, 87, 83, 0, 78, 0, 0, 24, 22, 14, 18, 0, 80, 45, 46, 0, 16, 25, 26, 0, 19, 15, 23, 20, 0, 21 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -62, -62, 139, -62, -62, -62, 66, 22, -49, -62, -62, -21, 86, 71, -61, -62, 42, 83, 56, 45, -62, -62, 15, 127, -62, 76, -62, 87, 130, -62 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 2, 3, 4, 5, 6, 84, 85, 142, 143, 153, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 45, 95, 33, 41, 42, 34, 35, 36, 37 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { 50, 108, 109, 110, 111, 112, 113, 114, 115, 116, 9, 150, 55, 9, 10, 1, 11, 10, 11, 96, 151, 12, 94, 87, 12, 90, 140, 81, 13, 14, 73, 13, 14, 43, 141, 44, 15, 81, 16, 15, 7, 16, 82, 78, 79, 80, 83, 56, 57, 58, 59, 152, 82, 8, 17, 97, 88, 17, 91, 71, 72, 128, 47, 48, 49, 39, 43, 51, 98, 40, 18, 19, 20, 18, 19, 20, 92, 146, 147, 21, 130, 9, 21, 127, 22, 10, 99, 22, 133, 74, 75, 136, 12, 9, 132, 154, 52, 53, 54, 13, 14, 76, 77, 156, 12, 9, 134, 46, 158, 16, 137, 13, 14, 117, 118, 94, 12, 96, 138, 46, 139, 16, 144, 13, 14, 17, 103, 104, 105, 106, 107, 46, 148, 16, 124, 125, 126, 17, 100, 101, 102, 18, 19, 82, 60, 61, 155, 38, 157, 17, 21, 145, 131, 18, 19, 22, 119, 120, 121, 122, 123, 86, 21, 93, 149, 18, 19, 22, 135, 89, 129, 0, 0, 0, 21, 0, 0, 0, 0, 22, 62, 63, 64, 65, 66, 0, 67, 68, 0, 69, 70 }; static const yytype_int16 yycheck[] = { 21, 62, 63, 64, 65, 66, 67, 68, 69, 70, 11, 5, 21, 11, 15, 28, 17, 15, 17, 37, 14, 22, 43, 4, 22, 4, 29, 26, 29, 30, 12, 29, 30, 80, 37, 82, 37, 26, 39, 37, 0, 39, 41, 73, 74, 75, 45, 56, 57, 58, 59, 45, 41, 44, 55, 73, 37, 55, 37, 8, 9, 82, 17, 18, 19, 80, 80, 22, 82, 37, 71, 72, 73, 71, 72, 73, 45, 138, 139, 80, 26, 11, 80, 10, 85, 15, 81, 85, 81, 71, 72, 45, 22, 11, 37, 144, 52, 53, 54, 29, 30, 83, 84, 152, 22, 11, 37, 37, 157, 39, 81, 29, 30, 71, 72, 136, 22, 37, 53, 37, 53, 39, 10, 29, 30, 55, 55, 56, 57, 58, 59, 37, 82, 39, 78, 79, 80, 55, 52, 53, 54, 71, 72, 41, 24, 25, 37, 8, 45, 55, 80, 136, 86, 71, 72, 85, 73, 74, 75, 76, 77, 34, 80, 81, 142, 71, 72, 85, 92, 39, 83, -1, -1, -1, 80, -1, -1, -1, -1, 85, 60, 61, 62, 63, 64, -1, 66, 67, -1, 69, 70 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 28, 87, 88, 89, 90, 91, 0, 44, 11, 15, 17, 22, 29, 30, 37, 39, 55, 71, 72, 73, 80, 85, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 109, 112, 113, 114, 115, 88, 80, 37, 110, 111, 80, 82, 107, 37, 105, 105, 105, 97, 105, 52, 53, 54, 21, 56, 57, 58, 59, 24, 25, 60, 61, 62, 63, 64, 66, 67, 69, 70, 8, 9, 12, 71, 72, 83, 84, 73, 74, 75, 26, 41, 45, 92, 93, 109, 4, 37, 114, 4, 37, 45, 81, 97, 108, 37, 73, 82, 81, 98, 98, 98, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 102, 102, 103, 103, 103, 103, 103, 104, 104, 104, 10, 97, 113, 26, 92, 37, 81, 37, 111, 45, 81, 53, 53, 29, 37, 94, 95, 10, 108, 100, 100, 82, 93, 5, 14, 45, 96, 94, 37, 94, 45, 94 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 86, 87, 88, 88, 88, 89, 90, 90, 90, 90, 90, 91, 92, 92, 92, 92, 93, 94, 94, 94, 94, 95, 95, 95, 96, 96, 97, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, 107, 107, 108, 108, 109, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 113, 114, 114, 115, 115 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 3, 1, 2, 1, 2, 3, 2, 3, 4, 1, 1, 3, 4, 4, 2, 1, 2, 3, 4, 1, 3, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 1, 2, 2, 1, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 3, 1, 1, 1, 1, 1, 3, 3, 2, 3, 1, 2, 3, 1, 1, 2, 3, 3, 1, 1, 1, 3, 2, 1, 4, 1, 3 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) { FILE *yyo = yyoutput; YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) { YYFPRINTF (yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, Rule); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YYUSE (yytype); YY_IGNORE_MAYBE_UNINITIALIZED_END } /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ int yyparse (void) { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: -#line 566 "sqlparser.y" /* yacc.c:1646 */ +#line 567 "sqlparser.y" /* yacc.c:1646 */ { //todo: multiple statements //todo: not only "select" statements parser->setOperation(Parser::OP_Select); parser->setQuerySchema((yyvsp[0].querySchema)); } -#line 1481 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1482 "sqlparser.cpp" /* yacc.c:1646 */ break; case 3: -#line 576 "sqlparser.y" /* yacc.c:1646 */ +#line 577 "sqlparser.y" /* yacc.c:1646 */ { //todo: multiple statements } -#line 1489 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1490 "sqlparser.cpp" /* yacc.c:1646 */ break; case 5: -#line 581 "sqlparser.y" /* yacc.c:1646 */ +#line 582 "sqlparser.y" /* yacc.c:1646 */ { (yyval.querySchema) = (yyvsp[-1].querySchema); } -#line 1497 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1498 "sqlparser.cpp" /* yacc.c:1646 */ break; case 6: -#line 596 "sqlparser.y" /* yacc.c:1646 */ +#line 597 "sqlparser.y" /* yacc.c:1646 */ { (yyval.querySchema) = (yyvsp[0].querySchema); } -#line 1505 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1506 "sqlparser.cpp" /* yacc.c:1646 */ break; case 7: -#line 694 "sqlparser.y" /* yacc.c:1646 */ +#line 695 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "Select ColViews=" << (yyvsp[0].exprList)->debugString(); if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-1].querySchema), (yyvsp[0].exprList) ))) return 0; } -#line 1516 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1517 "sqlparser.cpp" /* yacc.c:1646 */ break; case 8: -#line 701 "sqlparser.y" /* yacc.c:1646 */ +#line 702 "sqlparser.y" /* yacc.c:1646 */ { if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), (yyvsp[-1].exprList), (yyvsp[0].exprList) ))) return 0; } -#line 1525 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1526 "sqlparser.cpp" /* yacc.c:1646 */ break; case 9: -#line 706 "sqlparser.y" /* yacc.c:1646 */ +#line 707 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "Select ColViews Tables"; if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-1].querySchema), 0, (yyvsp[0].exprList) ))) return 0; } -#line 1535 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1536 "sqlparser.cpp" /* yacc.c:1646 */ break; case 10: -#line 712 "sqlparser.y" /* yacc.c:1646 */ +#line 713 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "Select ColViews Conditions"; if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), (yyvsp[-1].exprList), 0, (yyvsp[0].selectOptions) ))) return 0; } -#line 1545 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1546 "sqlparser.cpp" /* yacc.c:1646 */ break; case 11: -#line 718 "sqlparser.y" /* yacc.c:1646 */ +#line 719 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "Select ColViews Tables SelectOptions"; if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-3].querySchema), (yyvsp[-2].exprList), (yyvsp[-1].exprList), (yyvsp[0].selectOptions) ))) return 0; } -#line 1555 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1556 "sqlparser.cpp" /* yacc.c:1646 */ break; case 12: -#line 727 "sqlparser.y" /* yacc.c:1646 */ +#line 728 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "SELECT"; // parser->createSelect(); // parser->setOperation(Parser::OP_Select); - (yyval.querySchema) = new QuerySchema(); + (yyval.querySchema) = QuerySchemaPrivate::createQuery(parser->db()); } -#line 1566 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1567 "sqlparser.cpp" /* yacc.c:1646 */ break; case 13: -#line 737 "sqlparser.y" /* yacc.c:1646 */ +#line 738 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "WhereClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->whereExpr = (yyvsp[0].expr); } -#line 1576 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1577 "sqlparser.cpp" /* yacc.c:1646 */ break; case 14: -#line 743 "sqlparser.y" /* yacc.c:1646 */ +#line 744 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "OrderByClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->orderByColumns = (yyvsp[0].orderByColumns); } -#line 1586 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1587 "sqlparser.cpp" /* yacc.c:1646 */ break; case 15: -#line 749 "sqlparser.y" /* yacc.c:1646 */ +#line 750 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "WhereClause ORDER BY OrderByClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->whereExpr = (yyvsp[-3].expr); (yyval.selectOptions)->orderByColumns = (yyvsp[0].orderByColumns); } -#line 1597 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1598 "sqlparser.cpp" /* yacc.c:1646 */ break; case 16: -#line 756 "sqlparser.y" /* yacc.c:1646 */ +#line 757 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "OrderByClause WhereClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->whereExpr = (yyvsp[0].expr); (yyval.selectOptions)->orderByColumns = (yyvsp[-1].orderByColumns); } -#line 1608 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1609 "sqlparser.cpp" /* yacc.c:1646 */ break; case 17: -#line 766 "sqlparser.y" /* yacc.c:1646 */ +#line 767 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); } -#line 1616 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1617 "sqlparser.cpp" /* yacc.c:1646 */ break; case 18: -#line 775 "sqlparser.y" /* yacc.c:1646 */ +#line 776 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "ORDER BY IDENTIFIER"; (yyval.orderByColumns) = new OrderByColumnInternal::List; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[0].variantValue) ); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[0].variantValue); } -#line 1629 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1630 "sqlparser.cpp" /* yacc.c:1646 */ break; case 19: -#line 784 "sqlparser.y" /* yacc.c:1646 */ +#line 785 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "ORDER BY IDENTIFIER OrderByOption"; (yyval.orderByColumns) = new OrderByColumnInternal::List; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[-1].variantValue) ); orderByColumn.ascending = (yyvsp[0].booleanValue); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[-1].variantValue); } -#line 1643 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1644 "sqlparser.cpp" /* yacc.c:1646 */ break; case 20: -#line 794 "sqlparser.y" /* yacc.c:1646 */ +#line 795 "sqlparser.y" /* yacc.c:1646 */ { (yyval.orderByColumns) = (yyvsp[0].orderByColumns); OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[-2].variantValue) ); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[-2].variantValue); } -#line 1655 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1656 "sqlparser.cpp" /* yacc.c:1646 */ break; case 21: -#line 802 "sqlparser.y" /* yacc.c:1646 */ +#line 803 "sqlparser.y" /* yacc.c:1646 */ { (yyval.orderByColumns) = (yyvsp[0].orderByColumns); OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[-3].variantValue) ); orderByColumn.ascending = (yyvsp[-2].booleanValue); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[-3].variantValue); } -#line 1668 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1669 "sqlparser.cpp" /* yacc.c:1646 */ break; case 22: -#line 814 "sqlparser.y" /* yacc.c:1646 */ +#line 815 "sqlparser.y" /* yacc.c:1646 */ { (yyval.variantValue) = new QVariant( *(yyvsp[0].stringValue) ); KexiDBDbg << "OrderByColumnId: " << *(yyval.variantValue); delete (yyvsp[0].stringValue); } -#line 1678 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1679 "sqlparser.cpp" /* yacc.c:1646 */ break; case 23: -#line 820 "sqlparser.y" /* yacc.c:1646 */ +#line 821 "sqlparser.y" /* yacc.c:1646 */ { (yyval.variantValue) = new QVariant( *(yyvsp[-2].stringValue) + "." + *(yyvsp[0].stringValue) ); KexiDBDbg << "OrderByColumnId: " << *(yyval.variantValue); delete (yyvsp[-2].stringValue); delete (yyvsp[0].stringValue); } -#line 1689 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1690 "sqlparser.cpp" /* yacc.c:1646 */ break; case 24: -#line 827 "sqlparser.y" /* yacc.c:1646 */ +#line 828 "sqlparser.y" /* yacc.c:1646 */ { (yyval.variantValue) = new QVariant((yyvsp[0].integerValue)); KexiDBDbg << "OrderByColumnId: " << *(yyval.variantValue); } -#line 1698 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1699 "sqlparser.cpp" /* yacc.c:1646 */ break; case 25: -#line 834 "sqlparser.y" /* yacc.c:1646 */ +#line 835 "sqlparser.y" /* yacc.c:1646 */ { (yyval.booleanValue) = true; } -#line 1706 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1707 "sqlparser.cpp" /* yacc.c:1646 */ break; case 26: -#line 838 "sqlparser.y" /* yacc.c:1646 */ +#line 839 "sqlparser.y" /* yacc.c:1646 */ { (yyval.booleanValue) = false; } -#line 1714 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1715 "sqlparser.cpp" /* yacc.c:1646 */ break; case 28: -#line 850 "sqlparser.y" /* yacc.c:1646 */ +#line 851 "sqlparser.y" /* yacc.c:1646 */ { // KexiDBDbg << "AND " << $3.debugString(); (yyval.expr) = new BinaryExpr( KexiDBExpr_Logical, (yyvsp[-2].expr), AND, (yyvsp[0].expr) ); } -#line 1723 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1724 "sqlparser.cpp" /* yacc.c:1646 */ break; case 29: -#line 855 "sqlparser.y" /* yacc.c:1646 */ +#line 856 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr( KexiDBExpr_Logical, (yyvsp[-2].expr), OR, (yyvsp[0].expr) ); } -#line 1731 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1732 "sqlparser.cpp" /* yacc.c:1646 */ break; case 30: -#line 859 "sqlparser.y" /* yacc.c:1646 */ +#line 860 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr( KexiDBExpr_Arithm, (yyvsp[-2].expr), XOR, (yyvsp[0].expr) ); } -#line 1739 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1740 "sqlparser.cpp" /* yacc.c:1646 */ break; case 32: -#line 869 "sqlparser.y" /* yacc.c:1646 */ +#line 870 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), '>', (yyvsp[0].expr)); } -#line 1747 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1748 "sqlparser.cpp" /* yacc.c:1646 */ break; case 33: -#line 873 "sqlparser.y" /* yacc.c:1646 */ +#line 874 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), GREATER_OR_EQUAL, (yyvsp[0].expr)); } -#line 1755 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1756 "sqlparser.cpp" /* yacc.c:1646 */ break; case 34: -#line 877 "sqlparser.y" /* yacc.c:1646 */ +#line 878 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), '<', (yyvsp[0].expr)); } -#line 1763 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1764 "sqlparser.cpp" /* yacc.c:1646 */ break; case 35: -#line 881 "sqlparser.y" /* yacc.c:1646 */ +#line 882 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), LESS_OR_EQUAL, (yyvsp[0].expr)); } -#line 1771 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1772 "sqlparser.cpp" /* yacc.c:1646 */ break; case 36: -#line 885 "sqlparser.y" /* yacc.c:1646 */ +#line 886 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), '=', (yyvsp[0].expr)); } -#line 1779 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1780 "sqlparser.cpp" /* yacc.c:1646 */ break; case 38: -#line 895 "sqlparser.y" /* yacc.c:1646 */ +#line 896 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), NOT_EQUAL, (yyvsp[0].expr)); } -#line 1787 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1788 "sqlparser.cpp" /* yacc.c:1646 */ break; case 39: -#line 900 "sqlparser.y" /* yacc.c:1646 */ +#line 901 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), NOT_EQUAL2, (yyvsp[0].expr)); } -#line 1795 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1796 "sqlparser.cpp" /* yacc.c:1646 */ break; case 40: -#line 904 "sqlparser.y" /* yacc.c:1646 */ +#line 905 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), LIKE, (yyvsp[0].expr)); } -#line 1803 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1804 "sqlparser.cpp" /* yacc.c:1646 */ break; case 41: -#line 908 "sqlparser.y" /* yacc.c:1646 */ +#line 909 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), NOT_LIKE, (yyvsp[0].expr)); } -#line 1811 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1812 "sqlparser.cpp" /* yacc.c:1646 */ break; case 42: -#line 912 "sqlparser.y" /* yacc.c:1646 */ +#line 913 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), SQL_IN, (yyvsp[0].expr)); } -#line 1819 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1820 "sqlparser.cpp" /* yacc.c:1646 */ break; case 43: -#line 916 "sqlparser.y" /* yacc.c:1646 */ +#line 917 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), SIMILAR_TO, (yyvsp[0].expr)); } -#line 1827 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1828 "sqlparser.cpp" /* yacc.c:1646 */ break; case 44: -#line 920 "sqlparser.y" /* yacc.c:1646 */ +#line 921 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Relational, (yyvsp[-2].expr), NOT_SIMILAR_TO, (yyvsp[0].expr)); } -#line 1835 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1836 "sqlparser.cpp" /* yacc.c:1646 */ break; case 45: -#line 924 "sqlparser.y" /* yacc.c:1646 */ +#line 925 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new NArgExpr(KexiDBExpr_Relational, KEXIDB_TOKEN_BETWEEN_AND); (yyval.expr)->toNArg()->add( (yyvsp[-4].expr) ); (yyval.expr)->toNArg()->add( (yyvsp[-2].expr) ); (yyval.expr)->toNArg()->add( (yyvsp[0].expr) ); } -#line 1846 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1847 "sqlparser.cpp" /* yacc.c:1646 */ break; case 46: -#line 931 "sqlparser.y" /* yacc.c:1646 */ +#line 932 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new NArgExpr(KexiDBExpr_Relational, KEXIDB_TOKEN_NOT_BETWEEN_AND); (yyval.expr)->toNArg()->add( (yyvsp[-4].expr) ); (yyval.expr)->toNArg()->add( (yyvsp[-2].expr) ); (yyval.expr)->toNArg()->add( (yyvsp[0].expr) ); } -#line 1857 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1858 "sqlparser.cpp" /* yacc.c:1646 */ break; case 48: -#line 944 "sqlparser.y" /* yacc.c:1646 */ +#line 945 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnaryExpr( SQL_IS_NULL, (yyvsp[-1].expr) ); } -#line 1865 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1866 "sqlparser.cpp" /* yacc.c:1646 */ break; case 49: -#line 948 "sqlparser.y" /* yacc.c:1646 */ +#line 949 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnaryExpr( SQL_IS_NOT_NULL, (yyvsp[-1].expr) ); } -#line 1873 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1874 "sqlparser.cpp" /* yacc.c:1646 */ break; case 51: -#line 958 "sqlparser.y" /* yacc.c:1646 */ +#line 959 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), BITWISE_SHIFT_LEFT, (yyvsp[0].expr)); } -#line 1881 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1882 "sqlparser.cpp" /* yacc.c:1646 */ break; case 52: -#line 962 "sqlparser.y" /* yacc.c:1646 */ +#line 963 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), BITWISE_SHIFT_RIGHT, (yyvsp[0].expr)); } -#line 1889 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1890 "sqlparser.cpp" /* yacc.c:1646 */ break; case 54: -#line 972 "sqlparser.y" /* yacc.c:1646 */ +#line 973 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '+', (yyvsp[0].expr)); (yyval.expr)->debug(); } -#line 1898 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1899 "sqlparser.cpp" /* yacc.c:1646 */ break; case 55: -#line 977 "sqlparser.y" /* yacc.c:1646 */ +#line 978 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), CONCATENATION, (yyvsp[0].expr)); } -#line 1906 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1907 "sqlparser.cpp" /* yacc.c:1646 */ break; case 56: -#line 981 "sqlparser.y" /* yacc.c:1646 */ +#line 982 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '-', (yyvsp[0].expr)); } -#line 1914 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1915 "sqlparser.cpp" /* yacc.c:1646 */ break; case 57: -#line 985 "sqlparser.y" /* yacc.c:1646 */ +#line 986 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '&', (yyvsp[0].expr)); } -#line 1922 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1923 "sqlparser.cpp" /* yacc.c:1646 */ break; case 58: -#line 989 "sqlparser.y" /* yacc.c:1646 */ +#line 990 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '|', (yyvsp[0].expr)); } -#line 1930 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1931 "sqlparser.cpp" /* yacc.c:1646 */ break; case 60: -#line 999 "sqlparser.y" /* yacc.c:1646 */ +#line 1000 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '/', (yyvsp[0].expr)); } -#line 1938 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1939 "sqlparser.cpp" /* yacc.c:1646 */ break; case 61: -#line 1003 "sqlparser.y" /* yacc.c:1646 */ +#line 1004 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '*', (yyvsp[0].expr)); } -#line 1946 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1947 "sqlparser.cpp" /* yacc.c:1646 */ break; case 62: -#line 1007 "sqlparser.y" /* yacc.c:1646 */ +#line 1008 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr(KexiDBExpr_Arithm, (yyvsp[-2].expr), '%', (yyvsp[0].expr)); } -#line 1954 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1955 "sqlparser.cpp" /* yacc.c:1646 */ break; case 64: -#line 1018 "sqlparser.y" /* yacc.c:1646 */ +#line 1019 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnaryExpr( '-', (yyvsp[0].expr) ); } -#line 1962 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1963 "sqlparser.cpp" /* yacc.c:1646 */ break; case 65: -#line 1022 "sqlparser.y" /* yacc.c:1646 */ +#line 1023 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnaryExpr( '+', (yyvsp[0].expr) ); } -#line 1970 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1971 "sqlparser.cpp" /* yacc.c:1646 */ break; case 66: -#line 1026 "sqlparser.y" /* yacc.c:1646 */ +#line 1027 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnaryExpr( '~', (yyvsp[0].expr) ); } -#line 1978 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1979 "sqlparser.cpp" /* yacc.c:1646 */ break; case 67: -#line 1030 "sqlparser.y" /* yacc.c:1646 */ +#line 1031 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnaryExpr( NOT, (yyvsp[0].expr) ); } -#line 1986 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1987 "sqlparser.cpp" /* yacc.c:1646 */ break; case 68: -#line 1034 "sqlparser.y" /* yacc.c:1646 */ +#line 1035 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new VariableExpr( *(yyvsp[0].stringValue) ); //TODO: simplify this later if that's 'only one field name' expression KexiDBDbg << " + identifier: " << *(yyvsp[0].stringValue); delete (yyvsp[0].stringValue); } -#line 1998 "sqlparser.cpp" /* yacc.c:1646 */ +#line 1999 "sqlparser.cpp" /* yacc.c:1646 */ break; case 69: -#line 1042 "sqlparser.y" /* yacc.c:1646 */ +#line 1043 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new QueryParameterExpr( *(yyvsp[0].stringValue) ); KexiDBDbg << " + query parameter: " << (yyval.expr)->debugString(); delete (yyvsp[0].stringValue); } -#line 2008 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2009 "sqlparser.cpp" /* yacc.c:1646 */ break; case 70: -#line 1048 "sqlparser.y" /* yacc.c:1646 */ +#line 1049 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << " + function: " << *(yyvsp[-1].stringValue) << "(" << (yyvsp[0].exprList)->debugString() << ")"; (yyval.expr) = new FunctionExpr(*(yyvsp[-1].stringValue), (yyvsp[0].exprList)); delete (yyvsp[-1].stringValue); } -#line 2018 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2019 "sqlparser.cpp" /* yacc.c:1646 */ break; case 71: -#line 1055 "sqlparser.y" /* yacc.c:1646 */ +#line 1056 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new VariableExpr( *(yyvsp[-2].stringValue) + "." + *(yyvsp[0].stringValue) ); KexiDBDbg << " + identifier.identifier: " << *(yyvsp[-2].stringValue) << "." << *(yyvsp[0].stringValue); delete (yyvsp[-2].stringValue); delete (yyvsp[0].stringValue); } -#line 2029 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2030 "sqlparser.cpp" /* yacc.c:1646 */ break; case 72: -#line 1062 "sqlparser.y" /* yacc.c:1646 */ +#line 1063 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new ConstExpr( SQL_NULL, QVariant() ); KexiDBDbg << " + NULL"; // $$ = new Field(); //$$->setName(QString()); } -#line 2040 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2041 "sqlparser.cpp" /* yacc.c:1646 */ break; case 73: -#line 1069 "sqlparser.y" /* yacc.c:1646 */ +#line 1070 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new ConstExpr( CHARACTER_STRING_LITERAL, *(yyvsp[0].stringValue) ); KexiDBDbg << " + constant " << (yyvsp[0].stringValue); delete (yyvsp[0].stringValue); } -#line 2050 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2051 "sqlparser.cpp" /* yacc.c:1646 */ break; case 74: -#line 1075 "sqlparser.y" /* yacc.c:1646 */ +#line 1076 "sqlparser.y" /* yacc.c:1646 */ { QVariant val; if ((yyvsp[0].integerValue) <= INT_MAX && (yyvsp[0].integerValue) >= INT_MIN) val = (int)(yyvsp[0].integerValue); else if ((yyvsp[0].integerValue) <= UINT_MAX && (yyvsp[0].integerValue) >= 0) val = (uint)(yyvsp[0].integerValue); else if ((yyvsp[0].integerValue) <= LLONG_MAX && (yyvsp[0].integerValue) >= LLONG_MIN) val = (qint64)(yyvsp[0].integerValue); // if ($1 < ULLONG_MAX) // val = (quint64)$1; //TODO ok? (yyval.expr) = new ConstExpr( INTEGER_CONST, val ); KexiDBDbg << " + int constant: " << val.toString(); } -#line 2071 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2072 "sqlparser.cpp" /* yacc.c:1646 */ break; case 75: -#line 1092 "sqlparser.y" /* yacc.c:1646 */ +#line 1093 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new ConstExpr( REAL_CONST, QPoint( (yyvsp[0].realValue).integer, (yyvsp[0].realValue).fractional ) ); KexiDBDbg << " + real constant: " << (yyvsp[0].realValue).integer << "." << (yyvsp[0].realValue).fractional; } -#line 2080 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2081 "sqlparser.cpp" /* yacc.c:1646 */ break; case 77: -#line 1103 "sqlparser.y" /* yacc.c:1646 */ +#line 1104 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "(expr)"; (yyval.expr) = new UnaryExpr('(', (yyvsp[-1].expr)); } -#line 2089 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2090 "sqlparser.cpp" /* yacc.c:1646 */ break; case 78: -#line 1111 "sqlparser.y" /* yacc.c:1646 */ +#line 1112 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[-1].exprList); } -#line 2097 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2098 "sqlparser.cpp" /* yacc.c:1646 */ break; case 79: -#line 1115 "sqlparser.y" /* yacc.c:1646 */ +#line 1116 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = new NArgExpr(KexiDBExpr_ArgumentList, ','); } -#line 2105 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2106 "sqlparser.cpp" /* yacc.c:1646 */ break; case 80: -#line 1122 "sqlparser.y" /* yacc.c:1646 */ +#line 1123 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[0].exprList); (yyval.exprList)->prepend( (yyvsp[-2].expr) ); } -#line 2114 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2115 "sqlparser.cpp" /* yacc.c:1646 */ break; case 81: -#line 1127 "sqlparser.y" /* yacc.c:1646 */ +#line 1128 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = new NArgExpr(KexiDBExpr_ArgumentList, ','); (yyval.exprList)->add( (yyvsp[0].expr) ); } -#line 2123 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2124 "sqlparser.cpp" /* yacc.c:1646 */ break; case 82: -#line 1135 "sqlparser.y" /* yacc.c:1646 */ +#line 1136 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[0].exprList); } -#line 2131 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2132 "sqlparser.cpp" /* yacc.c:1646 */ break; case 83: -#line 1180 "sqlparser.y" /* yacc.c:1646 */ +#line 1181 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[-2].exprList); (yyval.exprList)->add((yyvsp[0].expr)); } -#line 2140 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2141 "sqlparser.cpp" /* yacc.c:1646 */ break; case 84: -#line 1185 "sqlparser.y" /* yacc.c:1646 */ +#line 1186 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = new NArgExpr(KexiDBExpr_TableList, IDENTIFIER); //ok? (yyval.exprList)->add((yyvsp[0].expr)); } -#line 2149 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2150 "sqlparser.cpp" /* yacc.c:1646 */ break; case 85: -#line 1193 "sqlparser.y" /* yacc.c:1646 */ +#line 1194 "sqlparser.y" /* yacc.c:1646 */ { KexiDBDbg << "FROM: '" << *(yyvsp[0].stringValue) << "'"; (yyval.expr) = new VariableExpr(*(yyvsp[0].stringValue)); /* //TODO: this isn't ok for more tables: Field::ListIterator it = parser->select()->fieldsIterator(); for(Field *item; (item = it.current()); ++it) { if(item->table() == dummy) { item->setTable(schema); } if(item->table() && !item->isQueryAsterisk()) { Field *f = item->table()->field(item->name()); if(!f) { ParserError err(i18n("Field List Error"), i18n("Unknown column '%1' in table '%2'",item->name(),schema->name()), ctoken, current); parser->setError(err); yyerror("fieldlisterror"); } } }*/ delete (yyvsp[0].stringValue); } -#line 2181 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2182 "sqlparser.cpp" /* yacc.c:1646 */ break; case 86: -#line 1221 "sqlparser.y" /* yacc.c:1646 */ +#line 1222 "sqlparser.y" /* yacc.c:1646 */ { //table + alias (yyval.expr) = new BinaryExpr( KexiDBExpr_SpecialBinary, new VariableExpr(*(yyvsp[-1].stringValue)), 0, new VariableExpr(*(yyvsp[0].stringValue)) ); delete (yyvsp[-1].stringValue); delete (yyvsp[0].stringValue); } -#line 2196 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2197 "sqlparser.cpp" /* yacc.c:1646 */ break; case 87: -#line 1232 "sqlparser.y" /* yacc.c:1646 */ +#line 1233 "sqlparser.y" /* yacc.c:1646 */ { //table + alias (yyval.expr) = new BinaryExpr( KexiDBExpr_SpecialBinary, new VariableExpr(*(yyvsp[-2].stringValue)), AS, new VariableExpr(*(yyvsp[0].stringValue)) ); delete (yyvsp[-2].stringValue); delete (yyvsp[0].stringValue); } -#line 2211 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2212 "sqlparser.cpp" /* yacc.c:1646 */ break; case 88: -#line 1248 "sqlparser.y" /* yacc.c:1646 */ +#line 1249 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[-2].exprList); (yyval.exprList)->add( (yyvsp[0].expr) ); KexiDBDbg << "ColViews: ColViews , ColItem"; } -#line 2221 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2222 "sqlparser.cpp" /* yacc.c:1646 */ break; case 89: -#line 1254 "sqlparser.y" /* yacc.c:1646 */ +#line 1255 "sqlparser.y" /* yacc.c:1646 */ { (yyval.exprList) = new NArgExpr(0,0); (yyval.exprList)->add( (yyvsp[0].expr) ); KexiDBDbg << "ColViews: ColItem"; } -#line 2231 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2232 "sqlparser.cpp" /* yacc.c:1646 */ break; case 90: -#line 1263 "sqlparser.y" /* yacc.c:1646 */ +#line 1264 "sqlparser.y" /* yacc.c:1646 */ { // $$ = new Field(); // dummy->addField($$); // $$->setExpression( $1 ); // parser->select()->addField($$); (yyval.expr) = (yyvsp[0].expr); KexiDBDbg << " added column expr: '" << (yyvsp[0].expr)->debugString() << "'"; } -#line 2244 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2245 "sqlparser.cpp" /* yacc.c:1646 */ break; case 91: -#line 1272 "sqlparser.y" /* yacc.c:1646 */ +#line 1273 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); KexiDBDbg << " added column wildcard: '" << (yyvsp[0].expr)->debugString() << "'"; } -#line 2253 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2254 "sqlparser.cpp" /* yacc.c:1646 */ break; case 92: -#line 1277 "sqlparser.y" /* yacc.c:1646 */ +#line 1278 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr( KexiDBExpr_SpecialBinary, (yyvsp[-2].expr), AS, new VariableExpr(*(yyvsp[0].stringValue)) ); KexiDBDbg << " added column expr: " << (yyval.expr)->debugString(); delete (yyvsp[0].stringValue); } -#line 2266 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2267 "sqlparser.cpp" /* yacc.c:1646 */ break; case 93: -#line 1286 "sqlparser.y" /* yacc.c:1646 */ +#line 1287 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new BinaryExpr( KexiDBExpr_SpecialBinary, (yyvsp[-1].expr), 0, new VariableExpr(*(yyvsp[0].stringValue)) ); KexiDBDbg << " added column expr: " << (yyval.expr)->debugString(); delete (yyvsp[0].stringValue); } -#line 2279 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2280 "sqlparser.cpp" /* yacc.c:1646 */ break; case 94: -#line 1298 "sqlparser.y" /* yacc.c:1646 */ +#line 1299 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); } -#line 2287 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2288 "sqlparser.cpp" /* yacc.c:1646 */ break; case 95: -#line 1342 "sqlparser.y" /* yacc.c:1646 */ +#line 1343 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[-1].expr); //TODO // $$->setName("DISTINCT(" + $3->name() + ")"); } -#line 2297 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2298 "sqlparser.cpp" /* yacc.c:1646 */ break; case 96: -#line 1351 "sqlparser.y" /* yacc.c:1646 */ +#line 1352 "sqlparser.y" /* yacc.c:1646 */ { (yyval.expr) = new VariableExpr("*"); KexiDBDbg << "all columns"; // QueryAsterisk *ast = new QueryAsterisk(parser->select(), dummy); // parser->select()->addAsterisk(ast); // requiresTable = true; } -#line 2310 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2311 "sqlparser.cpp" /* yacc.c:1646 */ break; case 97: -#line 1360 "sqlparser.y" /* yacc.c:1646 */ +#line 1361 "sqlparser.y" /* yacc.c:1646 */ { QString s( *(yyvsp[-2].stringValue) ); s += ".*"; (yyval.expr) = new VariableExpr(s); KexiDBDbg << " + all columns from " << s; delete (yyvsp[-2].stringValue); } -#line 2322 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2323 "sqlparser.cpp" /* yacc.c:1646 */ break; -#line 2326 "sqlparser.cpp" /* yacc.c:1646 */ +#line 2327 "sqlparser.cpp" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } -#line 1375 "sqlparser.y" /* yacc.c:1906 */ +#line 1376 "sqlparser.y" /* yacc.c:1906 */ const char* tokenName(unsigned int offset) { return yytname[YYTRANSLATE(offset)]; } unsigned int maxToken() { return YYMAXUTOK; } diff --git a/libs/db/parser/sqlparser.h b/libs/db/parser/sqlparser.h index dde4199009..fb9f8cd02b 100644 --- a/libs/db/parser/sqlparser.h +++ b/libs/db/parser/sqlparser.h @@ -1,155 +1,155 @@ #ifndef _SQLPARSER_H_ #define _SQLPARSER_H_ #include #include "parser.h" #include "sqltypes.h" bool parseData(KexiDB::Parser *p, const char *data); const char* tokenName(unsigned int offset); unsigned int maxToken(); /* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ #ifndef YY_YY_SQLPARSER_TAB_H_INCLUDED # define YY_YY_SQLPARSER_TAB_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 1 #endif #if YYDEBUG extern int yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { SQL_TYPE = 258, AS = 259, ASC = 260, AUTO_INCREMENT = 261, BIT = 262, BITWISE_SHIFT_LEFT = 263, BITWISE_SHIFT_RIGHT = 264, BY = 265, CHARACTER_STRING_LITERAL = 266, CONCATENATION = 267, CREATE = 268, DESC = 269, DISTINCT = 270, DOUBLE_QUOTED_STRING = 271, FROM = 272, JOIN = 273, KEY = 274, LEFT = 275, LESS_OR_EQUAL = 276, SQL_NULL = 277, SQL_IS = 278, SQL_IS_NULL = 279, SQL_IS_NOT_NULL = 280, ORDER = 281, PRIMARY = 282, SELECT = 283, INTEGER_CONST = 284, REAL_CONST = 285, RIGHT = 286, SQL_ON = 287, DATE_CONST = 288, DATETIME_CONST = 289, TIME_CONST = 290, TABLE = 291, IDENTIFIER = 292, IDENTIFIER_DOT_ASTERISK = 293, QUERY_PARAMETER = 294, VARCHAR = 295, WHERE = 296, SCAN_ERROR = 297, UNION = 298, EXCEPT = 299, INTERSECT = 300, OR = 301, AND = 302, XOR = 303, NOT = 304, GREATER_OR_EQUAL = 305, NOT_EQUAL = 306, NOT_EQUAL2 = 307, SQL_IN = 308, LIKE = 309, NOT_LIKE = 310, ILIKE = 311, SIMILAR_TO = 312, NOT_SIMILAR_TO = 313, SIMILAR = 314, BETWEEN = 315, NOT_BETWEEN = 316, UMINUS = 317 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 476 "sqlparser.y" /* yacc.c:1909 */ +#line 477 "sqlparser.y" /* yacc.c:1909 */ QString* stringValue; qint64 integerValue; bool booleanValue; struct realType realValue; KexiDB::Field::Type colType; KexiDB::Field *field; KexiDB::BaseExpr *expr; KexiDB::NArgExpr *exprList; KexiDB::ConstExpr *constExpr; KexiDB::QuerySchema *querySchema; SelectOptionsInternal *selectOptions; OrderByColumnInternal::List *orderByColumns; QVariant *variantValue; #line 133 "sqlparser.tab.h" /* yacc.c:1909 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; int yyparse (void); #endif /* !YY_YY_SQLPARSER_TAB_H_INCLUDED */ #endif diff --git a/libs/db/parser/sqlparser.y b/libs/db/parser/sqlparser.y index 12ab0180dc..ebaad2cca0 100644 --- a/libs/db/parser/sqlparser.y +++ b/libs/db/parser/sqlparser.y @@ -1,1376 +1,1377 @@ /* This file is part of the KDE project Copyright (C) 2004 Lucijan Busch Copyright (C) 2004-2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // To keep binary compatibility, do not reorder tokens! Add new only at the end. %token SQL_TYPE %token AS %token ASC %token AUTO_INCREMENT %token BIT %token BITWISE_SHIFT_LEFT %token BITWISE_SHIFT_RIGHT %token BY %token CHARACTER_STRING_LITERAL %token CONCATENATION /* || */ %token CREATE %token DESC %token DISTINCT %token DOUBLE_QUOTED_STRING %token FROM %token JOIN %token KEY %token LEFT %token LESS_OR_EQUAL %token SQL_NULL %token SQL_IS %token SQL_IS_NULL /*helper */ %token SQL_IS_NOT_NULL /*helper */ %token ORDER %token PRIMARY %token SELECT %token INTEGER_CONST %token REAL_CONST %token RIGHT %token SQL_ON %token DATE_CONST %token DATETIME_CONST %token TIME_CONST %token TABLE %token IDENTIFIER %token IDENTIFIER_DOT_ASTERISK %token QUERY_PARAMETER %token VARCHAR %token WHERE %token SCAN_ERROR //%token UMINUS //%token SQL_ABS //%token ACOS //%token AMPERSAND //%token SQL_ABSOLUTE //%token ADA //%token ADD //%token ADD_DAYS //%token ADD_HOURS //%token ADD_MINUTES //%token ADD_MONTHS //%token ADD_SECONDS //%token ADD_YEARS //%token ALL //%token ALLOCATE //%token ALTER //%token AND //%token ANY //%token ARE //%token ASIN //%token ASCII //%token ASSERTION //%token ATAN //%token ATAN2 //%token AUTHORIZATION //%token AVG //%token BEFORE //%token SQL_BEGIN //%token BIGINT //%token BINARY //%token BIT_LENGTH //%token BREAK //%token CASCADE //%token CASCADED //%token CASE //%token CAST //%token CATALOG //%token CEILING //%token CENTER //%token SQL_CHAR //%token CHAR_LENGTH //%token CHECK //%token CLOSE //%token COALESCE //%token COBOL //%token COLLATE //%token COLLATION //%token COLUMN //%token COMMIT //%token COMPUTE //%token CONCAT //%token CONNECT //%token CONNECTION //%token CONSTRAINT //%token CONSTRAINTS //%token CONTINUE //%token CONVERT //%token CORRESPONDING //%token COS //%token COT //%token COUNT //%token CURDATE //%token CURRENT //%token CURRENT_DATE //%token CURRENT_TIME //%token CURRENT_TIMESTAMP //%token CURTIME //%token CURSOR //%token DATABASE //%token SQL_DATE //%token DATE_FORMAT //%token DATE_REMAINDER //%token DATE_VALUE //%token DAY //%token DAYOFMONTH //%token DAYOFWEEK //%token DAYOFYEAR //%token DAYS_BETWEEN //%token DEALLOCATE //%token DEC //%token DECLARE //%token DEFAULT //%token DEFERRABLE //%token DEFERRED //%token SQL_DELETE //%token DESCRIBE //%token DESCRIPTOR //%token DIAGNOSTICS //%token DICTIONARY //%token DIRECTORY //%token DISCONNECT //%token DISPLACEMENT //%token DOMAIN_TOKEN //%token SQL_DOUBLE //%token DROP //%token ELSE //%token END //%token END_EXEC //%token ESCAPE //%token SQL_EXCEPTION //%token EXEC //%token EXECUTE //%token EXISTS //%token EXP //%token EXPONENT //%token EXTERNAL //%token EXTRACT //%token SQL_FALSE //%token FETCH //%token FIRST //%token SQL_FLOAT //%token FLOOR //%token FN //%token FOR //%token FOREIGN //%token FORTRAN //%token FOUND //%token FOUR_DIGITS //%token FULL //%token GET //%token GLOBAL //%token GO //%token GOTO //%token GRANT //conflict %token GROUP //%token HAVING //%token HOUR //%token HOURS_BETWEEN //%token IDENTITY //%token IFNULL //%token SQL_IGNORE //%token IMMEDIATE //%token INCLUDE //%token INDEX //%token INDICATOR //%token INITIALLY //%token INNER //%token SQL_INPUT //%token INSENSITIVE //%token INSERT //%token INTEGER //%token INTERVAL //%token INTO //%token IS //%token ISOLATION //%token JUSTIFY //%token LANGUAGE //%token LAST //%token LCASE //%token LENGTH //%token LEVEL //%token LINE_WIDTH //%token LOCAL //%token LOCATE //%token LOG //%token SQL_LONG //%token LOWER //%token LTRIM //%token LTRIP //%token MATCH //%token SQL_MAX //%token MICROSOFT //%token SQL_MIN //%token MINUTE //%token MINUTES_BETWEEN //%token MOD //%token MODIFY //%token MODULE //%token MONTH //%token MONTHS_BETWEEN //%token MUMPS //%token NAMES //%token NATIONAL //%token NCHAR //%token NEXT //%token NODUP //%token NONE //%token NOT //%token NOW //%token NULLIF //%token NUMERIC //%token OCTET_LENGTH //%token ODBC //%token OF //%token SQL_OFF //%token ONLY //%token OPEN //%token OPTION //%token OUTER //%token OUTPUT //%token OVERLAPS //%token PAGE //%token PARTIAL //%token SQL_PASCAL //%token PERSISTENT //%token CQL_PI //%token PLI //%token POSITION //%token PRECISION //%token PREPARE //%token PRESERVE //%token PRIOR //%token PRIVILEGES //%token PROCEDURE //%token PRODUCT //%token PUBLIC //%token QUARTER //%token QUIT //%token RAND //%token READ_ONLY //%token REAL //%token REFERENCES //%token REPEAT //%token REPLACE //%token RESTRICT //%token REVOKE //%token ROLLBACK //%token ROWS //%token RPAD //%token RTRIM //%token SCHEMA //%token SCREEN_WIDTH //%token SCROLL //%token SECOND //%token SECONDS_BETWEEN //%token SEQUENCE //%token SETOPT //%token SET //%token SHOWOPT //%token SIGN //%token SIMILAR //%token SIN //%token SQL_SIZE //%token SMALLINT //%token SOME //%token SPACE //%token SQL //%token SQL_TRUE //%token SQLCA //%token SQLCODE //%token SQLERROR //%token SQLSTATE //%token SQLWARNING //%token SQRT //%token STDEV //%token SUBSTRING //%token SUM //%token SYSDATE //%token SYSDATE_FORMAT //%token SYSTEM //%token TAN //%token TEMPORARY //%token THEN //%token THREE_DIGITS //%token TIME //%token TIMESTAMP //%token TIMEZONE_HOUR //%token TIMEZONE_MINUTE //%token TINYINT //%token TO //%token TO_CHAR //%token TO_DATE //%token TRANSACTION //%token TRANSLATE //%token TRANSLATION //%token TRUNCATE //%token GENERAL_TITLE //%token TWO_DIGITS //%token UCASE //%token UNIQUE //%token SQL_UNKNOWN //%token UNSIGNED_INTEGER //%token UPDATE //%token UPPER //%token USAGE //%token USER //%token ERROR_DIGIT_BEFORE_IDENTIFIER //%token USING //%token VALUE //%token VALUES //%token VARBINARY //%token VARYING //%token VENDOR //%token VIEW //%token WEEK //%token WHEN //%token WHENEVER //%token WHERE_CURRENT_OF //%token WITH //%token WORD_WRAPPED //%token WORK //%token WRAPPED //%token XOR //%token YEAR //%token YEARS_BETWEEN %token '@' %token ';' %token ',' %token '$' %token '?' %token '\'' %type IDENTIFIER %type IDENTIFIER_DOT_ASTERISK %type QUERY_PARAMETER %type CHARACTER_STRING_LITERAL %type DOUBLE_QUOTED_STRING /* %type ColExpression %type ColView */ %type ColExpression %type ColWildCard //%type ColView %type ColItem %type ColViews %type aExpr %type aExpr2 %type aExpr3 %type aExpr4 %type aExpr5 %type aExpr6 %type aExpr7 %type aExpr8 %type aExpr9 %type aExpr10 %type aExprList %type aExprList2 %type WhereClause %type OrderByClause %type OrderByOption %type OrderByColumnId %type SelectOptions %type FlatTable %type Tables %type FlatTableList %type SelectStatement %type Select /*todo : list*/ %type StatementList /*todo: not onlu select*/ %type Statement %type SQL_TYPE %type INTEGER_CONST %type REAL_CONST /*%type SIGNED_INTEGER */ %{ #include #include #include #include #include #include //TODO OK? #ifdef Q_WS_WIN //workaround for bug on msvc # undef LLONG_MIN #endif #ifndef LLONG_MAX # define LLONG_MAX 0x7fffffffffffffffLL #endif #ifndef LLONG_MIN # define LLONG_MIN 0x8000000000000000LL #endif #ifndef LLONG_MAX # define ULLONG_MAX 0xffffffffffffffffLL #endif #ifdef _WIN32 # include #endif #include #include #include #include #include #include #include +#include #include #include #include "parser.h" #include "parser_p.h" #include "sqltypes.h" #ifdef Q_OS_SOLARIS #include #endif int yylex(); using namespace KexiDB; #define YY_NO_UNPUT #define YYSTACK_USE_ALLOCA 1 #define YYMAXDEPTH 255 extern "C" { int yywrap() { return 1; } } %} %union { QString* stringValue; qint64 integerValue; bool booleanValue; struct realType realValue; KexiDB::Field::Type colType; KexiDB::Field *field; KexiDB::BaseExpr *expr; KexiDB::NArgExpr *exprList; KexiDB::ConstExpr *constExpr; KexiDB::QuerySchema *querySchema; SelectOptionsInternal *selectOptions; OrderByColumnInternal::List *orderByColumns; QVariant *variantValue; } /* precedence: lowest to highest */ %token UNION %token EXCEPT %token INTERSECT %token OR %token AND %token XOR %token NOT %token '=' %token '<' %token '>' %token GREATER_OR_EQUAL %token NOT_EQUAL %token NOT_EQUAL2 %token SQL_IN %token LIKE %token NOT_LIKE %token ILIKE %token SIMILAR_TO %token NOT_SIMILAR_TO %token SIMILAR //%nonassoc ESCAPE //%nonassoc OVERLAPS %token BETWEEN %token NOT_BETWEEN //%nonassoc IN_P //%left POSTFIXOP // dummy for postfix Op rules //%left Op OPERATOR // multi-character ops and user-defined operators //%nonassoc NOTNULL //%nonassoc ISNULL //%nonassoc IS // sets precedence for IS NULL, etc //%nonassoc NULL_P //%nonassoc TRUE_P //%nonassoc FALSE_P //%nonassoc UNKNOWN %token '+' %token '-' %token '*' %token '/' %token '%' %token '^' %token UMINUS // Unary Operators //%token AT ZONE // sets precedence for AT TIME ZONE //%right UMINUS %token '[' %token ']' %token '(' %token ')' //%token TYPECAST %token '.' /* * These might seem to be low-precedence, but actually they are not part * of the arithmetic hierarchy at all in their use as JOIN operators. * We make them high-precedence to support their use as function names. * They wouldn't be given a precedence at all, were it not that we need * left-associativity among the JOIN rules themselves. */ /* %left JOIN %left UNIONJOIN %left CROSS %left LEFT %left FULL %left RIGHT %left INNER_P %left NATURAL */ %% TopLevelStatement : StatementList { //todo: multiple statements //todo: not only "select" statements parser->setOperation(Parser::OP_Select); parser->setQuerySchema($1); } ; StatementList: Statement ';' StatementList { //todo: multiple statements } | Statement | Statement ';' { $$ = $1; } ; /* Statement CreateTableStatement { YYACCEPT; } | Statement SelectStatement { } */ Statement : /*CreateTableStatement { YYACCEPT; } |*/ SelectStatement { $$ = $1; } ; /*CreateTableStatement : CREATE TABLE IDENTIFIER { parser->setOperation(Parser::OP_CreateTable); parser->createTable($3->toLatin1()); delete $3; } '(' ColDefs ')' ; ColDefs: ColDefs ',' ColDef|ColDef { } ; ColDef: IDENTIFIER ColType { KexiDBDbg << "adding field " << *$1; field->setName($1->toLatin1()); parser->table()->addField(field); field = 0; delete $1; } | IDENTIFIER ColType ColKeys { KexiDBDbg << "adding field " << *$1; field->setName(*$1); delete $1; parser->table()->addField(field); // if(field->isPrimaryKey()) // parser->table()->addPrimaryKey(field->name()); // delete field; // field = 0; } ; ColKeys: ColKeys ColKey|ColKey { } ; ColKey: PRIMARY KEY { field->setPrimaryKey(true); KexiDBDbg << "primary"; } | NOT SQL_NULL { field->setNotNull(true); KexiDBDbg << "not_null"; } | AUTO_INCREMENT { field->setAutoIncrement(true); KexiDBDbg << "ainc"; } ; ColType: SQL_TYPE { field = new Field(); field->setType($1); } | SQL_TYPE '(' INTEGER_CONST ')' { KexiDBDbg << "sql + length"; field = new Field(); field->setPrecision($3); field->setType($1); } | VARCHAR '(' INTEGER_CONST ')' { field = new Field(); field->setPrecision($3); field->setType(Field::Text); } | { // SQLITE compatibillity field = new Field(); field->setType(Field::InvalidType); } ;*/ SelectStatement: Select ColViews { KexiDBDbg << "Select ColViews=" << $2->debugString(); if (!($$ = buildSelectQuery( $1, $2 ))) return 0; } | Select ColViews Tables { if (!($$ = buildSelectQuery( $1, $2, $3 ))) return 0; } | Select Tables { KexiDBDbg << "Select ColViews Tables"; if (!($$ = buildSelectQuery( $1, 0, $2 ))) return 0; } | Select ColViews SelectOptions { KexiDBDbg << "Select ColViews Conditions"; if (!($$ = buildSelectQuery( $1, $2, 0, $3 ))) return 0; } | Select ColViews Tables SelectOptions { KexiDBDbg << "Select ColViews Tables SelectOptions"; if (!($$ = buildSelectQuery( $1, $2, $3, $4 ))) return 0; } ; Select: SELECT { KexiDBDbg << "SELECT"; // parser->createSelect(); // parser->setOperation(Parser::OP_Select); - $$ = new QuerySchema(); + $$ = QuerySchemaPrivate::createQuery(parser->db()); } ; SelectOptions: /* todo: more options (having, group by, limit...) */ WhereClause { KexiDBDbg << "WhereClause"; $$ = new SelectOptionsInternal; $$->whereExpr = $1; } | ORDER BY OrderByClause { KexiDBDbg << "OrderByClause"; $$ = new SelectOptionsInternal; $$->orderByColumns = $3; } | WhereClause ORDER BY OrderByClause { KexiDBDbg << "WhereClause ORDER BY OrderByClause"; $$ = new SelectOptionsInternal; $$->whereExpr = $1; $$->orderByColumns = $4; } | ORDER BY OrderByClause WhereClause { KexiDBDbg << "OrderByClause WhereClause"; $$ = new SelectOptionsInternal; $$->whereExpr = $4; $$->orderByColumns = $3; } ; WhereClause: WHERE aExpr { $$ = $2; } ; /* todo: support "ORDER BY NULL" as described here http://dev.mysql.com/doc/refman/5.1/en/select.html */ /* todo: accept expr and position as well */ OrderByClause: OrderByColumnId { KexiDBDbg << "ORDER BY IDENTIFIER"; $$ = new OrderByColumnInternal::List; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); $$->append( orderByColumn ); delete $1; } | OrderByColumnId OrderByOption { KexiDBDbg << "ORDER BY IDENTIFIER OrderByOption"; $$ = new OrderByColumnInternal::List; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); orderByColumn.ascending = $2; $$->append( orderByColumn ); delete $1; } | OrderByColumnId ',' OrderByClause { $$ = $3; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); $$->append( orderByColumn ); delete $1; } | OrderByColumnId OrderByOption ',' OrderByClause { $$ = $4; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); orderByColumn.ascending = $2; $$->append( orderByColumn ); delete $1; } ; OrderByColumnId: IDENTIFIER { $$ = new QVariant( *$1 ); KexiDBDbg << "OrderByColumnId: " << *$$; delete $1; } | IDENTIFIER '.' IDENTIFIER { $$ = new QVariant( *$1 + "." + *$3 ); KexiDBDbg << "OrderByColumnId: " << *$$; delete $1; delete $3; } | INTEGER_CONST { $$ = new QVariant($1); KexiDBDbg << "OrderByColumnId: " << *$$; } OrderByOption: ASC { $$ = true; } | DESC { $$ = false; } ; aExpr: aExpr2 ; /* --- binary logical --- */ aExpr2: aExpr3 AND aExpr2 { // KexiDBDbg << "AND " << $3.debugString(); $$ = new BinaryExpr( KexiDBExpr_Logical, $1, AND, $3 ); } | aExpr3 OR aExpr2 { $$ = new BinaryExpr( KexiDBExpr_Logical, $1, OR, $3 ); } | aExpr3 XOR aExpr2 { $$ = new BinaryExpr( KexiDBExpr_Arithm, $1, XOR, $3 ); } | aExpr3 ; /* relational op precedence */ aExpr3: aExpr4 '>' %prec GREATER_OR_EQUAL aExpr3 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, '>', $3); } | aExpr4 GREATER_OR_EQUAL aExpr3 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, GREATER_OR_EQUAL, $3); } | aExpr4 '<' %prec LESS_OR_EQUAL aExpr3 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, '<', $3); } | aExpr4 LESS_OR_EQUAL aExpr3 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, LESS_OR_EQUAL, $3); } | aExpr4 '=' aExpr3 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, '=', $3); } | aExpr4 ; /* relational (equality) op precedence */ aExpr4: aExpr5 NOT_EQUAL aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, NOT_EQUAL, $3); } | aExpr5 NOT_EQUAL2 aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, NOT_EQUAL2, $3); } | aExpr5 LIKE aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, LIKE, $3); } | aExpr5 NOT_LIKE aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, NOT_LIKE, $3); } | aExpr5 SQL_IN aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, SQL_IN, $3); } | aExpr5 SIMILAR_TO aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, SIMILAR_TO, $3); } | aExpr5 NOT_SIMILAR_TO aExpr4 { $$ = new BinaryExpr(KexiDBExpr_Relational, $1, NOT_SIMILAR_TO, $3); } | aExpr5 BETWEEN aExpr4 AND aExpr4 { $$ = new NArgExpr(KexiDBExpr_Relational, KEXIDB_TOKEN_BETWEEN_AND); $$->toNArg()->add( $1 ); $$->toNArg()->add( $3 ); $$->toNArg()->add( $5 ); } | aExpr5 NOT_BETWEEN aExpr4 AND aExpr4 { $$ = new NArgExpr(KexiDBExpr_Relational, KEXIDB_TOKEN_NOT_BETWEEN_AND); $$->toNArg()->add( $1 ); $$->toNArg()->add( $3 ); $$->toNArg()->add( $5 ); } | aExpr5 ; /* --- unary logical right --- */ aExpr5: aExpr5 SQL_IS_NULL { $$ = new UnaryExpr( SQL_IS_NULL, $1 ); } | aExpr5 SQL_IS_NOT_NULL { $$ = new UnaryExpr( SQL_IS_NOT_NULL, $1 ); } | aExpr6 ; /* arithm. lowest precedence */ aExpr6: aExpr7 BITWISE_SHIFT_LEFT aExpr6 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, BITWISE_SHIFT_LEFT, $3); } | aExpr7 BITWISE_SHIFT_RIGHT aExpr6 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, BITWISE_SHIFT_RIGHT, $3); } | aExpr7 ; /* arithm. lower precedence */ aExpr7: aExpr8 '+' aExpr7 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '+', $3); $$->debug(); } | aExpr8 CONCATENATION aExpr7 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, CONCATENATION, $3); } | aExpr8 '-' %prec UMINUS aExpr7 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '-', $3); } | aExpr8 '&' aExpr7 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '&', $3); } | aExpr8 '|' aExpr7 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '|', $3); } | aExpr8 ; /* arithm. higher precedence */ aExpr8: aExpr9 '/' aExpr8 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '/', $3); } | aExpr9 '*' aExpr8 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '*', $3); } | aExpr9 '%' aExpr8 { $$ = new BinaryExpr(KexiDBExpr_Arithm, $1, '%', $3); } | aExpr9 ; /* parenthesis, unary operators, and terminals precedence */ aExpr9: /* --- unary logical left --- */ '-' aExpr9 { $$ = new UnaryExpr( '-', $2 ); } | '+' aExpr9 { $$ = new UnaryExpr( '+', $2 ); } | '~' aExpr9 { $$ = new UnaryExpr( '~', $2 ); } | NOT aExpr9 { $$ = new UnaryExpr( NOT, $2 ); } | IDENTIFIER { $$ = new VariableExpr( *$1 ); //TODO: simplify this later if that's 'only one field name' expression KexiDBDbg << " + identifier: " << *$1; delete $1; } | QUERY_PARAMETER { $$ = new QueryParameterExpr( *$1 ); KexiDBDbg << " + query parameter: " << $$->debugString(); delete $1; } | IDENTIFIER aExprList { KexiDBDbg << " + function: " << *$1 << "(" << $2->debugString() << ")"; $$ = new FunctionExpr(*$1, $2); delete $1; } /*TODO: shall we also support db name? */ | IDENTIFIER '.' IDENTIFIER { $$ = new VariableExpr( *$1 + "." + *$3 ); KexiDBDbg << " + identifier.identifier: " << *$1 << "." << *$3; delete $1; delete $3; } | SQL_NULL { $$ = new ConstExpr( SQL_NULL, QVariant() ); KexiDBDbg << " + NULL"; // $$ = new Field(); //$$->setName(QString()); } | CHARACTER_STRING_LITERAL { $$ = new ConstExpr( CHARACTER_STRING_LITERAL, *$1 ); KexiDBDbg << " + constant " << $1; delete $1; } | INTEGER_CONST { QVariant val; if ($1 <= INT_MAX && $1 >= INT_MIN) val = (int)$1; else if ($1 <= UINT_MAX && $1 >= 0) val = (uint)$1; else if ($1 <= LLONG_MAX && $1 >= LLONG_MIN) val = (qint64)$1; // if ($1 < ULLONG_MAX) // val = (quint64)$1; //TODO ok? $$ = new ConstExpr( INTEGER_CONST, val ); KexiDBDbg << " + int constant: " << val.toString(); } | REAL_CONST { $$ = new ConstExpr( REAL_CONST, QPoint( $1.integer, $1.fractional ) ); KexiDBDbg << " + real constant: " << $1.integer << "." << $1.fractional; } | aExpr10 ; aExpr10: '(' aExpr ')' { KexiDBDbg << "(expr)"; $$ = new UnaryExpr('(', $2); } ; aExprList: '(' aExprList2 ')' { $$ = $2; } | '(' ')' { $$ = new NArgExpr(KexiDBExpr_ArgumentList, ','); } ; aExprList2: aExpr ',' aExprList2 { $$ = $3; $$->prepend( $1 ); } | aExpr { $$ = new NArgExpr(KexiDBExpr_ArgumentList, ','); $$->add( $1 ); } ; Tables: FROM FlatTableList { $$ = $2; } /* | Tables LEFT JOIN IDENTIFIER SQL_ON ColExpression { KexiDBDbg << "LEFT JOIN: '" << *$4 << "' ON " << $6; addTable($4->toQString()); delete $4; } | Tables LEFT OUTER JOIN IDENTIFIER SQL_ON ColExpression { KexiDBDbg << "LEFT OUTER JOIN: '" << $5 << "' ON " << $7; addTable($5); } | Tables INNER JOIN IDENTIFIER SQL_ON ColExpression { KexiDBDbg << "INNER JOIN: '" << *$4 << "' ON " << $6; addTable($4->toQString()); delete $4; } | Tables RIGHT JOIN IDENTIFIER SQL_ON ColExpression { KexiDBDbg << "RIGHT JOIN: '" << *$4 << "' ON " << $6; addTable(*$4); delete $4; } | Tables RIGHT OUTER JOIN IDENTIFIER SQL_ON ColExpression { KexiDBDbg << "RIGHT OUTER JOIN: '" << *$5 << "' ON " << $7; addTable($5->toQString()); delete $5; }*/ ; /* FlatTableList: aFlatTableList { $$ } ;*/ FlatTableList: FlatTableList ',' FlatTable { $$ = $1; $$->add($3); } |FlatTable { $$ = new NArgExpr(KexiDBExpr_TableList, IDENTIFIER); //ok? $$->add($1); } ; FlatTable: IDENTIFIER { KexiDBDbg << "FROM: '" << *$1 << "'"; $$ = new VariableExpr(*$1); /* //TODO: this isn't ok for more tables: Field::ListIterator it = parser->select()->fieldsIterator(); for(Field *item; (item = it.current()); ++it) { if(item->table() == dummy) { item->setTable(schema); } if(item->table() && !item->isQueryAsterisk()) { Field *f = item->table()->field(item->name()); if(!f) { ParserError err(i18n("Field List Error"), i18n("Unknown column '%1' in table '%2'",item->name(),schema->name()), ctoken, current); parser->setError(err); yyerror("fieldlisterror"); } } }*/ delete $1; } | IDENTIFIER IDENTIFIER { //table + alias $$ = new BinaryExpr( KexiDBExpr_SpecialBinary, new VariableExpr(*$1), 0, new VariableExpr(*$2) ); delete $1; delete $2; } | IDENTIFIER AS IDENTIFIER { //table + alias $$ = new BinaryExpr( KexiDBExpr_SpecialBinary, new VariableExpr(*$1), AS, new VariableExpr(*$3) ); delete $1; delete $3; } ; ColViews: ColViews ',' ColItem { $$ = $1; $$->add( $3 ); KexiDBDbg << "ColViews: ColViews , ColItem"; } |ColItem { $$ = new NArgExpr(0,0); $$->add( $1 ); KexiDBDbg << "ColViews: ColItem"; } ; ColItem: ColExpression { // $$ = new Field(); // dummy->addField($$); // $$->setExpression( $1 ); // parser->select()->addField($$); $$ = $1; KexiDBDbg << " added column expr: '" << $1->debugString() << "'"; } | ColWildCard { $$ = $1; KexiDBDbg << " added column wildcard: '" << $1->debugString() << "'"; } | ColExpression AS IDENTIFIER { $$ = new BinaryExpr( KexiDBExpr_SpecialBinary, $1, AS, new VariableExpr(*$3) ); KexiDBDbg << " added column expr: " << $$->debugString(); delete $3; } | ColExpression IDENTIFIER { $$ = new BinaryExpr( KexiDBExpr_SpecialBinary, $1, 0, new VariableExpr(*$2) ); KexiDBDbg << " added column expr: " << $$->debugString(); delete $2; } ; ColExpression: aExpr { $$ = $1; } /* HANDLED BY 'IDENTIFIER aExprList' | IDENTIFIER '(' ColViews ')' { $$ = new FunctionExpr( $1, $3 ); }*/ /* | SUM '(' ColExpression ')' { FunctionExpr( // $$ = new AggregationExpr( SUM, ); //TODO // $$->setName("SUM(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait parser->select()->grouped(true); } | SQL_MIN '(' ColExpression ')' { $$ = $3; //TODO // $$->setName("MIN(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait parser->select()->grouped(true); } | SQL_MAX '(' ColExpression ')' { $$ = $3; //TODO // $$->setName("MAX(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait parser->select()->grouped(true); } | AVG '(' ColExpression ')' { $$ = $3; //TODO // $$->setName("AVG(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait parser->select()->grouped(true); }*/ //? | DISTINCT '(' ColExpression ')' { $$ = $3; //TODO // $$->setName("DISTINCT(" + $3->name() + ")"); } ; ColWildCard: '*' { $$ = new VariableExpr("*"); KexiDBDbg << "all columns"; // QueryAsterisk *ast = new QueryAsterisk(parser->select(), dummy); // parser->select()->addAsterisk(ast); // requiresTable = true; } | IDENTIFIER '.' '*' { QString s( *$1 ); s += ".*"; $$ = new VariableExpr(s); KexiDBDbg << " + all columns from " << s; delete $1; } /*| ERROR_DIGIT_BEFORE_IDENTIFIER { $$ = new VariableExpr($1); KexiDBDbg << " Invalid identifier! " << $1; setError(i18n("Invalid identifier \"%1\"",$1)); }*/ ; %% diff --git a/libs/db/queryschema.cpp b/libs/db/queryschema.cpp index bb71f979c1..5fee017630 100644 --- a/libs/db/queryschema.cpp +++ b/libs/db/queryschema.cpp @@ -1,1920 +1,1605 @@ /* This file is part of the KDE project Copyright (C) 2003-2012 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "queryschema.h" +#include "queryschema_p.h" #include "driver.h" #include "connection.h" #include "expression.h" #include "parser/sqlparser.h" #include "utils.h" #include "lookupfieldschema.h" #include -#include - #include #include using namespace KexiDB; QueryColumnInfo::QueryColumnInfo(Field *f, const QByteArray& _alias, bool _visible, QueryColumnInfo *foreignColumn) : field(f), alias(_alias), visible(_visible), m_indexForVisibleLookupValue(-1) , m_foreignColumn(foreignColumn) { } QueryColumnInfo::~QueryColumnInfo() { } QString QueryColumnInfo::debugString() const { QString res; if (field->table()) { res += field->table()->name() + QLatin1Char('.'); } res += field->debugString() + (alias.isEmpty() ? QString() : (QLatin1String(" AS ") + QString(alias))) + (visible ? QString() : QLatin1String(" [INVISIBLE]")); return res; } -//======================================= -namespace KexiDB -{ -//! @internal -class QuerySchemaPrivate -{ -public: - explicit QuerySchemaPrivate(QuerySchema* q, QuerySchemaPrivate* copy = 0) - : query(q) - , masterTable(0) - , fakeRowIDField(0) - , fakeRowIDCol(0) - , maxIndexWithAlias(-1) - , visibility(64) - , fieldsExpanded(0) - , internalFields(0) - , fieldsExpandedWithInternalAndRowID(0) - , fieldsExpandedWithInternal(0) - , orderByColumnList(0) - , autoincFields(0) - , columnsOrder(0) - , columnsOrderWithoutAsterisks(0) - , columnsOrderExpanded(0) - , pkeyFieldsOrder(0) - , pkeyFieldsCount(0) - , tablesBoundToColumns(64, -1) // will be resized if needed - , whereExpr(0) - , ownedVisibleColumns(0) - , regenerateExprAliases(false) { -//Qt 4 columnAliases.setAutoDelete(true); -//Qt 4 tableAliases.setAutoDelete(true); -//Qt 4 asterisks.setAutoDelete(true); -//Qt 4 relations.setAutoDelete(true); -//Qt 4 tablePositionsForAliases.setAutoDelete(true); -//Qt 4 columnPositionsForAliases.setAutoDelete(true); - visibility.fill(false); - if (copy) { - // deep copy - *this = *copy; - // - fieldsExpanded = 0; - internalFields = 0; - columnsOrder = 0; - columnsOrderWithoutAsterisks = 0; - columnsOrderExpanded = 0; - orderByColumnList = 0; - autoincFields = 0; - autoIncrementSQLFieldsList.clear(); - columnInfosByNameExpanded.clear(); - columnInfosByName.clear(); - ownedVisibleColumns = 0; - fieldsExpandedWithInternalAndRowID = 0; - fieldsExpandedWithInternal = 0; - pkeyFieldsOrder = 0; - fakeRowIDCol = 0; - fakeRowIDField = 0; - ownedVisibleColumns = 0; - // - if (copy->whereExpr) { - whereExpr = copy->whereExpr->copy(); - } - // "*this = *copy" causes copying pointers; pull of them without destroying, - // will be deep-copied in the QuerySchema ctor. - asterisks.setAutoDelete(false); - asterisks.clear(); - asterisks.setAutoDelete(true); - } - else { - orderByColumnList = new OrderByColumnList; - } - } - ~QuerySchemaPrivate() { - delete orderByColumnList; - delete autoincFields; - delete columnsOrder; - delete columnsOrderWithoutAsterisks; - delete columnsOrderExpanded; - delete pkeyFieldsOrder; - delete whereExpr; - delete fakeRowIDCol; - delete fakeRowIDField; - delete ownedVisibleColumns; - if (fieldsExpanded) - qDeleteAll(*fieldsExpanded); - delete fieldsExpanded; - if (internalFields) { - qDeleteAll(*internalFields); - delete internalFields; - } - delete fieldsExpandedWithInternalAndRowID; - delete fieldsExpandedWithInternal; - } - - void clear() { - columnAliases.clear(); - tableAliases.clear(); - asterisks.clear(); - relations.clear(); - masterTable = 0; - tables.clear(); - clearCachedData(); - delete pkeyFieldsOrder; - pkeyFieldsOrder = 0; - visibility.fill(false); - tablesBoundToColumns = QVector(64, -1); // will be resized if needed - tablePositionsForAliases.clear(); - columnPositionsForAliases.clear(); - } - - void clearCachedData() { - if (orderByColumnList) { - orderByColumnList->clear(); - } - if (fieldsExpanded) { - delete columnsOrder; - columnsOrder = 0; - delete columnsOrderWithoutAsterisks; - columnsOrderWithoutAsterisks = 0; - delete columnsOrderExpanded; - columnsOrderExpanded = 0; - delete autoincFields; - autoincFields = 0; - autoIncrementSQLFieldsList.clear(); - columnInfosByNameExpanded.clear(); - columnInfosByName.clear(); - delete ownedVisibleColumns; - ownedVisibleColumns = 0; - qDeleteAll(*fieldsExpanded); - delete fieldsExpanded; - fieldsExpanded = 0; - if (internalFields) { - qDeleteAll(*internalFields); - delete internalFields; - internalFields = 0; - } - } - } - - inline void setColumnAlias(uint position, const QByteArray& alias) { - if (alias.isEmpty()) { - columnAliases.remove(position); - maxIndexWithAlias = -1; - } else { - setColumnAliasInternal(position, alias); - } - } - - inline void setTableAlias(uint position, const QByteArray& alias) { - tableAliases.insert(position, alias.toLower()); - tablePositionsForAliases.insert(alias.toLower(), position); - } - - inline bool hasColumnAliases() { - tryRegenerateExprAliases(); - return !columnAliases.isEmpty(); - } - - inline QByteArray columnAlias(uint position) { - tryRegenerateExprAliases(); - return columnAliases.value(position); - } - - inline bool hasColumnAlias(uint position) { - tryRegenerateExprAliases(); - return columnAliases.contains(position); - } - - inline void removeTablePositionForAlias(const QByteArray& alias) { - tablePositionsForAliases.remove(alias.toLower()); - } - - inline int tablePositionForAlias(const QByteArray& alias) const { - return tablePositionsForAliases.value(alias.toLower(), -1); - } - - inline int columnPositionForAlias(const QByteArray& alias) const { - return columnPositionsForAliases.value(alias.toLower(), -1); - } - - QuerySchema *query; - - /*! Master table of the query. (may be NULL) - Any data modifications can be performed if we know master table. - If null, query's records cannot be modified. */ - TableSchema *masterTable; - - /*! List of tables used in this query */ - TableSchema::List tables; - - Field *fakeRowIDField; //! used to mark a place for ROWID - QueryColumnInfo *fakeRowIDCol; //! used to mark a place for ROWID - -protected: - void tryRegenerateExprAliases() { - if (!regenerateExprAliases) - return; - //regenerate missing aliases for experessions - uint colNum = 0; //used to generate a name - QByteArray columnAlias; - uint p = -1; - foreach(Field* f, *query->fields()) { - p++; - if (f->isExpression() && columnAliases.value(p).isEmpty()) { - //missing - do { //find 1st unused - colNum++; - columnAlias = (i18nc("short for 'expression' word, e.g. 'expr' (only latin letters, please, no '.')", "expr") - .toLatin1() + QByteArray::number(colNum)); - } while (-1 != tablePositionForAlias(columnAlias)); - - setColumnAliasInternal(p, columnAlias); - } - } - regenerateExprAliases = false; - } - - void setColumnAliasInternal(uint position, const QByteArray& alias) { - columnAliases.insert(position, alias.toLower()); - columnPositionsForAliases.insert(alias.toLower(), position); - maxIndexWithAlias = qMax(maxIndexWithAlias, (int)position); - } - - /*! Used to mapping columns to its aliases for this query */ - QHash columnAliases; - - /*! Collects table positions for aliases: used in tablePositionForAlias(). */ - QHash tablePositionsForAliases; - - /*! Collects column positions for aliases: used in columnPositionForAlias(). */ - QHash columnPositionsForAliases; - -public: - /*! Used to mapping tables to its aliases for this query */ - QHash tableAliases; - - /*! Helper used with aliases */ - int maxIndexWithAlias; - - /*! Helper used with tableAliases */ - int maxIndexWithTableAlias; - - /*! Used to store visibility flag for every field */ - QBitArray visibility; - - /*! List of asterisks defined for this query */ - Field::List asterisks; - - /*! Temporary field vector for using in fieldsExpanded() */ - QueryColumnInfo::Vector *fieldsExpanded; - - /*! Temporary field vector containing internal fields used for lookup columns. */ - QueryColumnInfo::Vector *internalFields; - - /*! Temporary, used to cache sum of expanded fields and internal fields (+rowid) used for lookup columns. - Contains not auto-deleted items.*/ - QueryColumnInfo::Vector *fieldsExpandedWithInternalAndRowID; - - /*! Temporary, used to cache sum of expanded fields and internal fields used for lookup columns. - Contains not auto-deleted items.*/ - QueryColumnInfo::Vector *fieldsExpandedWithInternal; - - /*! A list of fields for ORDER BY section. @see QuerySchema::orderByColumnList(). */ - OrderByColumnList* orderByColumnList; - - /*! A cache for autoIncrementFields(). */ - QueryColumnInfo::List *autoincFields; - - /*! A cache for autoIncrementSQLFieldsList(). */ - QString autoIncrementSQLFieldsList; - QPointer lastUsedDriverForAutoIncrementSQLFieldsList; - - /*! A hash for fast lookup of query columns' order (unexpanded version). */ - QHash *columnsOrder; - - /*! A hash for fast lookup of query columns' order (unexpanded version without asterisks). */ - QHash *columnsOrderWithoutAsterisks; - - /*! A hash for fast lookup of query columns' order. - This is exactly opposite information compared to vector returned - by fieldsExpanded() */ - QHash *columnsOrderExpanded; - -// QValueList detailedVisibility; - - /*! order of PKEY fields (e.g. for updateRow() ) */ - QVector *pkeyFieldsOrder; - - /*! number of PKEY fields within the query */ - uint pkeyFieldsCount; - - /*! forced (predefined) statement */ - QString statement; - - /*! Relationships defined for this query. */ - Relationship::List relations; - - /*! Information about columns bound to tables. - Used if table is used in FROM section more than once - (using table aliases). - - This list is updated by insertField(uint position, Field *field, - int bindToTable, bool visible), using bindToTable parameter. - - Example: for this statement: - SELECT t1.a, othertable.x, t2.b FROM table t1, table t2, othertable; - tablesBoundToColumns list looks like this: - [ 0, -1, 1 ] - - first column is bound to table 0 "t1" - - second coulmn is not specially bound (othertable.x isn't ambiguous) - - third column is bound to table 1 "t2" - */ - QVector tablesBoundToColumns; - - /*! WHERE expression */ - BaseExpr *whereExpr; - - QHash columnInfosByNameExpanded; - - QHash columnInfosByName; //!< Same as columnInfosByNameExpanded but asterisks are skipped - - //! field schemas created for multiple joined columns like a||' '||b||' '||c - Field::List *ownedVisibleColumns; - - /*! Set by insertField(): true, if aliases for expression columns should - be generated on next columnAlias() call. */ - bool regenerateExprAliases : 1; -}; -} - //======================================= OrderByColumn::OrderByColumn() : m_column(0) , m_pos(-1) , m_field(0) , m_ascending(true) { } OrderByColumn::OrderByColumn(QueryColumnInfo& column, bool ascending, int pos) : m_column(&column) , m_pos(pos) , m_field(0) , m_ascending(ascending) { } OrderByColumn::OrderByColumn(Field& field, bool ascending) : m_column(0) , m_pos(-1) , m_field(&field) , m_ascending(ascending) { } OrderByColumn* OrderByColumn::copy(QuerySchema* fromQuery, QuerySchema* toQuery) const { //kDebug() << "this=" << this << debugString() << "m_column=" << m_column; if (m_field) { return new OrderByColumn(*m_field, m_ascending); } if (m_column) { QueryColumnInfo* columnInfo; if (fromQuery && toQuery) { int columnIndex = fromQuery->columnsOrder().value(m_column); if (columnIndex < 0) { kDebug() << "index not found for column" << m_column->debugString(); return 0; } columnInfo = toQuery->expandedOrInternalField(columnIndex); if (!columnInfo) { kDebug() << "column info not found at index" << columnIndex << "in toQuery"; return 0; } } else { columnInfo = m_column; } return new OrderByColumn(*columnInfo, m_ascending, m_pos); } Q_ASSERT(m_field || m_column); return 0; } OrderByColumn::~OrderByColumn() { //kDebug() << this << debugString(); } QString OrderByColumn::debugString() const { QString orderString(m_ascending ? "ascending" : "descending"); if (m_column) { if (m_pos > -1) return QString("COLUMN_AT_POSITION_%1(%2, %3)") .arg(m_pos + 1).arg(m_column->debugString()).arg(orderString); else return QString("COLUMN(%1, %2)").arg(m_column->debugString()).arg(orderString); } return m_field ? QString("FIELD(%1, %2)").arg(m_field->debugString()).arg(orderString) : QString("NONE"); } QString OrderByColumn::toSQLString(bool includeTableName, const Driver *drv, int identifierEscaping) const { //kDebug() << this << debugString(); const QString orderString(m_ascending ? "" : " DESC"); QString fieldName, tableName, collationString; if (m_column) { if (m_pos > -1) return QString::number(m_pos + 1) + orderString; else { if (includeTableName && m_column->alias.isEmpty()) { tableName = KexiDB::escapeIdentifier( drv, m_column->field->table()->name(), identifierEscaping) + '.'; } fieldName = KexiDB::escapeIdentifier(drv, m_column->aliasOrName(), identifierEscaping); } if (m_column->field->isTextType() && drv) { collationString = drv->collationSQL(); } } else { if (m_field && includeTableName) { tableName = KexiDB::escapeIdentifier( drv, m_field->table()->name(), identifierEscaping) + '.'; } fieldName = KexiDB::escapeIdentifier(drv, m_field ? m_field->name() : "??"/*error*/, identifierEscaping); if (m_field && m_field->isTextType() && drv) { collationString = drv->collationSQL(); } } return tableName + fieldName + collationString + orderString; } //======================================= OrderByColumnList::OrderByColumnList() : OrderByColumnListBase() { } OrderByColumnList::OrderByColumnList(const OrderByColumnList& other, QuerySchema* fromQuery, QuerySchema* toQuery) : OrderByColumnListBase() { for (QList::ConstIterator it(other.constBegin()); it != other.constEnd(); ++it) { OrderByColumn* order = (*it)->copy(fromQuery, toQuery); if (order) { append(order); } } } bool OrderByColumnList::appendFields(QuerySchema& querySchema, const QString& field1, bool ascending1, const QString& field2, bool ascending2, const QString& field3, bool ascending3, const QString& field4, bool ascending4, const QString& field5, bool ascending5) { uint numAdded = 0; #define ADD_COL(fieldName, ascending) \ if (ok && !fieldName.isEmpty()) { \ if (!appendField( querySchema, fieldName, ascending )) \ ok = false; \ else \ numAdded++; \ } bool ok = true; ADD_COL(field1, ascending1); ADD_COL(field2, ascending2); ADD_COL(field3, ascending3); ADD_COL(field4, ascending4); ADD_COL(field5, ascending5); #undef ADD_COL if (ok) return true; for (uint i = 0; i < numAdded; i++) removeLast(); return false; } OrderByColumnList::~OrderByColumnList() { qDeleteAll(begin(), end()); } void OrderByColumnList::appendColumn(QueryColumnInfo& columnInfo, bool ascending) { append(new OrderByColumn(columnInfo, ascending)); } bool OrderByColumnList::appendColumn(QuerySchema& querySchema, bool ascending, int pos) { QueryColumnInfo::Vector fieldsExpanded(querySchema.fieldsExpanded()); QueryColumnInfo* ci = (pos < 0 || pos >= (int)fieldsExpanded.size()) ? 0 : fieldsExpanded[pos]; if (!ci) return false; append(new OrderByColumn(*ci, ascending, pos)); return true; } void OrderByColumnList::appendField(Field& field, bool ascending) { append(new OrderByColumn(field, ascending)); } bool OrderByColumnList::appendField(QuerySchema& querySchema, const QString& fieldName, bool ascending) { QueryColumnInfo *columnInfo = querySchema.columnInfo(fieldName); if (columnInfo) { append(new OrderByColumn(*columnInfo, ascending)); return true; } Field *field = querySchema.findTableField(fieldName); if (field) { append(new OrderByColumn(*field, ascending)); return true; } KexiDBWarn << "no such field" << fieldName; return false; } QString OrderByColumnList::debugString() const { if (isEmpty()) return "NONE"; QString dbg; for (QList::ConstIterator it(constBegin()); it != constEnd(); ++it) { if (!dbg.isEmpty()) dbg += "\n"; dbg += (*it)->debugString(); } return dbg; } QString OrderByColumnList::toSQLString(bool includeTableNames, const Driver *drv, int identifierEscaping) const { QString string; for (QList::ConstIterator it(constBegin()); it != constEnd(); ++it) { if (!string.isEmpty()) string += ", "; string += (*it)->toSQLString(includeTableNames, drv, identifierEscaping); } return string; } void OrderByColumnList::clear() { qDeleteAll(begin(), end()); OrderByColumnListBase::clear(); } //======================================= QuerySchema::QuerySchema() : FieldList(false)//fields are not owned by QuerySchema object , SchemaData(KexiDB::QueryObjectType) , d(new QuerySchemaPrivate(this)) { init(); } +QuerySchema::QuerySchema(Connection *conn) + : FieldList(false)//fields are not owned by QuerySchema object + , SchemaData(KexiDB::QueryObjectType) + , d(new QuerySchemaPrivate(this)) +{ + init(); + d->conn = conn; +} + QuerySchema::QuerySchema(TableSchema& tableSchema) : FieldList(false) , SchemaData(KexiDB::QueryObjectType) , d(new QuerySchemaPrivate(this)) { d->masterTable = &tableSchema; + d->conn = tableSchema.connection(); init(); /*if (!d->masterTable) { KexiDBWarn << "!d->masterTable"; m_name.clear(); return; }*/ addTable(d->masterTable); //defaults: //inherit name from a table m_name = d->masterTable->name(); //inherit caption from a table m_caption = d->masterTable->caption(); //replaced by explicit field list: //add all fields of the table as asterisk: //replaced by explicit field list: addField( new QueryAsterisk(this) ); // add explicit field list to avoid problems (e.g. with fields added outside of Kexi): foreach(Field* f, *d->masterTable->fields()) { addField(f); } } QuerySchema::QuerySchema(const QuerySchema& querySchema) : FieldList(querySchema, false /* !deepCopyFields */) , SchemaData(querySchema) , d(new QuerySchemaPrivate(this, querySchema.d)) { //only deep copy query asterisks foreach(Field* f, querySchema.m_fields) { Field *copiedField; if (dynamic_cast(f)) { copiedField = f->copy(); if (static_cast(f->m_parent) == &querySchema) { copiedField->m_parent = this; } } else { copiedField = f; } addField(copiedField); } // this deep copy must be after the 'd' initialization because fieldsExpanded() is used there d->orderByColumnList = new OrderByColumnList(*querySchema.d->orderByColumnList, const_cast(&querySchema), this); } QuerySchema::~QuerySchema() { delete d; } void QuerySchema::init() { m_type = KexiDB::QueryObjectType; //m_fields_by_name.setAutoDelete( true ); //because we're using QueryColumnInfoEntry objects } void QuerySchema::clear() { FieldList::clear(); SchemaData::clear(); d->clear(); } FieldList& QuerySchema::insertField(uint position, Field *field, bool visible) { return insertField(position, field, -1/*don't bind*/, visible); } /*virtual*/ FieldList& QuerySchema::insertField(uint position, Field *field) { return insertField(position, field, -1/*don't bind*/, true); } FieldList& QuerySchema::insertField(uint position, Field *field, int bindToTable, bool visible) { if (!field) { KexiDBWarn << "!field"; return *this; } if (position > (uint)m_fields.count()) { KexiDBWarn << "position" << position << "out of range"; return *this; } if (!field->isQueryAsterisk() && !field->isExpression() && !field->table()) { KexiDBWarn << "field" << field->name() << "must contain table information!"; return *this; } if ((int)fieldCount() >= d->visibility.size()) { d->visibility.resize(d->visibility.size()*2); d->tablesBoundToColumns.resize(d->tablesBoundToColumns.size()*2); } d->clearCachedData(); FieldList::insertField(position, field); if (field->isQueryAsterisk()) { //kDebug() << "d->asterisks.append:" << field; //field->debug(); d->asterisks.append(field); //if this is single-table asterisk, //add a table to list if doesn't exist there: if (field->table() && !d->tables.contains(field->table())) d->tables.append(field->table()); } else if (field->table()) { //add a table to list if doesn't exist there: if (!d->tables.contains(field->table())) d->tables.append(field->table()); } // //visible by default // setFieldVisible(field, true); // d->visibility.setBit(fieldCount()-1, visible); //update visibility //--move bits to make a place for a new one for (uint i = fieldCount() - 1; i > position; i--) d->visibility.setBit(i, d->visibility.testBit(i - 1)); d->visibility.setBit(position, visible); //bind to table if (bindToTable < -1 || bindToTable > int(d->tables.count())) { KexiDBWarn << "bindToTable" << bindToTable << "out of range"; bindToTable = -1; } //--move items to make a place for a new one for (uint i = fieldCount() - 1; i > position; i--) d->tablesBoundToColumns[i] = d->tablesBoundToColumns[i-1]; d->tablesBoundToColumns[ position ] = bindToTable; /* debug: KexiDBDbg << "bound to table" << bindToTable << ":"; if (bindToTable == -1) KexiDBDbg << " "; else KexiDBDbg << " name=" << d->tables.at(bindToTable)->name() << " alias=" << tableAlias(bindToTable); QString s; for (uint i = 0; i < fieldCount();i++) s += (QString::number(d->tablesBoundToColumns[i]) + " "); KexiDBDbg << "tablesBoundToColumns == [" << s << "]";*/ if (field->isExpression()) d->regenerateExprAliases = true; return *this; } int QuerySchema::tableBoundToColumn(uint columnPosition) const { int res = d->tablesBoundToColumns.value(columnPosition, -99); if (res == -99) { KexiDBWarn << "columnPosition" << columnPosition << "out of range"; return -1; } return res; } KexiDB::FieldList& QuerySchema::addField(KexiDB::Field* field, bool visible) { return insertField(m_fields.count(), field, visible); } KexiDB::FieldList& QuerySchema::addField(KexiDB::Field* field, int bindToTable, bool visible) { return insertField(m_fields.count(), field, bindToTable, visible); } bool QuerySchema::removeField(KexiDB::Field *field) { int indexOfAsterisk = -1; if (field->isQueryAsterisk()) { indexOfAsterisk = d->asterisks.indexOf(field); } if (!FieldList::removeField(field)) { return false; } d->clearCachedData(); if (indexOfAsterisk >= 0) { //kDebug() << "d->asterisks.removeAt:" << field; //field->debug(); d->asterisks.removeAt(indexOfAsterisk); //this will destroy this asterisk } //! @todo should we also remove table for this field or asterisk? return true; } FieldList& QuerySchema::addExpression(BaseExpr* expr, bool visible) { return addField(new Field(this, expr), visible); } bool QuerySchema::isColumnVisible(uint position) const { return (position < fieldCount()) ? d->visibility.testBit(position) : false; } void QuerySchema::setColumnVisible(uint position, bool v) { if (position < fieldCount()) d->visibility.setBit(position, v); } FieldList& QuerySchema::addAsterisk(QueryAsterisk *asterisk, bool visible) { if (!asterisk) return *this; //make unique name asterisk->m_name = (asterisk->table() ? asterisk->table()->name() + ".*" : QString(QLatin1Char('*'))) + QString::number(asterisks()->count()); return addField(asterisk, visible); } Connection* QuerySchema::connection() const { - TableSchema *mt = masterTable(); - return mt ? mt->connection() : 0; + if (d->conn) + return d->conn; + if (!d->tables.isEmpty()) { + return d->tables.first()->connection(); + } + return 0; } QString QuerySchema::debugString() const { QString dbg; dbg.reserve(1024); //fields TableSchema *mt = masterTable(); dbg = QString("QUERY ") + schemaDataDebugString() + '\n' + "-masterTable=" + (mt ? mt->name() : "") + "\n-COLUMNS:\n" + ((fieldCount() > 0) ? FieldList::debugString() : "") + '\n' + "-FIELDS EXPANDED "; QString dbg1; uint fieldsExpandedCount = 0; if (fieldCount() > 0) { QueryColumnInfo::Vector fe(const_cast(this)->fieldsExpanded()); fieldsExpandedCount = fe.size(); for (uint i = 0; i < fieldsExpandedCount; i++) { QueryColumnInfo *ci = fe[i]; if (!dbg1.isEmpty()) dbg1 += ",\n"; dbg1 += ci->debugString(); } dbg1 += "\n"; } else { dbg1 = "\n"; } dbg1.prepend(QString("(%1):\n").arg(fieldsExpandedCount)); dbg += dbg1; //it's safer to delete fieldsExpanded for now // (debugString() could be called before all fields are added) //causes a crash d->clearCachedData(); //bindings QString dbg2; dbg2.reserve(512); for (uint i = 0; i < fieldCount(); i++) { int tablePos = tableBoundToColumn(i); if (tablePos >= 0) { QByteArray tAlias(tableAlias(tablePos)); if (!tAlias.isEmpty()) { dbg2 += (QString::fromLatin1(" field \"") + FieldList::field(i)->name() + "\" uses alias \"" + QString(tAlias) + "\" of table \"" + d->tables.at(tablePos)->name() + "\"\n"); } } } if (!dbg2.isEmpty()) { dbg += "\n-BINDINGS:\n" + dbg2; } //tables QString table_names; table_names.reserve(512); foreach(TableSchema *table, d->tables) { if (!table_names.isEmpty()) table_names += ", "; table_names += (QLatin1Char('\'') + table->name() + QLatin1Char('\'')); } if (d->tables.isEmpty()) table_names = ""; dbg += (QString("-TABLES:\n") + table_names); QString aliases; if (!d->hasColumnAliases()) aliases = "\n"; else { int i = -1; foreach(Field *f, m_fields) { i++; QByteArray alias(d->columnAlias(i)); if (!alias.isEmpty()) aliases += (QString("field #%1: ").arg(i) + (f->name().isEmpty() ? "" : f->name()) + " -> " + alias + "\n"); } } //aliases dbg += QString("\n-COLUMN ALIASES:\n" + aliases); if (d->tableAliases.isEmpty()) aliases = ""; else { aliases.clear(); int i = -1; foreach(TableSchema* table, d->tables) { i++; QByteArray alias(d->tableAliases.value(i)); if (!alias.isEmpty()) aliases += (QString("table #%1: ").arg(i) + (table->name().isEmpty() ? "" : table->name()) + " -> " + alias + "\n"); } } dbg += QString("\n-TABLE ALIASES:\n" + aliases); QString where = d->whereExpr ? d->whereExpr->debugString() : QString(); if (!where.isEmpty()) dbg += (QString("\n-WHERE EXPRESSION:\n") + where); if (!orderByColumnList().isEmpty()) dbg += (QString("\n-ORDER BY (%1):\n").arg(orderByColumnList().count()) + orderByColumnList().debugString()); return dbg; } TableSchema* QuerySchema::masterTable() const { if (d->masterTable) return d->masterTable; if (d->tables.isEmpty()) return 0; //try to find master table if there's only one table (with possible aliasses) QString tableNameLower; int num = -1; foreach(TableSchema *table, d->tables) { num++; if (!tableNameLower.isEmpty() && table->name().toLower() != tableNameLower) { //two or more different tables return 0; } tableNameLower = tableAlias(num); } return d->tables.first(); } void QuerySchema::setMasterTable(TableSchema *table) { if (table) d->masterTable = table; } TableSchema::List* QuerySchema::tables() const { return &d->tables; } void QuerySchema::addTable(TableSchema *table, const QByteArray& alias) { KexiDBDbg << (void *)table << "alias=" << alias; if (!table) return; // only append table if: it has alias or it has no alias but there is no such table on the list if (alias.isEmpty() && d->tables.contains(table)) { int num = -1; foreach(TableSchema *t, d->tables) { num++; if (0 == t->name().compare(table->name(), Qt::CaseInsensitive)) { if (tableAlias(num).isEmpty()) { KexiDBDbg << "table" << table->name() << "without alias already added"; return; } } } } d->tables.append(table); if (!alias.isEmpty()) setTableAlias(d->tables.count() - 1, alias); } void QuerySchema::removeTable(TableSchema *table) { if (!table) return; if (d->masterTable == table) d->masterTable = 0; d->tables.removeAt(d->tables.indexOf(table)); //! @todo remove fields! } TableSchema* QuerySchema::table(const QString& tableName) const { //! @todo maybe use tables_byname? foreach(TableSchema *table, d->tables) { if (table->name().toLower() == tableName.toLower()) return table; } return 0; } bool QuerySchema::contains(TableSchema *table) const { return d->tables.contains(table); } Field* QuerySchema::findTableField(const QString &tableOrTableAndFieldName) const { QString tableName, fieldName; if (!KexiDB::splitToTableAndFieldParts(tableOrTableAndFieldName, tableName, fieldName, KexiDB::SetFieldNameIfNoTableName)) { return 0; } if (tableName.isEmpty()) { foreach(TableSchema *table, d->tables) { if (table->field(fieldName)) return table->field(fieldName); } return 0; } TableSchema *tableSchema = table(tableName); if (!tableSchema) return 0; return tableSchema->field(fieldName); } QByteArray QuerySchema::columnAlias(uint position) const { return d->columnAlias(position); } bool QuerySchema::hasColumnAlias(uint position) const { return d->hasColumnAlias(position); } void QuerySchema::setColumnAlias(uint position, const QByteArray& alias) { if (position >= (uint)m_fields.count()) { KexiDBWarn << "position" << position << "out of range!"; return; } QByteArray fixedAlias(alias.trimmed()); Field *f = FieldList::field(position); if (f->captionOrName().isEmpty() && fixedAlias.isEmpty()) { KexiDBWarn << "position" << position << "could not remove alias when no name is specified for expression column!"; return; } d->setColumnAlias(position, fixedAlias); } QByteArray QuerySchema::tableAlias(uint position) const { return d->tableAliases.value(position); } QByteArray QuerySchema::tableAlias(const QString& tableName) const { const int pos = tablePosition(tableName); if (pos == -1) { return QByteArray(); } return d->tableAliases.value(pos); } QString QuerySchema::tableAliasOrName(const QString& tableName) const { const int pos = tablePosition(tableName); if (pos == -1) { return QString(); } return KexiDB::iifNotEmpty(d->tableAliases.value(pos), tableName); } int QuerySchema::tablePositionForAlias(const QByteArray& name) const { return d->tablePositionForAlias(name); } int QuerySchema::tablePosition(const QString& tableName) const { int num = -1; QString tableNameLower(tableName.toLower()); foreach(TableSchema* table, d->tables) { num++; if (table->name().toLower() == tableNameLower) return num; } return -1; } QList QuerySchema::tablePositions(const QString& tableName) const { QList result; QString tableNameLower(tableName.toLower()); int num = -1; foreach(TableSchema* table, d->tables) { num++; if (table->name().toLower() == tableNameLower) { result += num; } } return result; } bool QuerySchema::hasTableAlias(uint position) const { return d->tableAliases.contains(position); } int QuerySchema::columnPositionForAlias(const QByteArray& name) const { return d->columnPositionForAlias(name); } void QuerySchema::setTableAlias(uint position, const QByteArray& alias) { if (position >= (uint)d->tables.count()) { KexiDBWarn << "position" << position << "out of range!"; return; } QByteArray fixedAlias(alias.trimmed()); if (fixedAlias.isEmpty()) { QByteArray oldAlias(d->tableAliases.take(position)); if (!oldAlias.isEmpty()) { d->removeTablePositionForAlias(oldAlias); } // d->maxIndexWithTableAlias = -1; } else { d->setTableAlias(position, fixedAlias); // d->maxIndexWithTableAlias = qMax( d->maxIndexWithTableAlias, (int)index ); } } Relationship::List* QuerySchema::relationships() const { return &d->relations; } Field::List* QuerySchema::asterisks() const { return &d->asterisks; } QString QuerySchema::statement() const { return d->statement; } void QuerySchema::setStatement(const QString &s) { d->statement = s; } Field* QuerySchema::field(const QString& identifier, bool expanded) { QueryColumnInfo *ci = columnInfo(identifier, expanded); return ci ? ci->field : 0; } QueryColumnInfo* QuerySchema::columnInfo(const QString& identifier, bool expanded) { computeFieldsExpanded(); return expanded ? d->columnInfosByNameExpanded[identifier] : d->columnInfosByName[identifier]; } QueryColumnInfo::Vector QuerySchema::fieldsExpanded(FieldsExpandedOptions options) { computeFieldsExpanded(); if (options == WithInternalFields || options == WithInternalFieldsAndRowID) { //a ref to a proper pointer (as we cache the vector for two cases) QueryColumnInfo::Vector*& tmpFieldsExpandedWithInternal = (options == WithInternalFields) ? d->fieldsExpandedWithInternal : d->fieldsExpandedWithInternalAndRowID; //special case if (!tmpFieldsExpandedWithInternal) { //glue expanded and internal fields and cache it const uint size = d->fieldsExpanded->count() + (d->internalFields ? d->internalFields->count() : 0) + ((options == WithInternalFieldsAndRowID) ? 1 : 0) /*ROWID*/; tmpFieldsExpandedWithInternal = new QueryColumnInfo::Vector(size); const uint fieldsExpandedVectorSize = d->fieldsExpanded->size(); for (uint i = 0; i < fieldsExpandedVectorSize; i++) (*tmpFieldsExpandedWithInternal)[i] = d->fieldsExpanded->at(i); const uint internalFieldsCount = d->internalFields ? d->internalFields->size() : 0; if (internalFieldsCount > 0) { for (uint i = 0; i < internalFieldsCount; i++) (*tmpFieldsExpandedWithInternal)[fieldsExpandedVectorSize + i] = d->internalFields->at(i); } if (options == WithInternalFieldsAndRowID) { if (!d->fakeRowIDField) { d->fakeRowIDField = new Field("rowID", Field::BigInteger); d->fakeRowIDCol = new QueryColumnInfo(d->fakeRowIDField, QByteArray(), true); } (*tmpFieldsExpandedWithInternal)[fieldsExpandedVectorSize + internalFieldsCount] = d->fakeRowIDCol; } } return *tmpFieldsExpandedWithInternal; } if (options == Default) return *d->fieldsExpanded; //options == Unique: QSet columnsAlreadyFound; const uint fieldsExpandedCount(d->fieldsExpanded->count()); QueryColumnInfo::Vector result(fieldsExpandedCount); //initial size is set // QMapConstIterator columnsAlreadyFoundIt; //compute unique list uint uniqueListCount = 0; for (uint i = 0; i < fieldsExpandedCount; i++) { QueryColumnInfo *ci = d->fieldsExpanded->at(i); // columnsAlreadyFoundIt = columnsAlreadyFound.find(ci); // uint foundColumnIndex = -1; if (!columnsAlreadyFound.contains(ci->aliasOrName())) {// columnsAlreadyFoundIt==columnsAlreadyFound.constEnd()) columnsAlreadyFound.insert(ci->aliasOrName()); result[uniqueListCount++] = ci; } } result.resize(uniqueListCount); //update result size return result; } QueryColumnInfo::Vector QuerySchema::internalFields() { computeFieldsExpanded(); return d->internalFields ? *d->internalFields : QueryColumnInfo::Vector(); } QueryColumnInfo* QuerySchema::expandedOrInternalField(uint index) { return fieldsExpanded(WithInternalFields).value(index); } inline QString lookupColumnKey(Field *foreignField, Field* field) { QString res; if (field->table()) // can be 0 for anonymous fields built as joined multiple visible columns res = field->table()->name() + QLatin1Char('.'); return res + field->name() + QLatin1Char('_') + foreignField->table()->name() + QLatin1Char('.') + foreignField->name(); } void QuerySchema::computeFieldsExpanded() { if (d->fieldsExpanded) return; if (!d->columnsOrder) { d->columnsOrder = new QHash(); d->columnsOrderWithoutAsterisks = new QHash(); } else { d->columnsOrder->clear(); d->columnsOrderWithoutAsterisks->clear(); } if (d->ownedVisibleColumns) d->ownedVisibleColumns->clear(); //collect all fields in a list (not a vector yet, because we do not know its size) QueryColumnInfo::List list; //temporary QueryColumnInfo::List lookup_list; //temporary, for collecting additional fields related to lookup fields QHash columnInfosOutsideAsterisks; //helper for filling d->columnInfosByName int i = 0; uint numberOfColumnsWithMultipleVisibleFields = 0; //used to find an unique name for anonymous field int fieldPosition = -1; foreach(Field *f, m_fields) { fieldPosition++; if (f->isQueryAsterisk()) { if (static_cast(f)->isSingleTableAsterisk()) { const Field::List *ast_fields = static_cast(f)->table()->fields(); foreach(Field *ast_f, *ast_fields) { // d->detailedVisibility += isFieldVisible(fieldPosition); QueryColumnInfo *ci = new QueryColumnInfo(ast_f, QByteArray()/*no field for asterisk!*/, isColumnVisible(fieldPosition)); list.append(ci); //KexiDBDbg << "caching (unexpanded) columns order: " // << ci->debugString() << " at position " << fieldPosition; d->columnsOrder->insert(ci, fieldPosition); // list.append(ast_f); } } else {//all-tables asterisk: iterate through table list foreach(TableSchema *table, d->tables) { //add all fields from this table const Field::List *tab_fields = table->fields(); foreach(Field *tab_f, *tab_fields) { //! \todo (js): perhaps not all fields should be appended here // d->detailedVisibility += isFieldVisible(fieldPosition); // list.append(tab_f); QueryColumnInfo *ci = new QueryColumnInfo(tab_f, QByteArray()/*no field for asterisk!*/, isColumnVisible(fieldPosition)); list.append(ci); //KexiDBDbg << "caching (unexpanded) columns order: " // << ci->debugString() << " at position " << fieldPosition; d->columnsOrder->insert(ci, fieldPosition); } } } } else { //a single field // d->detailedVisibility += isFieldVisible(fieldPosition); QueryColumnInfo *ci = new QueryColumnInfo(f, columnAlias(fieldPosition), isColumnVisible(fieldPosition)); list.append(ci); columnInfosOutsideAsterisks.insert(ci, true); //KexiDBDbg << "caching (unexpanded) column's order: " // << ci->debugString() << " at position " << fieldPosition; d->columnsOrder->insert(ci, fieldPosition); d->columnsOrderWithoutAsterisks->insert(ci, fieldPosition); //handle lookup field schema LookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema(*f) : 0; if (!lookupFieldSchema || lookupFieldSchema->boundColumn() < 0) continue; // Lookup field schema found: // Now we also need to fetch "visible" value from the lookup table, not only the value of binding. // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken) // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField" LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource(); if (rowSource.type() == LookupFieldSchema::RowSource::Table) { + rowSource.debug(); TableSchema *lookupTable = connection()->tableSchema(rowSource.name()); FieldList* visibleColumns = 0; Field *boundField = 0; if (lookupTable && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount() && (visibleColumns = lookupTable->subList(lookupFieldSchema->visibleColumns())) && (boundField = lookupTable->field(lookupFieldSchema->boundColumn()))) { Field *visibleColumn = 0; // for single visible column, just add it as-is if (visibleColumns->fieldCount() == 1) { visibleColumn = visibleColumns->fields()->first(); } else { // for multiple visible columns, build an expression column // (the expression object will be owned by column info) visibleColumn = new Field(); visibleColumn->setName( QString::fromLatin1("[multiple_visible_fields_%1]") .arg(++numberOfColumnsWithMultipleVisibleFields)); visibleColumn->setExpression( new ConstExpr(CHARACTER_STRING_LITERAL, QVariant()/*not important*/)); if (!d->ownedVisibleColumns) { d->ownedVisibleColumns = new Field::List(); //Qt 4 d->ownedVisibleColumns->setAutoDelete(true); } d->ownedVisibleColumns->append(visibleColumn); // remember to delete later } lookup_list.append( new QueryColumnInfo(visibleColumn, QByteArray(), true/*visible*/, ci/*foreign*/)); /* //add visibleField to the list of SELECTed fields if it is not yes present there if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) { if (!table( visibleField->table()->name() )) { } if (!sql.isEmpty()) sql += QString::fromLatin1(", "); sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "." + escapeIdentifier(visibleField->name(), drvEscaping)); }*/ } delete visibleColumns; } else if (rowSource.type() == LookupFieldSchema::RowSource::Query) { QuerySchema *lookupQuery = connection()->querySchema(rowSource.name()); if (!lookupQuery) continue; const QueryColumnInfo::Vector lookupQueryFieldsExpanded(lookupQuery->fieldsExpanded()); if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count()) continue; QueryColumnInfo *boundColumnInfo = 0; if (!(boundColumnInfo = lookupQueryFieldsExpanded[ lookupFieldSchema->boundColumn()])) continue; Field *boundField = boundColumnInfo->field; if (!boundField) continue; const QList visibleColumns(lookupFieldSchema->visibleColumns()); bool ok = true; // all indices in visibleColumns should be in [0..lookupQueryFieldsExpanded.size()-1] foreach(uint visibleColumn, visibleColumns) { if (visibleColumn >= (uint)lookupQueryFieldsExpanded.count()) { ok = false; break; } } if (!ok) continue; Field *visibleColumn = 0; // for single visible column, just add it as-is if (visibleColumns.count() == 1) { visibleColumn = lookupQueryFieldsExpanded[ visibleColumns.first()]->field; } else { // for multiple visible columns, build an expression column // (the expression object will be owned by column info) visibleColumn = new Field(); visibleColumn->setName( QString::fromLatin1("[multiple_visible_fields_%1]") .arg(++numberOfColumnsWithMultipleVisibleFields)); visibleColumn->setExpression( new ConstExpr(CHARACTER_STRING_LITERAL, QVariant()/*not important*/)); if (!d->ownedVisibleColumns) { d->ownedVisibleColumns = new Field::List(); //Qt 4 d->ownedVisibleColumns->setAutoDelete(true); } d->ownedVisibleColumns->append(visibleColumn); // remember to delete later } lookup_list.append( new QueryColumnInfo(visibleColumn, QByteArray(), true/*visible*/, ci/*foreign*/)); /* //add visibleField to the list of SELECTed fields if it is not yes present there if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) { if (!table( visibleField->table()->name() )) { } if (!sql.isEmpty()) sql += QString::fromLatin1(", "); sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "." + escapeIdentifier(visibleField->name(), drvEscaping)); }*/ } } } //prepare clean vector for expanded list, and a map for order information if (!d->fieldsExpanded) { d->fieldsExpanded = new QueryColumnInfo::Vector(list.count()); // Field::Vector( list.count() ); //Qt 4 d->fieldsExpanded->setAutoDelete(true); d->columnsOrderExpanded = new QHash(); } else {//for future: qDeleteAll(*d->fieldsExpanded); d->fieldsExpanded->clear(); d->fieldsExpanded->resize(list.count()); d->columnsOrderExpanded->clear(); } /*fill (based on prepared 'list' and 'lookup_list'): -the vector -the map -"fields by name" dictionary */ d->columnInfosByName.clear(); d->columnInfosByNameExpanded.clear(); i = -1; foreach(QueryColumnInfo* ci, list) { i++; (*d->fieldsExpanded)[i] = ci; d->columnsOrderExpanded->insert(ci, i); //remember field by name/alias/table.name if there's no such string yet in d->columnInfosByNameExpanded if (!ci->alias.isEmpty()) { //store alias and table.alias if (!d->columnInfosByNameExpanded[ ci->alias ]) d->columnInfosByNameExpanded.insert(ci->alias, ci); QString tableAndAlias(ci->alias); if (ci->field->table()) tableAndAlias.prepend(ci->field->table()->name() + "."); if (!d->columnInfosByNameExpanded[ tableAndAlias ]) d->columnInfosByNameExpanded.insert(tableAndAlias, ci); //the same for "unexpanded" list if (columnInfosOutsideAsterisks.contains(ci)) { if (!d->columnInfosByName[ ci->alias ]) d->columnInfosByName.insert(ci->alias, ci); if (!d->columnInfosByName[ tableAndAlias ]) d->columnInfosByName.insert(tableAndAlias, ci); } } else { //no alias: store name and table.name if (!d->columnInfosByNameExpanded[ ci->field->name()]) d->columnInfosByNameExpanded.insert(ci->field->name(), ci); QString tableAndName(ci->field->name()); if (ci->field->table()) tableAndName.prepend(ci->field->table()->name() + "."); if (!d->columnInfosByNameExpanded[ tableAndName ]) d->columnInfosByNameExpanded.insert(tableAndName, ci); //the same for "unexpanded" list if (columnInfosOutsideAsterisks.contains(ci)) { if (!d->columnInfosByName[ ci->field->name()]) d->columnInfosByName.insert(ci->field->name(), ci); if (!d->columnInfosByName[ tableAndName ]) d->columnInfosByName.insert(tableAndName, ci); } } } //remove duplicates for lookup fields QHash lookup_dict; //used to fight duplicates and to update QueryColumnInfo::indexForVisibleLookupValue() // (a mapping from table.name string to uint* lookupFieldIndex i = 0; for (QMutableListIterator it(lookup_list); it.hasNext();) { QueryColumnInfo* ci = it.next(); const QString key(lookupColumnKey(ci->foreignColumn()->field, ci->field)); if ( /* not needed columnInfo( tableAndFieldName ) || */ lookup_dict.contains(key)) { // this table.field is already fetched by this query it.remove(); delete ci; } else { lookup_dict.insert(key, i); i++; } } //create internal expanded list with lookup fields if (d->internalFields) { qDeleteAll(*d->internalFields); d->internalFields->clear(); d->internalFields->resize(lookup_list.count()); } delete d->fieldsExpandedWithInternal; //clear cache delete d->fieldsExpandedWithInternalAndRowID; //clear cache d->fieldsExpandedWithInternal = 0; d->fieldsExpandedWithInternalAndRowID = 0; if (!lookup_list.isEmpty() && !d->internalFields) {//create on demand d->internalFields = new QueryColumnInfo::Vector(lookup_list.count()); //Qt 4 d->internalFields->setAutoDelete(true); } i = -1; foreach(QueryColumnInfo *ci, lookup_list) { i++; //add it to the internal list (*d->internalFields)[i] = ci; d->columnsOrderExpanded->insert(ci, list.count() + i); } //update QueryColumnInfo::indexForVisibleLookupValue() cache for columns numberOfColumnsWithMultipleVisibleFields = 0; for (i = 0; i < (int)d->fieldsExpanded->size(); ++i) { QueryColumnInfo* ci = d->fieldsExpanded->at(i); //! @todo QuerySchema itself will also support lookup fields... LookupFieldSchema *lookupFieldSchema = ci->field->table() ? ci->field->table()->lookupFieldSchema(*ci->field) : 0; if (!lookupFieldSchema || lookupFieldSchema->boundColumn() < 0) continue; LookupFieldSchema::RowSource& rowSource = lookupFieldSchema->rowSource(); if (rowSource.type() == LookupFieldSchema::RowSource::Table) { TableSchema *lookupTable = connection()->tableSchema(rowSource.name()); FieldList* visibleColumns = 0; if (lookupTable && (uint)lookupFieldSchema->boundColumn() < lookupTable->fieldCount() && (visibleColumns = lookupTable->subList(lookupFieldSchema->visibleColumns()))) { Field *visibleColumn = 0; // for single visible column, just add it as-is if (visibleColumns->fieldCount() == 1) { visibleColumn = visibleColumns->fields()->first(); const QString key(lookupColumnKey(ci->field, visibleColumn)); int index = lookup_dict.value(key, -99); if (index != -99) ci->setIndexForVisibleLookupValue(d->fieldsExpanded->size() + index); } else { const QString key(QString::fromLatin1("[multiple_visible_fields_%1]_%2.%3") .arg(++numberOfColumnsWithMultipleVisibleFields) .arg(ci->field->table()->name()).arg(ci->field->name())); int index = lookup_dict.value(key, -99); if (index != -99) ci->setIndexForVisibleLookupValue(d->fieldsExpanded->size() + index); } } delete visibleColumns; } else if (rowSource.type() == LookupFieldSchema::RowSource::Query) { QuerySchema *lookupQuery = connection()->querySchema(rowSource.name()); if (!lookupQuery) continue; const QueryColumnInfo::Vector lookupQueryFieldsExpanded(lookupQuery->fieldsExpanded()); if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count()) continue; QueryColumnInfo *boundColumnInfo = 0; if (!(boundColumnInfo = lookupQueryFieldsExpanded[ lookupFieldSchema->boundColumn()])) continue; Field *boundField = boundColumnInfo->field; if (!boundField) continue; const QList visibleColumns(lookupFieldSchema->visibleColumns()); // for single visible column, just add it as-is if (visibleColumns.count() == 1) { if (lookupQueryFieldsExpanded.count() > (int)visibleColumns.first()) { // sanity check Field *visibleColumn = lookupQueryFieldsExpanded.at(visibleColumns.first())->field; const QString key(lookupColumnKey(ci->field, visibleColumn)); int index = lookup_dict.value(key, -99); if (index != -99) ci->setIndexForVisibleLookupValue(d->fieldsExpanded->size() + index); } } else { const QString key(QString::fromLatin1("[multiple_visible_fields_%1]_%2.%3") .arg(++numberOfColumnsWithMultipleVisibleFields) .arg(ci->field->table()->name()).arg(ci->field->name())); int index = lookup_dict.value(key, -99); if (index != -99) ci->setIndexForVisibleLookupValue(d->fieldsExpanded->size() + index); } } else { KexiDBWarn << "unsupported record source type" << rowSource.typeName(); } } } QHash QuerySchema::columnsOrder(ColumnsOrderOptions options) { if (!d->columnsOrder) computeFieldsExpanded(); if (options == UnexpandedList) return *d->columnsOrder; else if (options == UnexpandedListWithoutAsterisks) return *d->columnsOrderWithoutAsterisks; return *d->columnsOrderExpanded; } QVector QuerySchema::pkeyFieldsOrder() { if (d->pkeyFieldsOrder) return *d->pkeyFieldsOrder; TableSchema *tbl = masterTable(); if (!tbl || !tbl->primaryKey()) return QVector(); //get order of PKEY fields (e.g. for rows updating or inserting ) IndexSchema *pkey = tbl->primaryKey(); pkey->debug(); //debug(); //20080107, sebsauer; this seems to crash in kexi on query SQL text view d->pkeyFieldsOrder = new QVector(pkey->fieldCount(), -1); const uint fCount = fieldsExpanded().count(); d->pkeyFieldsCount = 0; for (uint i = 0; i < fCount; i++) { QueryColumnInfo *fi = d->fieldsExpanded->at(i); const int fieldIndex = fi->field->table() == tbl ? pkey->indexOf(fi->field) : -1; if (fieldIndex != -1/* field found in PK */ && d->pkeyFieldsOrder->at(fieldIndex) == -1 /* first time */) { //KexiDBDbg << "FIELD" << fi->field->name() // << "IS IN PKEY AT POSITION #" << fieldIndex; // (*d->pkeyFieldsOrder)[j]=i; (*d->pkeyFieldsOrder)[fieldIndex] = i; d->pkeyFieldsCount++; // j++; } } //KexiDBDbg << d->pkeyFieldsCount //<< " OUT OF " << pkey->fieldCount() << " PKEY'S FIELDS FOUND IN QUERY " << name(); return *d->pkeyFieldsOrder; } uint QuerySchema::pkeyFieldsCount() { (void)pkeyFieldsOrder(); /* rebuild information */ return d->pkeyFieldsCount; } Relationship* QuerySchema::addRelationship(Field *field1, Field *field2) { //@todo: find existing global db relationships Relationship *r = new Relationship(this, field1, field2); if (r->isEmpty()) { delete r; return 0; } d->relations.append(r); return r; } QueryColumnInfo::List* QuerySchema::autoIncrementFields() { if (!d->autoincFields) { d->autoincFields = new QueryColumnInfo::List(); } TableSchema *mt = masterTable(); if (!mt) { KexiDBWarn << "no master table!"; return d->autoincFields; } if (d->autoincFields->isEmpty()) {//no cache QueryColumnInfo::Vector fexp = fieldsExpanded(); for (int i = 0; i < (int)fexp.count(); i++) { QueryColumnInfo *fi = fexp[i]; if (fi->field->table() == mt && fi->field->isAutoIncrement()) { d->autoincFields->append(fi); } } } return d->autoincFields; } QString QuerySchema::sqlColumnsList(QueryColumnInfo::List* infolist, const Driver *driver) { if (!infolist) return QString(); QString result; result.reserve(256); bool start = true; foreach(QueryColumnInfo* ci, *infolist) { if (!start) result += ','; else start = false; result += KexiDB::escapeIdentifier(driver, ci->field->name()); } return result; } QString QuerySchema::autoIncrementSQLFieldsList(const Driver *driver) { if ((const Driver *)d->lastUsedDriverForAutoIncrementSQLFieldsList != driver || d->autoIncrementSQLFieldsList.isEmpty()) { d->autoIncrementSQLFieldsList = QuerySchema::sqlColumnsList(autoIncrementFields(), driver); d->lastUsedDriverForAutoIncrementSQLFieldsList = const_cast(driver); } return d->autoIncrementSQLFieldsList; } void QuerySchema::setWhereExpression(BaseExpr *expr) { delete d->whereExpr; d->whereExpr = expr; } void QuerySchema::addToWhereExpression(KexiDB::Field *field, const QVariant& value, int relation) { int token; if (value.isNull()) token = SQL_NULL; else if (field->isIntegerType()) { token = INTEGER_CONST; } else if (field->isFPNumericType()) { token = REAL_CONST; } else { token = CHARACTER_STRING_LITERAL; //! @todo date, time } BinaryExpr * newExpr = new BinaryExpr( KexiDBExpr_Relational, new ConstExpr(token, value), relation, new VariableExpr((field->table() ? (field->table()->name() + ".") : QString()) + field->name()) ); if (d->whereExpr) { d->whereExpr = new BinaryExpr( KexiDBExpr_Logical, d->whereExpr, AND, newExpr ); } else { d->whereExpr = newExpr; } } /* void QuerySchema::addToWhereExpression(KexiDB::Field *field, const QVariant& value) switch (value.type()) { case Int: case UInt: case Bool: case LongLong: case ULongLong: token = INTEGER_CONST; break; case Double: token = REAL_CONST; break; default: token = CHARACTER_STRING_LITERAL; } //! @todo date, time */ BaseExpr *QuerySchema::whereExpression() const { return d->whereExpr; } void QuerySchema::setOrderByColumnList(const OrderByColumnList& list) { delete d->orderByColumnList; d->orderByColumnList = new OrderByColumnList(list, 0, 0); // all field names should be found, exit otherwise ..........? } OrderByColumnList& QuerySchema::orderByColumnList() const { return *d->orderByColumnList; } QuerySchemaParameterList QuerySchema::parameters() { if (!whereExpression()) return QuerySchemaParameterList(); QuerySchemaParameterList params; whereExpression()->getQueryParameters(params); return params; } /* new field1, Field *field2 if (!field1 || !field2) { KexiDBWarn << "!masterField || !detailsField"; return; } if (field1->isQueryAsterisk() || field2->isQueryAsterisk()) { KexiDBWarn << "relationship's fields cannot be asterisks"; return; } if (!hasField(field1) && !hasField(field2)) { KexiDBWarn << "fields do not belong to this query"; return; } if (field1->table() == field2->table()) { KexiDBWarn << "fields cannot belong to the same table"; return; } //@todo: check more things: -types //@todo: find existing global db relationships Field *masterField = 0, *detailsField = 0; IndexSchema *masterIndex = 0, *detailsIndex = 0; if (field1->isPrimaryKey() && field2->isPrimaryKey()) { //2 primary keys masterField = field1; masterIndex = masterField->table()->primaryKey(); detailsField = field2; detailsIndex = masterField->table()->primaryKey(); } else if (field1->isPrimaryKey()) { masterField = field1; masterIndex = masterField->table()->primaryKey(); detailsField = field2; //@todo: check if it already exists detailsIndex = new IndexSchema(detailsField->table()); detailsIndex->addField(detailsField); detailsIndex->setForeigKey(true); // detailsField->setForeignKey(true); } else if (field2->isPrimaryKey()) { detailsField = field1; masterField = field2; masterIndex = masterField->table()->primaryKey(); //@todo } if (!masterIndex || !detailsIndex) return; //failed Relationship *rel = new Relationship(masterIndex, detailsIndex); d->relations.append( rel ); }*/ //--------------------------------------------------- QueryAsterisk::QueryAsterisk(QuerySchema *query, TableSchema *table) : Field() , m_table(table) { assert(query); m_parent = query; setType(Field::Asterisk); } QueryAsterisk::QueryAsterisk(const QueryAsterisk& asterisk) : Field(asterisk) , m_table(asterisk.table()) { } QueryAsterisk::~QueryAsterisk() { //kDebug() << this << debugString(); } Field* QueryAsterisk::copy() const { return new QueryAsterisk(*this); } void QueryAsterisk::setTable(TableSchema *table) { //KexiDBDbg; m_table = table; } QString QueryAsterisk::debugString() const { QString dbg; if (isAllTableAsterisk()) { dbg += "ALL-TABLES ASTERISK (*) ON TABLES("; QString table_names; foreach(TableSchema *table, *query()->tables()) { if (!table_names.isEmpty()) table_names += ", "; table_names += table->name(); } dbg += (table_names + ")"); } else { dbg += ("SINGLE-TABLE ASTERISK (" + table()->name() + ".*)"); } return dbg; } diff --git a/libs/db/queryschema.h b/libs/db/queryschema.h index 3da6837467..318d0131c9 100644 --- a/libs/db/queryschema.h +++ b/libs/db/queryschema.h @@ -1,915 +1,918 @@ /* This file is part of the KDE project Copyright (C) 2003-2012 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIDB_QUERYSCHEMA_H #define KEXIDB_QUERYSCHEMA_H #include #include #include #include #include #include "fieldlist.h" #include "schemadata.h" #include "tableschema.h" #include "relationship.h" namespace KexiDB { class Connection; class QueryAsterisk; class QuerySchemaPrivate; class QuerySchemaParameter; typedef QList QuerySchemaParameterList; //! @short Helper class that assigns additional information for the column in a query /*! The following information is assigned: - alias - visibility QueryColumnInfo::Vector is created and returned by QuerySchema::fieldsExpanded(). It is efficiently cached within the QuerySchema object. */ class CALLIGRADB_EXPORT QueryColumnInfo { public: typedef QVector Vector; typedef QList List; typedef QList::ConstIterator ListIterator; QueryColumnInfo(Field *f, const QByteArray& _alias, bool _visible, QueryColumnInfo *foreignColumn = 0); ~QueryColumnInfo(); //! \return alias if it is not empty, field's name otherwise. inline QByteArray aliasOrName() const { if (alias.isEmpty()) { return field->name().toLatin1(); } else { return QByteArray((const char*)alias); } } //! \return field's caption if it is not empty, field's alias otherwise. //! If alias is also empty - returns field's name. inline QString captionOrAliasOrName() const { return field->caption().isEmpty() ? QString(aliasOrName()) : field->caption(); } Field *field; QByteArray alias; /*! \return index of column with visible lookup value within the 'fields expanded' vector. -1 means no visible lookup value is available because there is no lookup for the column defined. Cached for efficiency as we use this information frequently. @see LookupFieldSchema::visibleVolumn() */ inline int indexForVisibleLookupValue() const { return m_indexForVisibleLookupValue; } /*! Sets index of column with visible lookup value within the 'fields expanded' vector. */ inline void setIndexForVisibleLookupValue(int index) { m_indexForVisibleLookupValue = index; } //! \return non-0 if this column is a visible column for other column QueryColumnInfo *foreignColumn() const { return m_foreignColumn; } /*! \return string for debugging purposes. */ QString debugString() const; //! true if this column is visible to the user (and its data is fetched by the engine) bool visible; private: /*! Index of column with visible lookup value within the 'fields expanded' vector. @see indexForVisibleLookupValue() */ int m_indexForVisibleLookupValue; //! Non-0 if this column is a visible column for \a m_foreignColumn QueryColumnInfo *m_foreignColumn; }; //! @short KexiDB::OrderByColumn provides information about a single query column used for sorting /*! The column can be expression or table field. */ class CALLIGRADB_EXPORT OrderByColumn { public: typedef QList::ConstIterator ListConstIterator; OrderByColumn(); explicit OrderByColumn(QueryColumnInfo& column, bool ascending = true, int pos = -1); //! Like above but used when the field \a field is not present on the list of columns. //! (e.g. SELECT a FROM t ORDER BY b; where T is a table with fields (a,b)). explicit OrderByColumn(Field& field, bool ascending = true); ~OrderByColumn(); /*! @return copy of this OrderByColumn object. In @a fromQuery and @a toQuery is needed if column() is assigned to this info. Then, column info within @a toQuery will be assigned to the new OrderByColumn object, corresponding to column() from "this" OrderByColumn object. */ OrderByColumn* copy(QuerySchema* fromQuery, QuerySchema* toQuery) const; //! A column to sort. inline QueryColumnInfo* column() const { return m_column; } /*! A helper for column() that allows you to know that sorting column was defined by providing its position. -1 by default. Example query: SELECT a, b FROM T ORDER BY 2 */ inline int position() const { return m_pos; } //! A field to sort, used only in case when the second constructor was used. inline Field *field() const { return m_field; } //! \return true if ascending sorting should be performed (the default). inline bool ascending() const { return m_ascending; } //! \return true if this column is thesame as \a col bool operator== (const OrderByColumn& col) const { return m_column == col.m_column && m_field == col.m_field && m_ascending == col.m_ascending; } /*! \return string for debugging purposes. */ QString debugString() const; /*! \return a string like "name ASC" usable for building a SQL statement. If \a includeTableNames is true (the default) field is output in a form of "tablename.fieldname" (but only if fieldname is not a name of alias). \a drv and \a identifierEscaping are used for escaping the table and field identifiers. */ QString toSQLString(bool includeTableName = true, const Driver *drv = 0, int identifierEscaping = Driver::EscapeDriver | Driver::EscapeAsNecessary) const; protected: //! Column to sort QueryColumnInfo* m_column; //!< 0 if m_field is non-0. int m_pos; //!< A helper for m_column that allows to know that sorting column //!< was defined by providing its position. -1 by default. //!< e.g. SELECT a, b FROM T ORDER BY 2 Field* m_field; //!< Used only in case when the second contructor is used. bool m_ascending; //!< true if ascending sorting should be performed (the default). }; //! A base for KexiDB::OrderByColumnList typedef QList OrderByColumnListBase; //! @short KexiDB::OrderByColumnList provides list of sorted columns for a query schema class CALLIGRADB_EXPORT OrderByColumnList : protected OrderByColumnListBase { public: /*! Constructs empty list of ordered columns. */ OrderByColumnList(); /*! A copy constructor. */ OrderByColumnList(const OrderByColumnList& other, QuerySchema* fromQuery, QuerySchema* toQuery); ~OrderByColumnList(); class CALLIGRADB_EXPORT const_iterator : public OrderByColumnListBase::const_iterator { public: inline const_iterator() : OrderByColumnListBase::const_iterator() {} inline const_iterator(const OrderByColumnListBase::const_iterator &o) : OrderByColumnListBase::const_iterator(o) {} }; class CALLIGRADB_EXPORT iterator : public OrderByColumnListBase::iterator { public: inline iterator() : OrderByColumnListBase::iterator() {} inline iterator(const OrderByColumnListBase::iterator &o) : OrderByColumnListBase::iterator(o) {} }; /*! Appends multiple fields for sorting. \a querySchema is used to find appropriate field or alias name. \return false if there is at least one name for which a field or alias name does not exist (all the newly appended fields are removed in this case) */ bool appendFields(QuerySchema& querySchema, const QString& field1, bool ascending1 = true, const QString& field2 = QString(), bool ascending2 = true, const QString& field3 = QString(), bool ascending3 = true, const QString& field4 = QString(), bool ascending4 = true, const QString& field5 = QString(), bool ascending5 = true); /*! Appends column \a columnInfo. Ascending sorting is set is \a ascending is true. */ void appendColumn(QueryColumnInfo& columnInfo, bool ascending = true); /*! Appends a field \a field. Ascending sorting is set is \a ascending is true. Read documentation of \ref OrderByColumn(const Field& field, bool ascending = true) for more info. */ void appendField(Field& field, bool ascending = true); /*! Appends field with a name \a field. Ascending sorting is set is \a ascending is true. \return true on successful appending, and false if there is no such field or alias name in the \a querySchema. */ bool appendField(QuerySchema& querySchema, const QString& fieldName, bool ascending = true); /*! Appends a column that is at position \a pos (counted from 0). \return true on successful adding and false if there is no such position \a pos. */ bool appendColumn(QuerySchema& querySchema, bool ascending = true, int pos = -1); /*! \return true if the list is empty. */ bool isEmpty() const { return OrderByColumnListBase::isEmpty(); } /*! \return number of elements of the list. */ uint count() const { return OrderByColumnListBase::count(); } /*! Removes all elements from the list (deletes them). */ void clear(); iterator begin() { return OrderByColumnListBase::begin(); } iterator end() { return OrderByColumnListBase::end(); } const_iterator constBegin() const { return OrderByColumnListBase::constBegin(); } const_iterator constEnd() const { return OrderByColumnListBase::constEnd(); } /*! \return string for debugging purposes. */ QString debugString() const; /*! \return a string like "name ASC, 2 DESC" usable for building a SQL statement. If \a includeTableNames is true (the default) fields are output in a form of "tablename.fieldname". \a drv and \a identifierEscaping are used for escaping the table and field identifiers. */ QString toSQLString(bool includeTableNames = true, const Driver *drv = 0, int identifierEscaping = Driver::EscapeDriver | Driver::EscapeAsNecessary) const; }; //! @short KexiDB::QuerySchema provides information about database query /*! The query that can be executed using KexiDB-compatible SQL database engine or used as an introspection tool. KexiDB parser builds QuerySchema objects by parsing SQL statements. */ class CALLIGRADB_EXPORT QuerySchema : public FieldList, public SchemaData { public: /*! Creates empty query object (without columns). */ QuerySchema(); /*! Creates query schema object that is equivalent to "SELECT * FROM table" sql command. Schema of \a table is used to contruct this query -- it is defined by just adding all the fields to the query in natural order. To avoid problems (e.g. with fields added outside of Kexi using ALTER TABLE) we do not use "all-tables query asterisk" (see QueryAsterisk) item to achieve this effect. Properties such as the name and caption of the query are inherited from table schema. We consider that query schema based on \a table is not (a least yet) stored in a system table, so query connection is set to NULL (even if \a tableSchema's connection is not NULL). Id of the created query is set to 0. */ explicit QuerySchema(TableSchema& tableSchema); /*! Copy constructor. Creates deep copy of \a querySchema. QueryAsterisk objects are deeply copied while only pointers to Field objects are copied. */ QuerySchema(const QuerySchema& querySchema); virtual ~QuerySchema(); /*! Inserts \a field to the columns list at \a position. Inserted field will not be owned by this QuerySchema object, but still by corresponding TableSchema. As \a field object you can also pass KexiDB::QueryAsterisk, (see QueryAsterisk class description). Note: After inserting a field, corresponding table will be automatically added to query's tables list if it is not present there (see tables()). Field must have its table assigned. Added field will be visible. Use insertField(position, field, false) to add invisible field. */ virtual FieldList& insertField(uint position, Field *field); /* Like above method, but you can also set column's visibility. New column is not bound explicitly to any table. */ FieldList& insertField(uint position, Field *field, bool visible); /* Like above method, but you can also explicitly bound the new column to specific position on tables list. If \a visible is true (the default), the field will be visible. If bindToTable==-1, no particular table should be bound. @see tableBoundToColumn(uint columnPosition) */ FieldList& insertField(uint position, Field *field, int bindToTable, bool visible = true); /*! Adds \a field to the columns list. If \a visible is true (the default), the field will be visible. \sa insertField() */ FieldList& addField(Field* field, bool visible = true); /*! Adds \a field to the columns list. Also binds to a table at \a bindToTable position. Use bindToTable==-1 if no table should be bound. If \a visible is true (the default), the field will be visible. \sa insertField() \sa tableBoundToColumn(uint columnPosition) */ FieldList& addField(Field* field, int bindToTable, bool visible = true); /*! Removes field from the columns list. Use with care. */ virtual bool removeField(Field *field); /*! Adds a field built on top of \a expr expression. This creates a new Field object and adds it to the query schema using addField(). */ FieldList& addExpression(BaseExpr* expr, bool visible = true); /*! \return visibility flag for column at \a position. By default column is visible. */ bool isColumnVisible(uint position) const; //! Sets visibility flag for column at \a position to \a v. void setColumnVisible(uint position, bool v); /*! Adds \a asterisk at the and of columns list. */ FieldList& addAsterisk(QueryAsterisk *asterisk, bool visible = true); /*! Removes all columns and their aliases from the columns list, removes all tables and their aliases from the tables list within this query. Sets master table information to NULL. Does not destroy any objects though. Clears name and all other properties. \sa FieldList::clear() */ virtual void clear(); /*! \return string for debugging purposes. */ virtual QString debugString() const; /*! If query was created using a connection, returns this connection object, otherwise NULL. */ Connection* connection() const; /*! \return table that is master to this query. All potentially-editable columns within this query belong just to this table. This method also can return NULL if there are no tables at all, or if previously assigned master table schema has been removed with removeTable(). Every query that has at least one table defined, should have assigned a master table. If no master table is assigned explicitly but this method there is only one table used for this query even if there are table aliases, a single table is returned here. (e.g. "T" table is returned for "SELECT T1.A, T2.B FROM T T1, T T2" statement). */ TableSchema* masterTable() const; /*! Sets master table of this query to \a table. This table should be also added to query's tables list using addTable(). If \a table equals NULL, nothing is performed. \sa masterTable() */ void setMasterTable(TableSchema *table); /*! \return list of tables used in a query. This also includes master table. \sa masterTable() */ TableSchema::List* tables() const; /*! Adds \a table schema as one of tables used in a query. If \a alias is not empty, it will be assigned to this table using setTableAlias(position, alias). */ void addTable(TableSchema *table, const QByteArray& alias = QByteArray()); /*! Removes \a table schema from this query. This does not destroy \a table object but only takes it out of the list. If this table was master for the query, master table information is also invalidated. */ void removeTable(TableSchema *table); /*! \return table with name \a tableName or 0 if this query has no such table. */ TableSchema* table(const QString& tableName) const; /*! \return true if the query uses \a table. */ bool contains(TableSchema *table) const; /*! Convenience function. \return table field by searching through all tables in this query. The field does not need to be included on the list of query columns. Similarly, query aliases are not taken into account. \a tableOrTableAndFieldName string may contain table name and field name with '.' character between them, e.g. "mytable.myfield". This is recommended way to avoid ambiguity. 0 is returned if the query has no such table defined of the table has no such field defined. If you do not provide a table name, the first field found is returned. QuerySchema::table("mytable")->field("myfield") could be alternative for findTableField("mytable.myfield") but it can crash if "mytable" is not defined in the query. @see KexiDB::splitToTableAndFieldParts() */ Field* findTableField(const QString &tableOrTableAndFieldName) const; /*! \return alias of a column at \a position or null string If there is no alias for this column or if there is no such column within the query defined. If the column is an expression and has no alias defined, a new unique alias will be generated automatically on this call. */ QByteArray columnAlias(uint position) const; /*! Provided for convenience. \return true if a column at \a position has non empty alias defined within the query. If there is no alias for this column, or if there is no such column in the query defined, false is returned. */ bool hasColumnAlias(uint position) const; /*! Sets \a alias for a column at \a position, within the query. Passing empty string to \a alias clears alias for a given column. */ void setColumnAlias(uint position, const QByteArray& alias); /*! \return a table position (within FROM section), that is bound to column at \a columnPosition (within SELECT section). This information can be used to find if there is alias defined for a table that is referenced by a given column. For example, for "SELECT t2.id FROM table1 t1, table2 t2" query statement, columnBoundToTable(0) returns 1, what means that table at position 1 (within FROM section) is bound to column at position 0, so we can now call tableAlias(1) to see if we have used alias for this column (t2.id) or just a table name (table2.id). These checks are performed e.g. by Connection::selectStatement() to construct a statement string maximally identical to originally defined query statement. -1 is returned if: - \a columnPosition is out of range (i.e. < 0 or >= fieldCount()) - a column at \a columnPosition is not bound to any table (i.e. no database field is used for this column, e.g. "1" constant for "SELECT 1 from table" query statement) */ int tableBoundToColumn(uint columnPosition) const; /*! \return alias of a table at \a position (within FROM section) or null string if there is no alias for this table or if there is no such table within the query defined. */ QByteArray tableAlias(uint position) const; /*! \return alias of a table \a tableName (within FROM section) or empty value if there is no alias for this table or if there is no such table within the query defined. */ QByteArray tableAlias(const QString& tableName) const; /*! \return alias of a table \a tableName (within FROM section). If there is no alias for this table, its name is returned. Empty value is returned if there is no such table within the query defined. */ QString tableAliasOrName(const QString& tableName) const; /*! \return table position (within FROM section) that has attached alias \a name. If there is no such alias, -1 is returned. Only first table's position attached for this alias is returned. It is not especially bad, since aliases rarely can be duplicated, what leads to ambiguity. Duplicated aliases are only allowed for trivial queries that have no database fields used within their columns, e.g. "SELECT 1 from table1 t, table2 t" is ok but "SELECT t.id from table1 t, table2 t" is not. */ int tablePositionForAlias(const QByteArray& name) const; /*! \return position (within the FROM section) of table \a tableName. -1 is returend if there's no such table declared in the FROM section. \sa tablePositions() */ int tablePosition(const QString& tableName) const; /*! \return a list of all occurrences of table \a tableName (within the FROM section). E.g. for "SELECT * FROM table t, table t2" tablePositions("table") returns {0, 1} list. Empty list is returned if there's no table \a tableName used in the FROM section at all. \sa tablePosition() */ QList tablePositions(const QString& tableName) const; /*! Provided for convenience. \return true if a table at \a position (within FROM section of the query) has non empty alias defined. If there is no alias for this table, or if there is no such table in the query defined, false is returned. */ bool hasTableAlias(uint position) const; /*! \return column position that has defined alias \a name. If there is no such alias, -1 is returned. */ int columnPositionForAlias(const QByteArray& name) const; /*! Sets \a alias for a table at \a position (within FROM section of the query). Passing empty sting to \a alias clears alias for a given table (only for specified \a position). */ void setTableAlias(uint position, const QByteArray& alias); /*! \return a list of relationships defined for this query */ Relationship::List* relationships() const; /*! Adds a new relationship defined by \a field1 and \a field2. Both fields should belong to two different tables of this query. This is convenience function useful for a typical cases. It automatically creates Relationship object for this query. If one of the fields are primary keys, it will be detected and appropriate master-detail relation will be established. This functiuon does nothing if the arguments are invalid. */ Relationship* addRelationship(Field *field1, Field *field2); /*! \return list of QueryAsterisk objects defined for this query */ Field::List* asterisks() const; /*! \return field for \a identifier or 0 if no field for this name was found within the query. fieldsExpanded() method is used to lookup expanded list of the query fields, so queries with asterisks are processed well. If a field has alias defined, name is not taken into account, but only its alias. If a field has no alias: - field's name is checked - field's table and field's name are checked in a form of "tablename.fieldname", so you can provide \a identifier in this form to avoid ambiguity. If there are more than one fields with the same name equal to \a identifier, first-found is returned (checking is performed from first to last query field). Structures needed to compute result of this method are cached, so only first usage costs o(n) - another usages cost o(1). Example: Let query be defined by "SELECT T.B AS X, T.* FROM T" statement and let T be table containing fields A, B, C. Expanded list of columns for the query is: T.B AS X, T.A, T.B, T.C. - Calling field("B") will return a pointer to third query column (not the first, because it is covered by "X" alias). Additionally, calling field("X") will return the same pointer. - Calling field("T.A") will return the same pointer as field("A"). */ virtual Field* field(const QString& name, bool expanded); /*! This is overloaded method Field* field(const QString& name, bool expanded) with expanded = true. This method is also a product of inheritance from FieldList. */ inline virtual Field* field(const QString& name) { return field(name, true); } /*! \return field id or NULL if there is no such a field. */ inline Field* field(uint id) { return FieldList::field(id); } /*! Like QuerySchema::field(const QString& name) but returns not only Field object for \a identifier but entire QueryColumnInfo object. \a identifier can be: - a fieldname - an aliasname - a tablename.fieldname - a tablename.aliasname Note that if there are two occurrrences of the same name, only the first is accessible using this method. For instance, calling columnInfo("name") for "SELECT t1.name, t2.name FROM t1, t2" statement will only return the column related to t1.name and not t2.name, so you'll need to explicitly specify "t2.name" as the identifier to get the second column. */ QueryColumnInfo* columnInfo(const QString& identifier, bool expanded = true); /*! Options used in fieldsExpanded(). */ enum FieldsExpandedOptions { Default, //!< All fields are returned even if duplicated Unique, //!< Unique list of fields is returned WithInternalFields, //!< Like Default but internal fields (for lookup) are appended WithInternalFieldsAndRowID //!< Like WithInternalFields but RowID (big int type) field //!< is appended after internal fields }; /*! \return fully expanded list of fields. QuerySchema::fields() returns vector of fields used for the query columns, but in a case when there are asterisks defined for the query, it does not expand QueryAsterisk objects to field lists but return every asterisk as-is. This could be inconvenient when you need just a fully expanded list of fields, so this method does the work for you. If \a options is Unique, each field is returned in the vector only once (first found field is selected). Note however, that the same field can be returned more than once if it has attached a different alias. For example, let t be TABLE( a, b ) and let query be defined by "SELECT *, a AS alfa FROM t" statement. Both fieldsExpanded(Default) and fieldsExpanded(Unique) will return [ a, b, a (alfa) ] list. On the other hand, for query defined by "SELECT *, a FROM t" statement, fieldsExpanded(Default) will return [ a, b, a ] list while fieldsExpanded(Unique) will return [ a, b ] list. If \a options is WithInternalFields or WithInternalFieldsAndRowID, additional internal fields are also appended to the vector. If \a options is WithInternalFieldsAndRowID, one fake BigInteger column is appended to make space for ROWID column used by KexiDB::Cursor implementations. For example, let persons be TABLE( surname, city_id ), let city_number reference cities.is in TABLE cities( id, name ) and let query q be defined by "SELECT * FROM t" statement. If we want to display persons' city names instead of city_id's. To do this, cities.name has to be retrieved as well, so the following statement should be used: "SELECT * FROM persons, cities.name LEFT OUTER JOIN cities ON persons.city_id=cities.id". Thus, calling fieldsExpanded(WithInternalFieldsAndRowID) will return 4 elements instead of 2: persons.surname, persons.city_id, cities.name, {ROWID}. The {ROWID} item is the placeholder used for fetching ROWID by KexiDB cursors. By default, all fields are returned in the vector even if there are multiple occurrences of one or more (options == Default). Note: You should assign the resulted vector in your space - it will be shared and implicity copied on any modification. This method's result is cached by QuerySchema object. @todo js: UPDATE CACHE! */ QueryColumnInfo::Vector fieldsExpanded(FieldsExpandedOptions options = Default); /*! \return list of fields internal fields used for lookup columns. */ QueryColumnInfo::Vector internalFields(); /*! \return info for expanded of internal field at index \a index. The returned field can be either logical or internal (for lookup), the latter case is true if \a index >= fieldsExpanded().count(). Equivalent of QuerySchema::fieldsExpanded(WithInternalFields).at(index). */ QueryColumnInfo* expandedOrInternalField(uint index); /*! Options used in columnsOrder(). */ enum ColumnsOrderOptions { UnexpandedList, //!< A map for unexpanded list is created UnexpandedListWithoutAsterisks, //!< A map for unexpanded list is created, with asterisks skipped ExpandedList //!< A map for expanded list is created }; /*! \return a hash for fast lookup of query columns' order. - If \a options is UnexpandedList, each QueryColumnInfo pointer is mapped to the index within (unexpanded) list of fields, i.e. "*" or "table.*" asterisks are considered to be single items. - If \a options is UnexpandedListWithoutAsterisks, each QueryColumnInfo pointer is mapped to the index within (unexpanded) list of columns that come from asterisks like "*" or "table.*" are not included in the map at all. - If \a options is ExpandedList (the default) this method provides is exactly opposite information compared to vector returned by fieldsExpanded(). This method's result is cached by the QuerySchema object. Note: indices of internal fields (see internalFields()) are also returned here - in this case the index is counted as a sum of size(e) + i (where "e" is the list of expanded fields and i is the column index within internal fields list). This feature is used eg. at the end of Connection::updateRow() where need indices of fields (including internal) to update all the values in memory. Example use: let t be table (int id, name text, surname text) and q be query defined by a statement "select * from t". - columnsOrder(ExpandedList) will return the following map: QueryColumnInfo(id)->0, QueryColumnInfo(name)->1, QueryColumnInfo(surname)->2. - columnsOrder(UnexpandedList) will return the following map: QueryColumnInfo(id)->0, QueryColumnInfo(name)->0, QueryColumnInfo(surname)->0 because the column list is not expanded. This way you can use the returned index to get Field* pointer using field(uint) method of FieldList superclass. - columnsOrder(UnexpandedListWithoutAsterisks) will return the following map: QueryColumnInfo(id)->0, */ QHash columnsOrder(ColumnsOrderOptions options = ExpandedList); /*! \return table describing order of primary key (PKEY) fields within the query. Indexing is performed against vector returned by fieldsExpanded(). It is usable for e.g. Conenction::updateRow(), when we need to locate each primary key's field in a constant time. Returned vector is owned and cached by QuerySchema object. When you assign it, it is implicity shared. Its size is equal to number of primary key fields defined for master table (masterTable()->primaryKey()->fieldCount()). Each element of the returned vector: - can belong to [0..fieldsExpanded().count()-1] if there is such primary key's field in the fieldsExpanded() list. - can be equal to -1 if there is no such primary key's field in the fieldsExpanded() list. If there are more than one primary key's field included in the query, only first-found column (oin the fieldsExpanded() list) for each pkey's field is included. Returns empty vector if there is no master table or no master table's pkey. @see example for pkeyFieldsCount(). @todo js: UPDATE CACHE! */ QVector pkeyFieldsOrder(); /*! \return number of master table's primary key fields included in this query. This method is useful to quickly check whether the vector returned by pkeyFieldsOrder() if filled completely. User e.g. in Connection::updateRow() to check if entire primary key information is specified. Examples: let table T has (ID1 INTEGER, ID2 INTEGER, A INTEGER) fields, and let (ID1, ID2) is T's primary key. -# The query defined by "SELECT * FROM T" statement contains all T's primary key's fields as T is the master table, and thus pkeyFieldsCount() will return 2 (both primary key's fields are in the fieldsExpanded() list), and pkeyFieldsOrder() will return vector {0, 1}. -# The query defined by "SELECT A, ID2 FROM T" statement, and thus pkeyFieldsCount() will return 1 (only one primary key's field is in the fieldsExpanded() list), and pkeyFieldsOrder() will return vector {-1, 1}, as second primary key's field is at position #1 and first field is not specified at all within the query. */ uint pkeyFieldsCount(); /*! \return a list of field information for all auto-incremented fields from master table of this query. This result is cached for efficiency. fieldsExpanded() is used for that. */ QueryColumnInfo::List* autoIncrementFields(); /*! \return a preset statement (if any). */ QString statement() const; /*! Forces a query statement (i.e. no statement is composed from QuerySchema's content) */ void setStatement(const QString &s); /*! \return a string that is a result of concatenating all column names for \a infolist, with "," between each one. This is usable e.g. as argument like "field1,field2" for "INSERT INTO (xxx) ..". The result of this method is effectively cached, and it is invalidated when set of fields changes (e.g. using clear() or addField()). This method is similar to FieldList::sqlFieldsList() it just uses QueryColumnInfo::List instead of Field::List. */ static QString sqlColumnsList(QueryColumnInfo::List* infolist, const Driver *driver); /*! \return cached sql list created using sqlColumnsList() on a list returned by autoIncrementFields(). */ QString autoIncrementSQLFieldsList(const Driver *driver); /*! Sets a WHERE expression \a exp. It will be owned by this query, so you can forget about it. Previously set WHERE expression will be deleted. You can pass 0 to remove expresssion. */ void setWhereExpression(BaseExpr *expr); /*! \return WHERE expression or 0 if this query has no WHERE expression */ BaseExpr *whereExpression() const; /*! Adds a part to WHERE expression. Simplifies creating of WHERE expression, if used instead of setWhereExpression(BaseExpr *expr). */ void addToWhereExpression(KexiDB::Field *field, const QVariant& value, int relation = '='); /*! Sets a list of columns for ORDER BY section of the query. Each name on the list must be a field or alias present within the query and must not be covered by aliases. If one or more names cannot be found within the query, the method will have no effect. Any previous ORDER BY settings will be removed. Note that this information is cleared whenever you call methods that modify list of columns (QueryColumnInfo), i.e. insertFiled(), addField(), removeField(), addExpression(), etc. (because OrderByColumn items can point to a QueryColumnInfo that's removed by these methods), so you should use setOrderByColumnList() method after the query is completely built. */ void setOrderByColumnList(const OrderByColumnList& list); /*! \return a list of columns listed in ORDER BY section of the query. Read notes for \ref setOrderByColumnList(). */ OrderByColumnList& orderByColumnList() const; /*! \return query schema parameters. These are taked from the WHERE section (a tree of expression items). */ QuerySchemaParameterList parameters(); protected: + /*! @internal associates @a conn with this query so it's possible to find tables there. */ + explicit QuerySchema(Connection *conn); + void init(); void computeFieldsExpanded(); QuerySchemaPrivate * const d; friend class Connection; friend class QuerySchemaPrivate; }; //! @short KexiDB::QueryAsterisk class encapsulates information about single asterisk in query definition /*! There are two types of query asterisks: 1. "Single-table" asterisk, that references all fields of given table used in the query. Example SQL statement: \code SELECT staff.*, cars.model from staff, cars WHERE staff.car = cars.number; \endcode The "staff.*" element is our "single-table" asterisk; this tells us that we want to get all fields of table "staff". 2. "All-tables" asterisk, that references all fields of all tables used in the query. Example SQL statement: \code SELECT * from staff, cars WHERE staff.car = cars.number; \endcode The "*" is our "all-tables" asterisk; this tells us that we want to get all fields of all used tables (here: "staff" and "cars"). There can be many asterisks of 1st type defined for given single query. There can be one asterisk of 2nd type defined for given single query. */ class CALLIGRADB_EXPORT QueryAsterisk : public Field { public: /*! Constructs query asterisk definition object. Pass table schema to \a table if this asterisk should be of type "single-table", otherwise (if you want to define "all-tables" type asterisk), omit this parameter. QueryAsterisk objects are owned by QuerySchema object (not by TableSchema object like for ordinary Field objects) for that the QueryAsterisk object was added (using QuerySchema::addField()). */ explicit QueryAsterisk(QuerySchema *query, TableSchema *table = 0); QueryAsterisk(const QueryAsterisk& asterisk); virtual ~QueryAsterisk(); /*! \return Query object for that this asterisk object is defined */ QuerySchema *query() const { return static_cast(m_parent); } /*! \return Table schema for this asterisk if it has "single-table" type (1st type) or NULL if it has "all-tables" type (2nd type) defined. */ virtual TableSchema* table() const { return m_table; } /*! Sets table schema for this asterisk. \a table may be NULL - then the asterisk becames "all-tables" type asterisk. */ virtual void setTable(TableSchema *table); /*! This is convenience method that returns true if the asterisk has "all-tables" type (2nd type).*/ bool isSingleTableAsterisk() const { return m_table != NULL; } /*! This is convenience method that returns true if the asterisk has "single-tables" type (2nd type).*/ bool isAllTableAsterisk() const { return m_table == NULL; } /*! \return String for debugging purposes. */ virtual QString debugString() const; protected: //! \return a deep copy of this object. Used in FieldList(const FieldList& fl). virtual Field* copy() const; /*! Table schema for this asterisk */ TableSchema* m_table; friend class QuerySchema; }; } //namespace KexiDB #endif diff --git a/libs/db/queryschema_p.cpp b/libs/db/queryschema_p.cpp new file mode 100644 index 0000000000..4241775504 --- /dev/null +++ b/libs/db/queryschema_p.cpp @@ -0,0 +1,251 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2012 Jarosław Staniek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "queryschema_p.h" +#include "driver.h" +#include "connection.h" +#include "expression.h" +#include "parser/sqlparser.h" +#include "utils.h" +#include "lookupfieldschema.h" + +#include + +#include + +#include +#include + +using namespace KexiDB; + +QuerySchemaPrivate::QuerySchemaPrivate(QuerySchema* q, QuerySchemaPrivate* copy) + : query(q) + , masterTable(0) + , fakeRowIDField(0) + , fakeRowIDCol(0) + , maxIndexWithAlias(-1) + , visibility(64) + , fieldsExpanded(0) + , internalFields(0) + , fieldsExpandedWithInternalAndRowID(0) + , fieldsExpandedWithInternal(0) + , orderByColumnList(0) + , autoincFields(0) + , columnsOrder(0) + , columnsOrderWithoutAsterisks(0) + , columnsOrderExpanded(0) + , pkeyFieldsOrder(0) + , pkeyFieldsCount(0) + , tablesBoundToColumns(64, -1) // will be resized if needed + , whereExpr(0) + , ownedVisibleColumns(0) + , regenerateExprAliases(false) +{ + visibility.fill(false); + if (copy) { + // deep copy + *this = *copy; + // + fieldsExpanded = 0; + internalFields = 0; + columnsOrder = 0; + columnsOrderWithoutAsterisks = 0; + columnsOrderExpanded = 0; + orderByColumnList = 0; + autoincFields = 0; + autoIncrementSQLFieldsList.clear(); + columnInfosByNameExpanded.clear(); + columnInfosByName.clear(); + ownedVisibleColumns = 0; + fieldsExpandedWithInternalAndRowID = 0; + fieldsExpandedWithInternal = 0; + pkeyFieldsOrder = 0; + fakeRowIDCol = 0; + fakeRowIDField = 0; + ownedVisibleColumns = 0; + // + if (copy->whereExpr) { + whereExpr = copy->whereExpr->copy(); + } + // "*this = *copy" causes copying pointers; pull of them without destroying, + // will be deep-copied in the QuerySchema ctor. + asterisks.setAutoDelete(false); + asterisks.clear(); + asterisks.setAutoDelete(true); + } + else { + orderByColumnList = new OrderByColumnList; + } +} + +QuerySchemaPrivate::~QuerySchemaPrivate() +{ + delete orderByColumnList; + delete autoincFields; + delete columnsOrder; + delete columnsOrderWithoutAsterisks; + delete columnsOrderExpanded; + delete pkeyFieldsOrder; + delete whereExpr; + delete fakeRowIDCol; + delete fakeRowIDField; + delete ownedVisibleColumns; + if (fieldsExpanded) + qDeleteAll(*fieldsExpanded); + delete fieldsExpanded; + if (internalFields) { + qDeleteAll(*internalFields); + delete internalFields; + } + delete fieldsExpandedWithInternalAndRowID; + delete fieldsExpandedWithInternal; +} + +//static +QuerySchema* QuerySchemaPrivate::createQuery(Connection *conn) +{ + return new QuerySchema(conn); +} + +void QuerySchemaPrivate::clear() +{ + columnAliases.clear(); + tableAliases.clear(); + asterisks.clear(); + relations.clear(); + masterTable = 0; + tables.clear(); + clearCachedData(); + delete pkeyFieldsOrder; + pkeyFieldsOrder = 0; + visibility.fill(false); + tablesBoundToColumns = QVector(64, -1); // will be resized if needed + tablePositionsForAliases.clear(); + columnPositionsForAliases.clear(); +} + +void QuerySchemaPrivate::clearCachedData() +{ + if (orderByColumnList) { + orderByColumnList->clear(); + } + if (fieldsExpanded) { + delete columnsOrder; + columnsOrder = 0; + delete columnsOrderWithoutAsterisks; + columnsOrderWithoutAsterisks = 0; + delete columnsOrderExpanded; + columnsOrderExpanded = 0; + delete autoincFields; + autoincFields = 0; + autoIncrementSQLFieldsList.clear(); + columnInfosByNameExpanded.clear(); + columnInfosByName.clear(); + delete ownedVisibleColumns; + ownedVisibleColumns = 0; + qDeleteAll(*fieldsExpanded); + delete fieldsExpanded; + fieldsExpanded = 0; + if (internalFields) { + qDeleteAll(*internalFields); + delete internalFields; + internalFields = 0; + } + } +} + +void QuerySchemaPrivate::setColumnAlias(uint position, const QByteArray& alias) +{ + if (alias.isEmpty()) { + columnAliases.remove(position); + maxIndexWithAlias = -1; + } else { + setColumnAliasInternal(position, alias); + } +} + +void QuerySchemaPrivate::setTableAlias(uint position, const QByteArray& alias) +{ + tableAliases.insert(position, alias.toLower()); + tablePositionsForAliases.insert(alias.toLower(), position); +} + +bool QuerySchemaPrivate::hasColumnAliases() +{ + tryRegenerateExprAliases(); + return !columnAliases.isEmpty(); +} + +QByteArray QuerySchemaPrivate::columnAlias(uint position) +{ + tryRegenerateExprAliases(); + return columnAliases.value(position); +} + +bool QuerySchemaPrivate::hasColumnAlias(uint position) +{ + tryRegenerateExprAliases(); + return columnAliases.contains(position); +} + +void QuerySchemaPrivate::removeTablePositionForAlias(const QByteArray& alias) +{ + tablePositionsForAliases.remove(alias.toLower()); +} + +int QuerySchemaPrivate::tablePositionForAlias(const QByteArray& alias) const +{ + return tablePositionsForAliases.value(alias.toLower(), -1); +} + +int QuerySchemaPrivate::columnPositionForAlias(const QByteArray& alias) const +{ + return columnPositionsForAliases.value(alias.toLower(), -1); +} + +void QuerySchemaPrivate::tryRegenerateExprAliases() +{ + if (!regenerateExprAliases) + return; + //regenerate missing aliases for experessions + uint colNum = 0; //used to generate a name + QByteArray columnAlias; + uint p = -1; + foreach(Field* f, *query->fields()) { + p++; + if (f->isExpression() && columnAliases.value(p).isEmpty()) { + //missing + do { //find 1st unused + colNum++; + columnAlias = (i18nc("short for 'expression' word, e.g. 'expr' (only latin letters, please, no '.')", "expr") + .toLatin1() + QByteArray::number(colNum)); + } while (-1 != tablePositionForAlias(columnAlias)); + + setColumnAliasInternal(p, columnAlias); + } + } + regenerateExprAliases = false; +} + +void QuerySchemaPrivate::setColumnAliasInternal(uint position, const QByteArray& alias) +{ + columnAliases.insert(position, alias.toLower()); + columnPositionsForAliases.insert(alias.toLower(), position); + maxIndexWithAlias = qMax(maxIndexWithAlias, (int)position); +} diff --git a/libs/db/queryschema_p.h b/libs/db/queryschema_p.h new file mode 100644 index 0000000000..a15e96a543 --- /dev/null +++ b/libs/db/queryschema_p.h @@ -0,0 +1,201 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2012 Jarosław Staniek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDB_QUERYSCHEMA_P_H +#define KEXIDB_QUERYSCHEMA_P_H + +#include +#include +#include +#include +#include +#include + +#include "fieldlist.h" +#include "schemadata.h" +#include "queryschema.h" +#include "tableschema.h" +#include "relationship.h" + +namespace KexiDB +{ + +class Connection; + +//! @internal Internals of QuerySchema +class QuerySchemaPrivate +{ +public: + explicit QuerySchemaPrivate(QuerySchema* q, QuerySchemaPrivate* copy = 0); + + ~QuerySchemaPrivate(); + + //! @return a new query that's associated with @a conn. Used internally, e.g. by the parser. + //! Uses an internal QuerySchema ctor. + static QuerySchema* createQuery(Connection *conn); + + void clear(); + + void clearCachedData(); + + void setColumnAlias(uint position, const QByteArray& alias); + + void setTableAlias(uint position, const QByteArray& alias); + + bool hasColumnAliases(); + + QByteArray columnAlias(uint position); + + bool hasColumnAlias(uint position); + + void removeTablePositionForAlias(const QByteArray& alias); + + int tablePositionForAlias(const QByteArray& alias) const; + + int columnPositionForAlias(const QByteArray& alias) const; + + QuerySchema *query; + + /*! Master table of the query. (may be NULL) + Any data modifications can be performed if we know master table. + If null, query's records cannot be modified. */ + TableSchema *masterTable; + + /*! List of tables used in this query */ + TableSchema::List tables; + + Field *fakeRowIDField; //! used to mark a place for ROWID + QueryColumnInfo *fakeRowIDCol; //! used to mark a place for ROWID + + /*! Connection on which this query operates */ + QPointer conn; + + /*! Used to mapping tables to its aliases for this query */ + QHash tableAliases; + + /*! Helper used with aliases */ + int maxIndexWithAlias; + + /*! Helper used with tableAliases */ + int maxIndexWithTableAlias; + + /*! Used to store visibility flag for every field */ + QBitArray visibility; + + /*! List of asterisks defined for this query */ + Field::List asterisks; + + /*! Temporary field vector for using in fieldsExpanded() */ + QueryColumnInfo::Vector *fieldsExpanded; + + /*! Temporary field vector containing internal fields used for lookup columns. */ + QueryColumnInfo::Vector *internalFields; + + /*! Temporary, used to cache sum of expanded fields and internal fields (+rowid) used for lookup columns. + Contains not auto-deleted items.*/ + QueryColumnInfo::Vector *fieldsExpandedWithInternalAndRowID; + + /*! Temporary, used to cache sum of expanded fields and internal fields used for lookup columns. + Contains not auto-deleted items.*/ + QueryColumnInfo::Vector *fieldsExpandedWithInternal; + + /*! A list of fields for ORDER BY section. @see QuerySchema::orderByColumnList(). */ + OrderByColumnList* orderByColumnList; + + /*! A cache for autoIncrementFields(). */ + QueryColumnInfo::List *autoincFields; + + /*! A cache for autoIncrementSQLFieldsList(). */ + QString autoIncrementSQLFieldsList; + QPointer lastUsedDriverForAutoIncrementSQLFieldsList; + + /*! A hash for fast lookup of query columns' order (unexpanded version). */ + QHash *columnsOrder; + + /*! A hash for fast lookup of query columns' order (unexpanded version without asterisks). */ + QHash *columnsOrderWithoutAsterisks; + + /*! A hash for fast lookup of query columns' order. + This is exactly opposite information compared to vector returned + by fieldsExpanded() */ + QHash *columnsOrderExpanded; + +// QValueList detailedVisibility; + + /*! order of PKEY fields (e.g. for updateRow() ) */ + QVector *pkeyFieldsOrder; + + /*! number of PKEY fields within the query */ + uint pkeyFieldsCount; + + /*! forced (predefined) statement */ + QString statement; + + /*! Relationships defined for this query. */ + Relationship::List relations; + + /*! Information about columns bound to tables. + Used if table is used in FROM section more than once + (using table aliases). + + This list is updated by insertField(uint position, Field *field, + int bindToTable, bool visible), using bindToTable parameter. + + Example: for this statement: + SELECT t1.a, othertable.x, t2.b FROM table t1, table t2, othertable; + tablesBoundToColumns list looks like this: + [ 0, -1, 1 ] + - first column is bound to table 0 "t1" + - second coulmn is not specially bound (othertable.x isn't ambiguous) + - third column is bound to table 1 "t2" + */ + QVector tablesBoundToColumns; + + /*! WHERE expression */ + BaseExpr *whereExpr; + + QHash columnInfosByNameExpanded; + + QHash columnInfosByName; //!< Same as columnInfosByNameExpanded but asterisks are skipped + + //! field schemas created for multiple joined columns like a||' '||b||' '||c + Field::List *ownedVisibleColumns; + + /*! Set by insertField(): true, if aliases for expression columns should + be generated on next columnAlias() call. */ + bool regenerateExprAliases; + +protected: + void tryRegenerateExprAliases(); + + void setColumnAliasInternal(uint position, const QByteArray& alias); + + /*! Used to mapping columns to its aliases for this query */ + QHash columnAliases; + + /*! Collects table positions for aliases: used in tablePositionForAlias(). */ + QHash tablePositionsForAliases; + + /*! Collects column positions for aliases: used in columnPositionForAlias(). */ + QHash columnPositionsForAliases; +}; + +} //namespace KexiDB + +#endif