diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc index 4e554b16638..56a0fc4fd4f 100644 --- a/storage/connect/connect.cc +++ b/storage/connect/connect.cc @@ -735,17 +735,12 @@ static void SetSwapValue(PVAL valp, char *kp) /* IndexRead: fetch a record having the index value. */ /***********************************************************************/ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op, - const void *key, int len, bool mrr) + const key_range *kr, bool mrr) { - char *kp= (char*)key; int n, x; - short lg; - bool rcb; RCODE rc; - PVAL valp; - PCOL colp; XXBASE *xbp; - PTDBDOX tdbp; + PTDBDOX tdbp; if (!ptdb) return RC_FX; @@ -757,13 +752,13 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op, return RC_FX; } else if (x == 2) { // Remote index - if (ptdb->ReadKey(g, op, key, len)) + if (ptdb->ReadKey(g, op, kr)) return RC_FX; goto rnd; } else if (x == 3) { - if (key) - ((PTDBASE)ptdb)->SetRecpos(g, *(int*)key); + if (kr) + ((PTDBASE)ptdb)->SetRecpos(g, *(int*)kr->key); if (op == OP_SAME) return RC_NF; @@ -790,7 +785,14 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op, xbp= (XXBASE*)tdbp->To_Kindex; - if (key) { + if (kr) { + char *kp= (char*)kr->key; + int len= kr->length; + short lg; + bool rcb; + PVAL valp; + PCOL colp; + for (n= 0; n < tdbp->Knum; n++) { colp= (PCOL)tdbp->To_Key_Col[n]; @@ -832,10 +834,10 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op, kp+= valp->GetClen(); - if (len == kp - (char*)key) { + if (len == kp - (char*)kr->key) { n++; break; - } else if (len < kp - (char*)key) { + } else if (len < kp - (char*)kr->key) { strcpy(g->Message, "Key buffer is too small"); return RC_FX; } // endif len diff --git a/storage/connect/connect.h b/storage/connect/connect.h index fd8b7e9442f..bbefda52274 100644 --- a/storage/connect/connect.h +++ b/storage/connect/connect.h @@ -36,7 +36,7 @@ bool CntRewindTable(PGLOBAL g, PTDB tdbp); int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort); int CntIndexInit(PGLOBAL g, PTDB tdbp, int id, bool sorted); RCODE CntReadNext(PGLOBAL g, PTDB tdbp); -RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const void *k, int n, bool mrr); +RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const key_range *kr, bool mrr); RCODE CntWriteRow(PGLOBAL g, PTDB tdbp); RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp); RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all); @@ -60,7 +60,7 @@ class TDBDOX: public TDBDOS { friend int MakeIndex(PGLOBAL, PTDB, PIXDEF); friend int CntCloseTable(PGLOBAL, PTDB, bool, bool); friend int CntIndexInit(PGLOBAL, PTDB, int, bool); - friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const void*, int, bool); + friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const key_range*, bool); friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool); friend int CntIndexRange(PGLOBAL, PTDB, const uchar**, uint*, bool*, key_part_map*); diff --git a/storage/connect/english.msg b/storage/connect/english.msg deleted file mode 100644 index 9f445ca6000..00000000000 --- a/storage/connect/english.msg +++ /dev/null @@ -1,366 +0,0 @@ - 100 IDS_TABLES "Table Headers" - 101 IDS_TAB_01 "Table_Cat" - 102 IDS_TAB_02 "Table_Schema" - 103 IDS_TAB_03 "Table_Name" - 104 IDS_TAB_04 "Table_Type" - 105 IDS_TAB_05 "Remark" - 106 IDS_COLUMNS "Column Headers" - 107 IDS_COL_01 "Table_Cat" - 108 IDS_COL_02 "Table_Schema" - 109 IDS_COL_03 "Table_Name" - 110 IDS_COL_04 "Column_Name" - 111 IDS_COL_05 "Data_Type" - 112 IDS_COL_06 "Type_Name" - 113 IDS_COL_07 "Column_Size" - 114 IDS_COL_08 "Buffer_Length" - 115 IDS_COL_09 "Decimal_Digits" - 116 IDS_COL_10 "Radix" - 117 IDS_COL_11 "Nullable" - 118 IDS_COL_12 "Remarks" - 119 IDS_PKEY "Key Headers" - 120 IDS_PKY_01 "Table_Catalog" - 121 IDS_PKY_02 "Table_Schema" - 122 IDS_PKY_03 "Table_Name" - 123 IDS_PKY_04 "Column_Name" - 124 IDS_PKY_05 "Key_Seq" - 125 IDS_PKY_06 "Pk_Name" - 126 IDS_STAT "Stat Headers" - 127 IDS_STA_01 "Table_Catalog" - 128 IDS_STA_02 "Table_Schema" - 129 IDS_STA_03 "Table_Name" - 130 IDS_STA_04 "Non_Unique" - 131 IDS_STA_05 "Index_Qualifier" - 132 IDS_STA_06 "Index_Name" - 133 IDS_STA_07 "Type" - 134 IDS_STA_08 "Seq_in_Index" - 135 IDS_STA_09 "Column_Name" - 136 IDS_STA_10 "Collation" - 137 IDS_STA_11 "Cardinality" - 138 IDS_STA_12 "Pages" - 139 IDS_STA_13 "Filter_Condition" - 140 IDS_DRIVER "Driver Headers" - 141 IDS_DRV_01 "Description" - 142 IDS_DRV_02 "Attributes" - 143 IDS_DSRC "DataSrc Headers" - 144 IDS_DSC_01 "Name" - 145 IDS_DSC_02 "Description" - 200 ACCESS_VIOLATN "Access violation" - 201 ADD_BAD_TYPE "Array add value type mismatch (%s -> %s)" - 202 ALLOC_ERROR "Error allocating %s" - 203 ANSWER_TYPE "Answer of type" - 204 API_CONF_ERROR "SQL Error: API_CONFORMANCE" - 205 APPL_NOT_INIT "Application not initialized" - 206 ARRAY_BNDS_EXCD "Array bounds exceeded" - 207 BAD_ARRAY_OPER "Arrays must be used with the IN operator" - 208 BAD_ARRAY_TYPE "Illegal array type %d" - 209 BAD_ARRAY_VAL "Arrays must have the same number of values" - 210 BAD_BIN_FMT "Invalid format %c for the %s BIN column" - 211 BAD_BLK_ESTIM "Number of blocks exceeds estimate" - 212 BAD_BLK_SIZE "No match in block %d size" - 213 BAD_BYTE_NUM "bad number of bytes written" - 214 BAD_BYTE_READ "bad number of bytes read" - 215 BAD_COL_TYPE "Invalid type %s for column %s" - 216 BAD_COL_XPATH "Invalid Xpath in column %s for HTML table %s" - 217 BAD_CONST_TYPE "Bad constant type=%d" - 218 BAD_CONV_TYPE "Invalid convert type %d" - 219 BAD_DATETIME "Invalid datetime value" - 220 BAD_DBF_FILE "DBF file %s is corrupted" - 221 BAD_DBF_REC "DBF file %s corrupted at record %d" - 222 BAD_DBF_TYPE "Unsupported DBF type %c for column %s" - 223 BAD_DIRECTORY "Bad directory %s: %s" - 224 BAD_FIELD_RANK "Invalid field rank %d for column %s" - 225 BAD_FIELD_TYPE "Bad type field %s" - 226 BAD_FILE_HANDLE "Invalid File Handle: %s" - 227 BAD_FILTER "Bad filter: Opc=%d B_T=%d %d Type=%d %d" - 228 BAD_FILTER_CONV "Bad filter conversion, B_T=%d,%d" - 229 BAD_FILTER_OP "Invalid filter operator %d" - 230 BAD_FLD_FORMAT "Bad format for field %d of %s" - 231 BAD_FLD_LENGTH "Field %s too long (%s --> %d) line %d of %s" - 232 BAD_FREQ_SET "Bad frequency setting for column %s" - 233 BAD_FUNC_MODE "%s: invalid mode %d" - 234 BAD_HANDLE_VAL "Invalid handle value" - 235 BAD_HEADER "File %s: Header corrupted" - 236 BAD_HEAD_END "Can't read end of header" - 237 BAD_INDEX_FILE "Wrong index file %s" - 238 BAD_LINEFLD_FMT "Bad format line %d field %d of %s" - 239 BAD_LINE_LEN "Line length not equal to Lrecl" - 240 BAD_LRECL "Table/File lrecl mismatch (%d,%hd)" - 241 BAD_NODE_TYPE "Bad type %d for table node" - 242 BAD_OFFSET_VAL "Invalid null offset value for a CSV table" - 243 BAD_OPEN_MODE "Invalid open mode %d" - 244 BAD_PARAM_TYPE "%.8s: Bad parameter type=%d" - 245 BAD_PARM_COUNT "Parameter count mismatch" - 246 BAD_QUOTE_FIELD "Missing ending quote in %s field %d line %d" - 247 BAD_READ_NUMBER "Wrong number %d of values read from %s" - 248 BAD_RECFM "Invalid recfm type %d for DOSCOL" - 249 BAD_RECFM_VAL "Bad Recfm value %d" - 250 BAD_SET_CASE "Cannot set sensitive an insensitive array" - 251 BAD_SET_STRING "Invalid SetValue from string" - 252 BAD_SPECIAL_COL "Bad special column %s" - 253 BAD_SPEC_COLUMN "Special column invalid for this table type" - 254 BAD_TABLE_TYPE "Bad type %s for table %s" - 255 BAD_TYPE_LIKE "Bad operand(%d) type=%d for LIKE" - 256 BAD_VALBLK_INDX "Out of range valblock index value" - 257 BAD_VALBLK_TYPE "Invalid value block type %d" - 258 BAD_VALNODE "Bad type %d for column %s value node" - 259 BAD_VALUE_TYPE "Invalid value type %d" - 260 BAD_VAL_UPDATE "Don't know which %s value to update" - 261 BAS_NS_LIST "Invalid namespaces list format" - 262 BIN_F_TOO_LONG "Value too long for field %s (%d --> %d)" - 263 BIN_MODE_FAIL "Set binary mode failed: %s" - 264 BLKTYPLEN_MISM "Non matching block types/lengths in SetValue" - 265 BLK_IS_NULL "Blk is NULL" - 266 BREAKPOINT "Breakpoint" - 267 BUILD_INDEX "Building index %s on %s" - 268 CANNOT_OPEN "Cannot open %s" - 269 CHSIZE_ERROR "chsize error: %s" - 270 COL_ALLOC_ERR "Cannot allocate column node" - 271 COL_ISNOT_TABLE "Column %s is not in table %s" - 272 COL_NOT_SORTED "Column %s of table %s is not sorted" - 273 COL_NUM_MISM "Number of columns mismatch" - 274 COM_ERROR "Com error" - 275 CONCAT_SUBNODE "Cannot concatenate sub-nodes" - 276 CONNECT_CANCEL "Connection cancelled by user" - 277 CONTROL_C_EXIT "Control C exit" - 278 DATABASE_LOADED "Database %s loaded" - 279 DATA_MISALIGN "Datatype misalignment" - 280 DBASE_FILE "dBASE dbf file: " - 281 DEF_ALLOC_ERROR "Error allocating %s DEF class" - 282 DEL_FILE_ERR "Error deleting %s" - 283 DEL_READ_ERROR "Delete: read error req=%d len=%d" - 284 DEL_WRITE_ERROR "Delete: write error: %s" - 285 DEPREC_FLAG "Deprecated option Flag, use Coltype" - 286 DLL_LOAD_ERROR "Error %d loading module %s" - 287 DOM_NOT_SUPP "MS-DOM not supported by this version" - 288 DVAL_NOTIN_LIST "Value %s not found in distinct values list of column %s" - 289 EMPTY_DOC "Empty document" - 290 EMPTY_FILE "%s empty file %s: " - 291 EOF_AFTER_LINE "EOF after line %d" - 292 EOF_INDEX_FILE "EOF while reading index file" - 293 ERROR_IN_LSK "Error %d in lseek64" - 294 ERROR_IN_SFP "Error %d in SetFilePointer" - 295 ERR_READING_REC "Error reading record %d of %s" - 296 FAIL_ADD_NODE "Failed to add %s table node" - 297 FETCH_NO_RES "Fetch: No Result Set" - 298 FIELD_TOO_LONG "Value too long for field %d line %d" - 299 FILELEN_ERROR "Error in %s for %s" - 300 FILE_IS_EMPTY "File %s is empty" - 301 FILE_MAP_ERR "File mapping error" - 302 FILE_MAP_ERROR "CreateFileMapping %s error rc=%d" - 303 FILE_OPEN_YET "File %s already open" - 304 FILE_UNFOUND "File %s not found" - 305 FLD_TOO_LNG_FOR "Field %d too long for %s line %d of %s" - 306 FLT_BAD_RESULT "Float inexact result" - 307 FLT_DENORMAL_OP "Float denormal operand" - 308 FLT_INVALID_OP "Float invalid operation" - 309 FLT_OVERFLOW "Float overflow" - 310 FLT_STACK_CHECK "Float stack check" - 311 FLT_UNDERFLOW "Float underflow" - 312 FLT_ZERO_DIVIDE "Float divide by zero" - 313 FMT_WRITE_NIY "Writing %s files is not implemented yet" - 314 FOXPRO_FILE "FoxPro file: " - 315 FPUTS_ERROR "fputs error: %s" - 316 FSEEK_ERROR "fseek error: %s" - 317 FSETPOS_ERROR "fseek error for i=%d" - 318 FTELL_ERROR "ftell error for recd=%d: %s" - 319 FUNCTION_ERROR "%s error: %d" - 320 FUNC_ERRNO "Error %d in %s" - 321 FUNC_ERROR "Error in %s" - 322 FUNC_ERR_S "%s error: %s" - 323 FWRITE_ERROR "fwrite error: %s" - 324 GET_DIST_VALS "Retrieving distinct values from " - 325 GET_FUNC_ERR "Error getting function %s: %s" - 326 GLOBAL_ERROR "Cannot allocate Global (size=%d)\n" - 327 GUARD_PAGE "Guard page violation" - 328 GZOPEN_ERROR "gzopen %s error %d on %s" - 329 ILLEGAL_INSTR "Illegal instruction" - 330 ILL_FILTER_CONV "Filtering implies an illegal conversion" - 331 INDEX_NOT_UNIQ "Index is not unique" - 332 INDEX_YET_ON "Index %s already exists on %s" - 333 INDX_COL_NOTIN "Index column %s is not in table %s" - 334 INDX_EXIST_YET "Index entry already exists" - 335 INIT_FAILED "Failed to initialize %s processing" - 336 INT_COL_ERROR "Internal error for index column %s" - 337 INT_OVERFLOW "Integer overflow" - 338 INT_ZERO_DIVIDE "Integer divide by zero" - 339 INVALID_DISP "Invalid disposition" - 340 INVALID_FTYPE "SBV: invalid Ftype %d" - 341 INVALID_HANDLE "Invalid handle" - 342 INVALID_OPER "Invalid operator %d for %s" - 343 INV_COLUMN_TYPE "Invalid type %d for column %s" - 344 INV_COL_TYPE "Invalid column type %s" - 345 INV_DEF_READ "Invalid deferred Read rc=%d" - 346 INV_DIRCOL_OFST "Invalid DIRCOL offset %d" - 347 INV_MAP_POS "Invalid map position" - 348 INV_RAND_ACC "Invalid random access to non optimized table" - 349 INV_REC_POS "Invalid record position" - 350 INV_RESULT_TYPE "Invalid result type %s" - 351 INV_UPDT_TABLE "Table %s invalid for update" - 352 IN_WITHOUT_SUB "IN or EXISTS without array or subquery" - 353 KEY_ALLOC_ERR "Error allocating Key offset block" - 354 KEY_ALLOC_ERROR "Memory allocation error, Klen=%d n=%d" - 355 LINE_TOO_LONG "New line is too long" - 356 LIST "--List--" - 357 LOADING_FAILED "Loading of %s failed" - 358 LRECL_TOO_SMALL "Lrecl too small (headlen = %d)" - 359 MAKE_EMPTY_FILE "Making empty file %s: %s" - 360 MAKING "Making" - 361 MALLOC_ERROR "Memory allocation failed: %s returned Null" - 362 MAP_VIEW_ERROR "MapViewOfFile %s error rc=%d" - 363 MAXSIZE_ERROR "Cannot calculate max size on open table" - 364 MEM_ALLOC_ERR "Memory allocation error, %s size=%d" - 365 MEM_ALLOC_ERROR "Memory allocation error" - 366 MISPLACED_QUOTE "Misplaced quote in line %d" - 367 MISSING_ARG "Missing argument for operator %d" - 368 MISSING_FIELD "Missing field %d in %s line %d" - 369 MISSING_FNAME "Missing file name" - 370 MISSING_NODE "Missing %s node in %s" - 371 MISSING_ROWNODE "Can't find RowNode for row %d" - 372 MIS_TAG_LIST "Missing column tag list" - 373 MUL_MAKECOL_ERR "Tabmul MakeCol logical error" - 374 NAME_CONV_ERR "Error converting node name" - 375 NEW_DOC_FAILED "Cannot create new document" - 376 NEW_RETURN_NULL "New returned Null in PlugEvalLike" - 377 NEXT_FILE_ERROR "Couldn't find next file. rc=%d" - 378 NONCONT_EXCEPT "Noncontinuable exception" - 379 NOP_ZLIB_INDEX "Cannot do indexing on non optimized zlib table" - 380 NOT_A_DBF_FILE "Not a dBASE dbf file " - 381 NOT_FIXED_LEN "File %s is not fixed length, len=%d lrecl=%d" - 382 NO_0DH_HEAD "No 0Dh at end of header (dbc=%d)" - 383 NO_ACTIVE_DB "No active database" - 384 NO_CHAR_FROM "Cannot return char value from type %d" - 385 NO_DATE_FMT "No date format for valblock of type %d" - 386 NO_DEF_FNCCOL "Cannot find default function column" - 387 NO_DEF_PIVOTCOL "Cannot find default pivot column" - 388 NO_DIR_INDX_RD "No direct access of %s tables" - 389 NO_FEAT_SUPPORT "No %s support in this version" - 390 NO_FLD_FORMAT "Missing format for field %d of %s" - 391 NO_FORMAT_COL "Cannot format the type COLUMN" - 392 NO_FORMAT_TYPE "Cannot set format from type %d" - 393 NO_INDEX_READ "No indexed read for multiple tables" - 394 NO_KEY_COL "No key columns found" - 395 NO_KEY_UPDATE "Cannot update key names" - 396 NO_MAP_INSERT "MAP incompatible with Insert" - 397 NO_MATCHING_COL "No matching column %s in %s" - 398 NO_MATCH_COL "Cannot find matching column" - 399 NO_MEMORY "No memory" - 400 NO_MODE_PADDED "Mode not supported for padded files" - 401 NO_MUL_VCT "VCT tables cannot be multiple" - 402 NO_ODBC_DELETE "Delete should not be called for ODBC tables" - 403 NO_ODBC_DIRECT "Direct access of ODBC tables not implemented yet" - 404 NO_ODBC_MUL "Multiple(2) not supported for ODBC tables" - 405 NO_ODBC_SPECOL "No ODBC special columns" - 406 NO_PART_DEL "No partial delete of %s files" - 407 NO_PART_MAP "Partial mapping not implemented for this OS" - 408 NO_PAR_BLK_INS "Cannot insert partial block yet" - 409 NO_PIV_DIR_ACC "No direct access to PIVOT tables" - 410 NO_READ_32 "Can't read 32 bytes" - 411 NO_RECOV_SPACE "Cannot recover space in index file" - 412 NO_ROWID_FOR_AM "Can't get RowID in direct access for tables of type %s" - 413 NO_ROW_NODE "Row node name is not defined" - 414 NO_SECTION_NAME "Missing section name" - 415 NO_SEC_UPDATE "Cannot update section names" - 416 NO_SETPOS_YET "%s SetPos not implemented yet" - 417 NO_SPEC_COL "No MySQL special columns" - 418 NO_SUB_VAL "No sub value for array of type %d" - 419 NO_TABCOL_DATA "No data found for table %s column %s" - 420 NO_TABLE_DEL "Delete not enabled for %s tables " - 421 NO_TAB_DATA "No data found for table %s" - 422 NO_VCT_DELETE "Partial delete not yet implemented for VCT files" - 423 NO_ZIP_DELETE "Delete Zip files not implemented yet" - 424 OPENING "Opening" - 425 OPEN_EMPTY_FILE "Opening empty file %s: %s" - 426 OPEN_ERROR "Open error %d in mode %d on %s: " - 427 OPEN_ERROR_IS "Open error on %s: %s" - 428 OPEN_MODE_ERROR "Open(%s) error %d on %s" - 429 OPEN_STRERROR "open error: %s" - 430 OPTBLK_RD_ERR "Error reading opt block values: %s" - 431 OPTBLK_WR_ERR "Error writing opt block values: %s" - 432 OPTIMIZING "Optimizing " - 433 OPT_BMAP_RD_ERR "Error reading opt bitmaps: %s" - 434 OPT_BMAP_WR_ERR "Error writing opt bitmaps: %s" - 435 OPT_CANCELLED "Optimize cancelled by User" - 436 OPT_DVAL_RD_ERR "Error reading distinct values: %s" - 437 OPT_DVAL_WR_ERR "Error writing distinct values: %s" - 438 OPT_HEAD_RD_ERR "Error reading opt file header: %s" - 439 OPT_HEAD_WR_ERR "Error writing opt file header: %s" - 440 OPT_LOGIC_ERR "Logical error in SetBitmap, i=%d" - 441 OPT_MAX_RD_ERR "Error reading opt max values: %s" - 442 OPT_MAX_WR_ERR "Error writing opt max values: %s" - 443 OPT_MIN_RD_ERR "Error reading opt min values: %s" - 444 OPT_MIN_WR_ERR "Error writing opt min values: %s" - 445 OPT_NOT_MATCH "Non-matching opt file %s" - 446 PAGE_ERROR "In page error" - 447 PARM_CNT_MISS "Parameter count mismatch" - 448 PREC_VBLP_NULL "ARRAY SetPrecision: Vblp is NULL" - 449 PRIV_INSTR "Privileged instruction" - 450 PROCADD_ERROR "Error %d getting address of %s" - 451 QUERY_CANCELLED "Query Cancelled by User" - 452 RANGE_NO_JOIN "Range is not meant for join index" - 453 RC_READING "rc=%d reading table %s" - 454 READY "Ready" - 455 READ_ERROR "Error reading %s: %s" - 456 READ_ONLY "Cannot modify this read/only protected table" - 457 READ_SEEK_ERROR "Read seek error: %s" - 458 REGISTER_ERR "Unable to register NS with prefix='%s' and href='%s'" - 459 REMOVE_ERROR "Error removing %s: %s" - 460 RENAME_ERROR "Error renaming %s to %s: %s" - 461 ROWID_NOT_IMPL "RowNumber not implemented for tables of type %s" - 462 SEC_KEY_FIRST "Section and key names must come first on Insert" - 463 SEC_NAME_FIRST "Section name must come first on Insert" - 464 SEP_IN_FIELD "Field %d contains the separator character" - 465 SEQUENCE_ERROR "Sequence error on statement allocation" - 466 SETEOF_ERROR "Error %d in SetEndOfFile" - 467 SETRECPOS_NIY "SetRecpos not implemented for this table type" - 468 SET_STR_TRUNC "SetValue: String would be truncated" - 469 SFP_ERROR "SetFilePointer error: %s" - 470 SHARED_LIB_ERR "Error loading shared library %s: %s" - 471 SINGLE_STEP "Single step" - 472 SORTING_VAL "Sorting %d values" - 473 SPCOL_READONLY "Special column %s is Read Only" - 474 SQL_CONF_ERROR "SQL Error: SQL_CONFORMANCE" - 475 SRCH_CLOSE_ERR "Couldn't close search handle" - 476 SRC_TABLE_UNDEF "Source table is not defined" - 477 STACK_OVERFLOW "Stack overflow" - 478 TABDIR_READONLY "DIR tables are read/only" - 479 TABLE_NOT_OPT "Not an optimizable table" - 480 TABLE_NO_INDEX "Table %s is not indexable" - 481 TABLE_READ_ONLY "%s tables are read only " - 482 TABMUL_READONLY "Multiple tables are read/only" - 483 TOO_MANY_FIELDS "Too many fields line %d of %s" - 484 TOO_MANY_JUMPS "Too many jump levels" - 485 TOO_MANY_KEYS "Too many keys (%d)" - 486 TO_BLK_IS_NULL "To Blk is NULL" - 487 TRUNCATE_ERROR "truncate error: %s" - 488 TRUNC_BY_ESTIM "truncated by Estimate" - 489 TYPE_MISMATCH "Key and source are not of the same type" - 490 TYPE_VALUE_ERR "Column %s type(%s)/value(%s) mismatch" - 491 UNBALANCE_QUOTE "Unbalanced quote in line %d" - 492 UNDEFINED_AM "COLBLK %s: undefined Access Method" - 493 UNKNOWN_EXCPT "Unknown exception" - 494 UNMATCH_FIL_ARG "Unmatched filter argument" - 495 UPDATE_ERROR "Error updating %s" - 496 UPD_ZIP_NOT_IMP "Updating ZDOS tables not implemented yet" - 497 VALSTR_TOO_LONG "Value %s too long for string of length %d" - 498 VALTYPE_NOMATCH "Non matching Value types" - 499 VALUE_ERROR "Column %s: value is null" - 500 VALUE_TOO_BIG "Value %lld too big for column %s" - 501 VALUE_TOO_LONG "Value %s too long for column %s of length %d" - 502 VAL_ALLOC_ERR "Cannot allocate value node" - 503 VIR_NO_DELETE "Delete not allowed for %s tables" - 504 VIR_READ_ONLY "Virtual %s tables are read only" - 505 VOID_FIRST_ARG "First argument should not be void" - 506 WORK_AREA "Work area: %s" - 507 WRITE_SEEK_ERR "Write seek error: %s" - 508 WRITE_STRERROR "Error writing %s: %s" - 509 WRITING "Writing" - 510 WRITING_ERROR "Error writing to %s: %s" - 511 WS_CONV_ERR "Error converting %s to WS" - 512 XCOL_MISMATCH "Column %s mismatch in index" - 513 XFILE_READERR "Error %d reading index file" - 514 XFILE_WRITERR "Error writing index file: %s" - 515 XMLTAB_INIT_ERR "Error initializing XML table" - 516 XML_INIT_ERROR "Error initializing new XML file" - 517 XPATH_CNTX_ERR "Unable to create new XPath context" - 518 XPATH_EVAL_ERR "Unable to evaluate xpath location '%s'" - 519 XPATH_NOT_SUPP "Unsupported Xpath for column %s" diff --git a/storage/connect/french.msg b/storage/connect/french.msg deleted file mode 100644 index 019df607be3..00000000000 --- a/storage/connect/french.msg +++ /dev/null @@ -1,366 +0,0 @@ - 100 IDS_TABLES "Table Entêtes" - 101 IDS_TAB_01 "Catalogue" - 102 IDS_TAB_02 "Schéma" - 103 IDS_TAB_03 "Nom" - 104 IDS_TAB_04 "Type" - 105 IDS_TAB_05 "Remarque" - 106 IDS_COLUMNS "Colonne Entêtes" - 107 IDS_COL_01 "Cat_Table" - 108 IDS_COL_02 "Schem_Table" - 109 IDS_COL_03 "Nom_Table" - 110 IDS_COL_04 "Nom_Colonne" - 111 IDS_COL_05 "Type_Données" - 112 IDS_COL_06 "Nom_Type" - 113 IDS_COL_07 "Précision" - 114 IDS_COL_08 "Longueur" - 115 IDS_COL_09 "Echelle" - 116 IDS_COL_10 "Base" - 117 IDS_COL_11 "Nullifiable" - 118 IDS_COL_12 "Remarques" - 119 IDS_PKEY "Clé Entêtes" - 120 IDS_PKY_01 "Cat_Table" - 121 IDS_PKY_02 "Schem_Table" - 122 IDS_PKY_03 "Nom_Table" - 123 IDS_PKY_04 "Nom_Colonne" - 124 IDS_PKY_05 "Numéro_Clé" - 125 IDS_PKY_06 "Nom_Clé" - 126 IDS_STAT "Stat Entêtes" - 127 IDS_STA_01 "Table_Catalog" - 128 IDS_STA_02 "Table_Schema" - 129 IDS_STA_03 "Table_Name" - 130 IDS_STA_04 "Non_Unique" - 131 IDS_STA_05 "Index_Qualifier" - 132 IDS_STA_06 "Index_Name" - 133 IDS_STA_07 "Type" - 134 IDS_STA_08 "Seq_in_Index" - 135 IDS_STA_09 "Column_Name" - 136 IDS_STA_10 "Collation" - 137 IDS_STA_11 "Cardinality" - 138 IDS_STA_12 "Pages" - 139 IDS_STA_13 "Filter_Condition" - 140 IDS_DRIVER "Driver Entêtes" - 141 IDS_DRV_01 "Description" - 142 IDS_DRV_02 "Attributs" - 143 IDS_DSRC "DataSrc Entêtes" - 144 IDS_DSC_01 "Nom" - 145 IDS_DSC_02 "Description" - 200 ACCESS_VIOLATN "Violation accès mémoire" - 201 ADD_BAD_TYPE "Ajout d'une valeur de type %s non conforme dans un tableau %s" - 202 ALLOC_ERROR "Erreur d'allocation de %s" - 203 ANSWER_TYPE "Réponse de type" - 204 API_CONF_ERROR "Erreur SQL: API_CONFORMANCE" - 205 APPL_NOT_INIT "Application non initialisée" - 206 ARRAY_BNDS_EXCD "Hors limite de tableau" - 207 BAD_ARRAY_OPER "Les tableaux doivent utiliser l'opérateur IN" - 208 BAD_ARRAY_TYPE "Type=%d invalide pour un tableau" - 209 BAD_ARRAY_VAL "Les tableaux doivent avoir le même nombre de valeurs" - 210 BAD_BIN_FMT "Format invalide %c pour la colonne BIN %s" - 211 BAD_BLK_ESTIM "Nombre de blocs supérieur à l'estimation" - 212 BAD_BLK_SIZE "Taille du bloc %d non conforme" - 213 BAD_BYTE_NUM "Le nombre d'octets écrits est faux" - 214 BAD_BYTE_READ "Le nombre d'octets lus est faux" - 215 BAD_COL_TYPE "Type invalide %s pour la colonne %s" - 216 BAD_COL_XPATH "Xpath invalide colonne %s de la table HTML %s" - 217 BAD_CONST_TYPE "Type=%d invalide pour une constante" - 218 BAD_CONV_TYPE "Convertion de type invalide %d" - 219 BAD_DATETIME "Valeur date/temps invalide" - 220 BAD_DBF_FILE "Le fichier DBF %s est altéré" - 221 BAD_DBF_REC "Fichier DBF %s altéré enregistrement %d" - 222 BAD_DBF_TYPE "Type DBF %c non supporté colonne %s" - 223 BAD_DIRECTORY "Répertoire invalide %s: %s" - 224 BAD_FIELD_RANK "Rang %d invalide pour la colonne %s" - 225 BAD_FIELD_TYPE "Mauvais type de champ %s" - 226 BAD_FILE_HANDLE "Handle de fichier invalide: %s" - 227 BAD_FILTER "Mauvais filtre: Opc=%d B_T=%d %d Type=%d %d" - 228 BAD_FILTER_CONV "Conversion filtre incorrecte, B_T=%d,%d" - 229 BAD_FILTER_OP "Opérateur de filtre invalide %d" - 230 BAD_FLD_FORMAT "Format invalide pour le champs %d de %s" - 231 BAD_FLD_LENGTH "Champs %s trop long (%s --> %d) ligne %d de %s" - 232 BAD_FREQ_SET "Spécification erronnée de Freq pour la colonne %s" - 233 BAD_FUNC_MODE "%s: mode invalide %d" - 234 BAD_HANDLE_VAL "Valeur Handle invalide" - 235 BAD_HEADER "Fichier %s: bloc en-tête altéré" - 236 BAD_HEAD_END "Lecture fin d'en-tête impossible" - 237 BAD_INDEX_FILE "Fichier index %s corrompu" - 238 BAD_LINEFLD_FMT "Format invalide ligne %d champs %d de %s" - 239 BAD_LINE_LEN "Longueur ligne non égale à Lrecl" - 240 BAD_LRECL "Disparité lrecl table/fichier (%d,%hd)" - 241 BAD_NODE_TYPE "Type noeud erroné pour la table" - 242 BAD_OFFSET_VAL "Nul offset invalide pour une table CSV" - 243 BAD_OPEN_MODE "Mode d'ouverture invalide %d" - 244 BAD_PARAM_TYPE "%.8s: Paramètre de type=%d invalide" - 245 BAD_PARM_COUNT "Nombre de paramètres incohérent" - 246 BAD_QUOTE_FIELD "Quote manquante dans %s champs %d ligne %d" - 247 BAD_READ_NUMBER "Mauvais nombre %d de valeurs lues dans %s" - 248 BAD_RECFM "Recfm type %d invalide pour DOSCOL" - 249 BAD_RECFM_VAL "Valeur invalide %d de Recfm" - 250 BAD_SET_CASE "La casse d'un tableau ne peut pas passer de non respect à respecter" - 251 BAD_SET_STRING "SetValue: appel invalide pour STRING" - 252 BAD_SPECIAL_COL "Colonne spéciale invalide %s" - 253 BAD_SPEC_COLUMN "Colonne spéciale invalide pour ce type de table" - 254 BAD_TABLE_TYPE "Type invalide %s pour la table %s" - 255 BAD_TYPE_LIKE "Type(%d)= %d invalide pour LIKE" - 256 BAD_VALBLK_INDX "Valeur hors limites de l'index du bloc de valeurs" - 257 BAD_VALBLK_TYPE "Type=%d invalide pour un bloc de valeurs" - 258 BAD_VALNODE "Type %d invalide pour le noeud valeur colonne %s" - 259 BAD_VALUE_TYPE "Type de valeur invalide %d" - 260 BAD_VAL_UPDATE "Impossible de déterminer quelle valeur %s doit être mise à jour" - 261 BAS_NS_LIST "Format invalide de la liste des espace-noms" - 262 BIN_F_TOO_LONG "Valeur trop longue pour le champ %s (%d --> %d)" - 263 BIN_MODE_FAIL "Echec mode binaire: %s" - 264 BLKTYPLEN_MISM "Disparité types/longueurs de bloc dans SetValue" - 265 BLK_IS_NULL "Blk est nul" - 266 BREAKPOINT "Point de contrôle" - 267 BUILD_INDEX "Construction index %s sur %s" - 268 CANNOT_OPEN "Ouverture impossible de %s" - 269 CHSIZE_ERROR "Erreur dans chsize: %s" - 270 COL_ALLOC_ERR "Allocation impossible du noeud colonne" - 271 COL_ISNOT_TABLE "La colonne %s n'est pas dans la table %s" - 272 COL_NOT_SORTED "La colonne %s de la table %s n'est pas triée" - 273 COL_NUM_MISM "Disparité du nombre de colonnes" - 274 COM_ERROR "Erreur Com" - 275 CONCAT_SUBNODE "Concaténation de sous-noeuds impossible" - 276 CONNECT_CANCEL "Connection interrompue par l'utilisateur" - 277 CONTROL_C_EXIT "Exit par Ctrl-C" - 278 DATABASE_LOADED "Base de données %s chargée" - 279 DATA_MISALIGN "Mauvais alignement pour ce type de données" - 280 DBASE_FILE "Fichier dBASE dbf: " - 281 DEF_ALLOC_ERROR "Erreur d'allocation de la classe DEF %s" - 282 DEL_FILE_ERR "Erreur à l'effacement de %s" - 283 DEL_READ_ERROR "Delete: erreur en lecture req=%d len=%d" - 284 DEL_WRITE_ERROR "Delete: erreur en écriture: %s" - 285 DEPREC_FLAG "Option Flag périmée, utiliser Coltype" - 286 DLL_LOAD_ERROR "Erreur %d au chargement du module %s" - 287 DOM_NOT_SUPP "MS-DOM non supporté par cette version" - 288 DVAL_NOTIN_LIST "Valeur %s non trouvée dans la liste des valeurs distinctes de la colonne %s" - 289 EMPTY_DOC "Document vide" - 290 EMPTY_FILE "%s du fichier vide %s: " - 291 EOF_AFTER_LINE "Fin de fichier après la ligne %d" - 292 EOF_INDEX_FILE "EOF lisant le fichier index" - 293 ERROR_IN_LSK "Erreur %d dans lseek64" - 294 ERROR_IN_SFP "Erreur %d dans SetFilePointer" - 295 ERR_READING_REC "Erreur lisant l'enregistrement %d de %s" - 296 FAIL_ADD_NODE "L'ajout du noeud %s dans la table a échoué" - 297 FETCH_NO_RES "Fetch: Pas de Résultats" - 298 FIELD_TOO_LONG "Valeur trop longue pour le champs %d ligne %d" - 299 FILELEN_ERROR "Erreur dans %s pour %s" - 300 FILE_IS_EMPTY "Le fichier %s est vide" - 301 FILE_MAP_ERR "Erreur de File mapping" - 302 FILE_MAP_ERROR "CreateFileMapping %s erreur rc=%d" - 303 FILE_OPEN_YET "Fichier %s déjà ouvert" - 304 FILE_UNFOUND "Fichier %s non trouvé" - 305 FLD_TOO_LNG_FOR "Champs %d trop long pour %s ligne %d de %s" - 306 FLT_BAD_RESULT "Virgule flottante: résultat inexacte" - 307 FLT_DENORMAL_OP "Opérande virgule flottante non normalisé" - 308 FLT_INVALID_OP "Opération virgule flottante invalide" - 309 FLT_OVERFLOW "Dépassement de capacité virgule flottante" - 310 FLT_STACK_CHECK "Virgule flottante: Erreur de la pile" - 311 FLT_UNDERFLOW "Sous-dépassement de capacité virgule flottante" - 312 FLT_ZERO_DIVIDE "Virgule flottante: division par zéro" - 313 FMT_WRITE_NIY "L'écriture des fichiers %s n'est pas encore implémentée" - 314 FOXPRO_FILE "Fichier FoxPro: " - 315 FPUTS_ERROR "Erreur dans fputs: %s" - 316 FSEEK_ERROR "Erreur dans fseek: %s" - 317 FSETPOS_ERROR "Erreur dans fseek pour i=%d" - 318 FTELL_ERROR "Erreur dans ftell enregistrement=%d: %s" - 319 FUNCTION_ERROR "Erreur dans %s: %d" - 320 FUNC_ERRNO "Erreur %d dans %s" - 321 FUNC_ERROR "Erreur dans %s" - 322 FUNC_ERR_S "Erreur dans %s: %s" - 323 FWRITE_ERROR "Erreur dans fwrite: %s" - 324 GET_DIST_VALS "Récupération des valeurs distinctes de " - 325 GET_FUNC_ERR "Erreur en recherche de la fonction %s: %s" - 326 GLOBAL_ERROR "Erreur d'allocation de Global (taille=%d)\n" - 327 GUARD_PAGE "Violation de page de garde" - 328 GZOPEN_ERROR "gzopen %s: erreur %d sur %s" - 329 ILLEGAL_INSTR "Instruction illégale" - 330 ILL_FILTER_CONV "Conversion implicite illégale dans un filtre" - 331 INDEX_NOT_UNIQ "L'index n'est pas Unique" - 332 INDEX_YET_ON "L'index %s existe déjà sur %s" - 333 INDX_COL_NOTIN "La colonne index %s n'existe pas dans la table %s" - 334 INDX_EXIST_YET "L'entrée index existe déjà" - 335 INIT_FAILED "L'initialisation de %s a échoué" - 336 INT_COL_ERROR "Erreur interne sur la colonne index %s" - 337 INT_OVERFLOW "Dépassement de capacité sur entier" - 338 INT_ZERO_DIVIDE "Division entière par zéro" - 339 INVALID_DISP "Disposition invalide" - 340 INVALID_FTYPE "SBV: Ftype %d invalide" - 341 INVALID_HANDLE "Poignée invalide" - 342 INVALID_OPER "Opérateur invalide %d pour %s" - 343 INV_COLUMN_TYPE "Type %d Invalide pour la colonne %s" - 344 INV_COL_TYPE "Type de colonne %s invalide" - 345 INV_DEF_READ "Lecture différée invalide rc=%d" - 346 INV_DIRCOL_OFST "Offset invalide pour une colonne DIR" - 347 INV_MAP_POS "Position mémoire invalide" - 348 INV_RAND_ACC "L'accès aléatoire d'une table non optimisée est impossible" - 349 INV_REC_POS "Position d'enregistrement invalide" - 350 INV_RESULT_TYPE "Type de résultat invalide %s" - 351 INV_UPDT_TABLE "Table %s invalide pour Update" - 352 IN_WITHOUT_SUB "IN ou EXISTS sans tableau ou subquery" - 353 KEY_ALLOC_ERR "Erreur d'allocation d'un bloc offset clé" - 354 KEY_ALLOC_ERROR "Erreur d'allocation mémoire, Klen=%d n=%d" - 355 LINE_TOO_LONG "La nouvelle ligne est trop longue" - 356 LIST "--Liste--" - 357 LOADING_FAILED "Le chargement de %s a échoué" - 358 LRECL_TOO_SMALL "Lrecl trop petit (longueur en-tête = %d)" - 359 MAKE_EMPTY_FILE "Génération du fichier vide %s: %s" - 360 MAKING "Génération" - 361 MALLOC_ERROR "Allocation mémoire impossible par %s" - 362 MAP_VIEW_ERROR "MapViewOfFile %s erreur rc=%d" - 363 MAXSIZE_ERROR "Maxsize incalculable sur table ouverte" - 364 MEM_ALLOC_ERR "Erreur d'allocation mémoire, taille %s = %d" - 365 MEM_ALLOC_ERROR "Erreur d'allocation mémoire" - 366 MISPLACED_QUOTE "Appostrophe mal placée ligne %d" - 367 MISSING_ARG "Argument manquant pour l'opérateur %d" - 368 MISSING_FIELD "Champs %d manquant dans %s ligne %d" - 369 MISSING_FNAME "Nom du fichier manquant" - 370 MISSING_NODE "Noeud %s manquant dans %s" - 371 MISSING_ROWNODE "Impossible de trouver le noeud de la ligne %d" - 372 MIS_TAG_LIST "Liste des balises colonne manquante" - 373 MUL_MAKECOL_ERR "Erreur logique dans TABMUL::MakeCol" - 374 NAME_CONV_ERR "Erreur de convertion du nom de noeud" - 375 NEW_DOC_FAILED "Impossible de créer le nouveau document" - 376 NEW_RETURN_NULL "NULL renvoyé par New dans PlugEvalLike" - 377 NEXT_FILE_ERROR "Erreur en recherche du fichier suivant. rc=%s" - 378 NONCONT_EXCEPT "Exception non-continuable" - 379 NOP_ZLIB_INDEX "L'indexage d'une table zlib non optimisée est impossible" - 380 NOT_A_DBF_FILE "Le fichier n'a pas le format dBASE dbf " - 381 NOT_FIXED_LEN "Fichier %s non fixe, len=%d lrecl=%d" - 382 NO_0DH_HEAD "0DH manquant en fin d'en-tête (dbc=%d)" - 383 NO_ACTIVE_DB "Pas de base de données active" - 384 NO_CHAR_FROM "Conversion de type %d en caractères impossible" - 385 NO_DATE_FMT "Pas de format date pour le valblock de type %d" - 386 NO_DEF_FNCCOL "Colonne fonction par défaut introuvable" - 387 NO_DEF_PIVOTCOL "Colonne pivot par défaut introuvable" - 388 NO_DIR_INDX_RD "Pas d'accès directe des tables %s" - 389 NO_FEAT_SUPPORT "%s non supporté dans cette version" - 390 NO_FLD_FORMAT "Format absent pour le champs %d de %s" - 391 NO_FORMAT_COL "Type COLUMN informattable" - 392 NO_FORMAT_TYPE "Le format ne peut pas être défini à partir du type %d" - 393 NO_INDEX_READ "Pas d'accès directe des tables multiples" - 394 NO_KEY_COL "Pas de colonne clé trouvée" - 395 NO_KEY_UPDATE "Le nom des clés ne peut pas être modifié" - 396 NO_MAP_INSERT "MAP incompatible avec Insert" - 397 NO_MATCHING_COL "Pas de colonne correspondant à %s dans %s" - 398 NO_MATCH_COL "Colonne correspondante introuvable" - 399 NO_MEMORY "Mémoire pleine" - 400 NO_MODE_PADDED "Mode non supporté pour les fichiers 'padded'" - 401 NO_MUL_VCT "Les tables VCT ne peuvent pas être multiples" - 402 NO_ODBC_DELETE "Delete ne devrait pas être appelé pour les tables ODBC" - 403 NO_ODBC_DIRECT "Accès directe des tables ODBC non encore implémenté" - 404 NO_ODBC_MUL "Multiple(2) non supporté pour les tables ODBC" - 405 NO_ODBC_SPECOL "Pas de colonne spéciale ODBC" - 406 NO_PART_DEL "Delete partiel des fichier %s impossible" - 407 NO_PART_MAP "Mapping partiel non implémenté pour cet OS" - 408 NO_PAR_BLK_INS "Insertion de bloc partiel impossible" - 409 NO_PIV_DIR_ACC "Pas d'accès directe aux tables PIVOT" - 410 NO_READ_32 "Lecture de 32 octets impossible" - 411 NO_RECOV_SPACE "Espace non recouvrable dans le fichier index" - 412 NO_ROWID_FOR_AM "Accès direct impossible de ROWID pour les tables de type %s" - 413 NO_ROW_NODE "Le nom du Rownode n'est pas défini" - 414 NO_SECTION_NAME "Nom de section manquant" - 415 NO_SEC_UPDATE "Les noms de section ne peuvent pas être modifiés" - 416 NO_SETPOS_YET "SetPos pas encore implémenté pour les fichier %s" - 417 NO_SPEC_COL "Pas de colonne spéciales MYSQL" - 418 NO_SUB_VAL "Pas de sous-value d'un tableau de type %d" - 419 NO_TABCOL_DATA "Pas de données pour la table %s colonne %s" - 420 NO_TABLE_DEL "Delete non autorisé pour les tables %s " - 421 NO_TAB_DATA "Pas de données pour la table %s" - 422 NO_VCT_DELETE "Délétion Partielle non implémentée pour les fichiers VCT" - 423 NO_ZIP_DELETE "Delete sur fichier Zip non encore implementé" - 424 OPENING "Ouverture" - 425 OPEN_EMPTY_FILE "Ouverture du fichier vide %s: %s" - 426 OPEN_ERROR "Erreur d'ouverture %d en mode %d sur %s: " - 427 OPEN_ERROR_IS "Erreur à l'ouverture de %s: %s" - 428 OPEN_MODE_ERROR "Erreur d'ouverture(%s) %d sur %s" - 429 OPEN_STRERROR "Erreur à l'ouverture: %s" - 430 OPTBLK_RD_ERR "Erreur à la lecture d'un bloc optimisation: %s" - 431 OPTBLK_WR_ERR "Erreur à l'écriture d'un bloc optimisation: %s" - 432 OPTIMIZING "Optimisation de " - 433 OPT_BMAP_RD_ERR "Erreur en lecture des bitmaps d'optimisation: %s" - 434 OPT_BMAP_WR_ERR "Erreur en écriture des bitmaps d'optimisation: %s" - 435 OPT_CANCELLED "Optimisation interrompue par l'utilisateur" - 436 OPT_DVAL_RD_ERR "Erreur en lecture des valeurs distinctes: %s" - 437 OPT_DVAL_WR_ERR "Erreur en écriture des valeurs distinctes: %s" - 438 OPT_HEAD_RD_ERR "Erreur en lecture de l'entête du fichier opt: %s" - 439 OPT_HEAD_WR_ERR "Erreur en écriture de l'entête du fichier opt: %s" - 440 OPT_LOGIC_ERR "Erreur logique dans SetBitmap, i=%d" - 441 OPT_MAX_RD_ERR "Erreur en lecture des valeurs maxi: %s" - 442 OPT_MAX_WR_ERR "Erreur en écriture des valeurs maxi: %s" - 443 OPT_MIN_RD_ERR "Erreur en lecture des valeurs mini: %s" - 444 OPT_MIN_WR_ERR "Erreur en écriture des valeurs mini: %s" - 445 OPT_NOT_MATCH "Le fichier opt %s n'est pas à jour" - 446 PAGE_ERROR "Erreur de pagination" - 447 PARM_CNT_MISS "Disparité du nombre de Paramètres" - 448 PREC_VBLP_NULL "ARRAY SetPrecision: Vblp est NULL" - 449 PRIV_INSTR "Instruction privilégiée" - 450 PROCADD_ERROR "Erreur %d sur l'adresse de %s" - 451 QUERY_CANCELLED "Requête interrompue par l'utilisateur" - 452 RANGE_NO_JOIN "Range non compatible avec les index de jointure" - 453 RC_READING "rc=%d en lecture de la table %s" - 454 READY "Prêt" - 455 READ_ERROR "Erreur en lecture sur %s: %s" - 456 READ_ONLY "Cette table protégée en lecture seule ne peut être modifiée" - 457 READ_SEEK_ERROR "Erreur de recherche en lecture: %s" - 458 REGISTER_ERR "Enregistrement NS impossible, préfix='%s' et href='%s'" - 459 REMOVE_ERROR "Erreur en supprimant %s: %s" - 460 RENAME_ERROR "Erreur renommant %s en %s: %s" - 461 ROWID_NOT_IMPL "RowNumber non implémenté pour les tables de type %s" - 462 SEC_KEY_FIRST "Les sections et clés doivent être insérées en premier" - 463 SEC_NAME_FIRST "Le nom de section doit être en tête de liste en insertion" - 464 SEP_IN_FIELD "Le champ %d contient le caractère séparateur" - 465 SEQUENCE_ERROR "HSTMT: Allocation hors séquence" - 466 SETEOF_ERROR "Erreur %d dans SetEndOfFile" - 467 SETRECPOS_NIY "SetRecpos non implémenté pour ce type de table" - 468 SET_STR_TRUNC "SetValue: Chaîne de caractères tronquée" - 469 SFP_ERROR "Erreur sur SetFilePointer: %s" - 470 SHARED_LIB_ERR "Erreur au chargement de la librairie partagée %s: %s" - 471 SINGLE_STEP "Pas à pas" - 472 SORTING_VAL "Tri de %d valeurs" - 473 SPCOL_READONLY "La colonne spéciale %s est en lecture seulement" - 474 SQL_CONF_ERROR "Erreur SQL: SQL_CONFORMANCE" - 475 SRCH_CLOSE_ERR "Erreur à la fermeture de l'Handle de recherche" - 476 SRC_TABLE_UNDEF "La table source n'est pas définie" - 477 STACK_OVERFLOW "Dépassement de capacité de la pile" - 478 TABDIR_READONLY "Les tables DIR sont en lecture seulement" - 479 TABLE_NOT_OPT "Table non optimisable" - 480 TABLE_NO_INDEX "La table %s n'est pas indexable" - 481 TABLE_READ_ONLY "Les tables %s sont en lecture seulement " - 482 TABMUL_READONLY "Les tables multiples sont en lecture seulement" - 483 TOO_MANY_FIELDS "Trop de champs ligne %d de %s" - 484 TOO_MANY_JUMPS "Trop de niveaux de saut" - 485 TOO_MANY_KEYS "Trop de clés (%d)" - 486 TO_BLK_IS_NULL "To Blk est nul" - 487 TRUNCATE_ERROR "Erreur en troncation: %s" - 488 TRUNC_BY_ESTIM "Tronqué par l'option Estimate" - 489 TYPE_MISMATCH "Clé et source ne sont pas du même type" - 490 TYPE_VALUE_ERR "Colonne %s: disparité type(%s)/valeur(%s)" - 491 UNBALANCE_QUOTE "Appostrophe en trop ligne %d" - 492 UNDEFINED_AM "COLBLK %s: méthode d'accès indéfinie" - 493 UNKNOWN_EXCPT "Exception non répertoriée" - 494 UNMATCH_FIL_ARG "Argument de filtre dépareillé" - 495 UPDATE_ERROR "Erreur en Update sur %s" - 496 UPD_ZIP_NOT_IMP "Mise à jour des tables ZDOS non encore implementé" - 497 VALSTR_TOO_LONG "Valeur %s trop longue pour une chaîne de longueur %d" - 498 VALTYPE_NOMATCH "Disparité types de valeur" - 499 VALUE_ERROR "Colonne %s: bloc valeur nul" - 500 VALUE_TOO_BIG "Valeur %lld trop grande pour la colonne %s" - 501 VALUE_TOO_LONG "Valeur %s trop longue pour la colonne %s de longueur %d" - 502 VAL_ALLOC_ERR "Allocation impossible du noeud valeur" - 503 VIR_NO_DELETE "Delete impossible sur les tables %s" - 504 VIR_READ_ONLY "Les tables virtuelles %s sont en lecture seulement" - 505 VOID_FIRST_ARG "Le premier argument ne doit pas être vide" - 506 WORK_AREA "Espace de travail: %s" - 507 WRITE_SEEK_ERR "Erreur de recherche en écriture: %s" - 508 WRITE_STRERROR "Erreur en écriture sur %s: %s" - 509 WRITING "Ecriture" - 510 WRITING_ERROR "Erreur à l'écriture de %s: %s" - 511 WS_CONV_ERR "Erreur de convertion de %s en WS" - 512 XCOL_MISMATCH "La colonne %s ne correspond pas à l'index" - 513 XFILE_READERR "Erreur %d en lisant le fichier index" - 514 XFILE_WRITERR "Erreur en écrivant le fichier index: %s" - 515 XMLTAB_INIT_ERR "Erreur d'initialisation de la table XML" - 516 XML_INIT_ERROR "Erreur d'initialisation du nouveau fichier XML" - 517 XPATH_CNTX_ERR "Le nouveau contexte XPath ne peut être créé" - 518 XPATH_EVAL_ERR "Impossible d'évaluer l'emplacement xpath '%s'" - 519 XPATH_NOT_SUPP "Xpath non supporté colonne %s" diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 23066ae4d6b..4ac46d95d4e 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -169,9 +169,9 @@ #define JSONMAX 10 // JSON Default max grp size extern "C" { - char version[]= "Version 1.03.0007 October 20, 2015"; + char version[]= "Version 1.04.0003 October 25, 2015"; #if defined(__WIN__) - char compver[]= "Version 1.03.0007 " __DATE__ " " __TIME__; + char compver[]= "Version 1.04.0003 " __DATE__ " " __TIME__; char slash= '\\'; #else // !__WIN__ char slash= '/'; @@ -941,7 +941,7 @@ ulonglong ha_connect::table_flags() const // HA_NULL_IN_KEY | not implemented yet // HA_FAST_KEY_READ | causes error when sorting (???) HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER | - HA_NO_BLOBS | HA_CAN_TABLE_CONDITION_PUSHDOWN; + HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN; ha_connect *hp= (ha_connect*)this; PTOS pos= hp->GetTableOptionStruct(); @@ -1095,7 +1095,7 @@ bool GetBooleanTableOption(PGLOBAL g, PTOS options, char *opname, bool bdef) /****************************************************************************/ int GetIntegerTableOption(PGLOBAL g, PTOS options, char *opname, int idef) { - longlong opval= NO_IVAL; + ulonglong opval= NO_IVAL; if (!options) return idef; @@ -1134,10 +1134,10 @@ PTOS ha_connect::GetTableOptionStruct(TABLE_SHARE *s) { TABLE_SHARE *tsp= (tshp) ? tshp : (s) ? s : table_share; - return (tsp && (!tsp->db_plugin || - !stricmp(plugin_name(tsp->db_plugin)->str, "connect") || - !stricmp(plugin_name(tsp->db_plugin)->str, "partition"))) - ? tsp->option_struct : NULL; + return (tsp && (!tsp->db_plugin || + !stricmp(plugin_name(tsp->db_plugin)->str, "connect") || + !stricmp(plugin_name(tsp->db_plugin)->str, "partition"))) + ? tsp->option_struct : NULL; } // end of GetTableOptionStruct /****************************************************************************/ @@ -2191,99 +2191,159 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *) /***********************************************************************/ int ha_connect::CheckRecord(PGLOBAL g, const uchar *, uchar *newbuf) { - return ScanRecord(g, newbuf); + return ScanRecord(g, newbuf); } // end of dummy CheckRecord +/***********************************************************************/ +/* Return true if this field is used in current indexing. */ +/***********************************************************************/ +bool ha_connect::IsIndexed(Field *fp) +{ + if (active_index < MAX_KEY) { + KEY_PART_INFO *kpart; + KEY *kfp= &table->key_info[active_index]; + uint rem= kfp->user_defined_key_parts; + + for (kpart= kfp->key_part; rem; rem--, kpart++) + if (kpart->field == fp) + return true; + + } // endif active_index + + return false; +} // end of IsIndexed + + /***********************************************************************/ /* Return the where clause for remote indexed read. */ /***********************************************************************/ -bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q, - const void *key, int klen) +bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL vop, char q, + const key_range *kr) { - const uchar *ptr; - uint rem, len, stlen; //, prtlen; - bool nq, oom, b= false; - Field *fp; - KEY *kfp; - KEY_PART_INFO *kpart; + const uchar *ptr; +//uint i, rem, len, klen, stlen; + uint i, rem, len, stlen; + bool nq, both, oom= false; + OPVAL op; + Field *fp; + const key_range *ranges[2]; + my_bitmap_map *old_map; + KEY *kfp; + KEY_PART_INFO *kpart; if (active_index == MAX_KEY) return false; - else if (!key) { - strcpy(g->Message, "MakeKeyWhere: No key"); - return true; - } // endif key - oom= qry->Append(" WHERE ("); - kfp= &table->key_info[active_index]; - rem= kfp->user_defined_key_parts, - len= klen, - ptr= (const uchar *)key; + ranges[0]= kr; + ranges[1]= (end_range && !eq_range) ? &save_end_range : NULL; - for (kpart= kfp->key_part; rem; rem--, kpart++) { - fp= kpart->field; - stlen= kpart->store_length; -// prtlen= MY_MIN(stlen, len); - nq= fp->str_needs_quotes(); + if (!ranges[0] && !ranges[1]) { + strcpy(g->Message, "MakeKeyWhere: No key"); + return true; + } else + both= ranges[0] && ranges[1]; - if (b) - oom|= qry->Append(" AND "); - else - b= true; + kfp= &table->key_info[active_index]; + old_map= dbug_tmp_use_all_columns(table, table->write_set); - oom|= qry->Append(q); - oom|= qry->Append((PSZ)fp->field_name); - oom|= qry->Append(q); + for (i = 0; i <= 1; i++) { + if (ranges[i] == NULL) + continue; - switch (op) { - case OP_EQ: - case OP_GT: - case OP_GE: - case OP_LT: - case OP_LE: - oom |= qry->Append((PSZ)GetValStr(op, false)); - break; - default: - oom|= qry->Append(" ??? "); - } // endwitch op + if (both && i > 0) + oom|= qry->Append(") AND ("); + else + oom|= qry->Append(" WHERE ("); - if (nq) - oom|= qry->Append('\''); +// klen= len= ranges[i]->length; + len= ranges[i]->length; + rem= kfp->user_defined_key_parts; + ptr= ranges[i]->key; - if (kpart->key_part_flag & HA_VAR_LENGTH_PART) { - String varchar; - uint var_length= uint2korr(ptr); + for (kpart= kfp->key_part; rem; rem--, kpart++) { + fp= kpart->field; + stlen= kpart->store_length; + nq= fp->str_needs_quotes(); - varchar.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH, - var_length, &my_charset_bin); - oom|= qry->Append(varchar.ptr(), varchar.length()); - } else { - char strbuff[MAX_FIELD_WIDTH]; - String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res; + if (kpart != kfp->key_part) + oom|= qry->Append(" AND "); - res= fp->val_str(&str, ptr); - oom|= qry->Append(res->ptr(), res->length()); - } // endif flag + if (q) { + oom|= qry->Append(q); + oom|= qry->Append((PSZ)fp->field_name); + oom|= qry->Append(q); + } else + oom|= qry->Append((PSZ)fp->field_name); - if (nq) - oom|= qry->Append('\''); + switch (ranges[i]->flag) { + case HA_READ_KEY_EXACT: +// op= (stlen >= len || !nq || fp->result_type() != STRING_RESULT) +// ? OP_EQ : OP_LIKE; + op= OP_EQ; + break; + case HA_READ_AFTER_KEY: + op= (stlen >= len) ? (!i ? OP_GT : OP_LE) : OP_GE; + break; + case HA_READ_KEY_OR_NEXT: + op= OP_GE; + break; + case HA_READ_BEFORE_KEY: + op= (stlen >= len) ? OP_LT : OP_LE; + break; + case HA_READ_KEY_OR_PREV: + op= OP_LE; + break; + default: + sprintf(g->Message, "cannot handle flag %d", ranges[i]->flag); + goto err; + } // endswitch flag - if (stlen >= len) - break; + oom|= qry->Append((PSZ)GetValStr(op, false)); - len-= stlen; + if (nq) + oom|= qry->Append('\''); - /* For nullable columns, null-byte is already skipped before, that is - ptr was incremented by 1. Since store_length still counts null-byte, - we need to subtract 1 from store_length. */ - ptr+= stlen - MY_TEST(kpart->null_bit); - } // endfor kpart + if (kpart->key_part_flag & HA_VAR_LENGTH_PART) { + String varchar; + uint var_length= uint2korr(ptr); + + varchar.set_quick((char*)ptr + HA_KEY_BLOB_LENGTH, + var_length, &my_charset_bin); + oom|= qry->Append(varchar.ptr(), varchar.length(), nq); + } else { + char strbuff[MAX_FIELD_WIDTH]; + String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res; + + res= fp->val_str(&str, ptr); + oom|= qry->Append(res->ptr(), res->length(), nq); + } // endif flag + + if (nq) + oom |= qry->Append('\''); + + if (stlen >= len) + break; + + len-= stlen; + + /* For nullable columns, null-byte is already skipped before, that is + ptr was incremented by 1. Since store_length still counts null-byte, + we need to subtract 1 from store_length. */ + ptr+= stlen - MY_TEST(kpart->null_bit); + } // endfor kpart + + } // endfor i if ((oom|= qry->Append(")"))) strcpy(g->Message, "Out of memory"); - return oom; + dbug_tmp_restore_column_map(table->write_set, old_map); + return oom; + +err: + dbug_tmp_restore_column_map(table->write_set, old_map); + return true; } // end of MakeKeyWhere @@ -2483,9 +2543,11 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond) case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: return NULL; + default: + break; } // endswitch type - if (trace) { + if (trace) { htrc("Field index=%d\n", pField->field->field_index); htrc("Field name=%s\n", pField->field->field_name); } // endif trace @@ -2562,8 +2624,9 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond) /***********************************************************************/ /* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */ /***********************************************************************/ -PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) +PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) { + AMT tty = filp->Type; char *body= filp->Body; unsigned int i; bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC); @@ -2596,7 +2659,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) List* arglist= cond_item->argument_list(); List_iterator li(*arglist); - Item *subitem; + const Item *subitem; p1= body + strlen(body); strcpy(p1, "("); @@ -2604,7 +2667,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) for (i= 0; i < arglist->elements; i++) if ((subitem= li++)) { - if (!CheckCond(g, filp, tty, subitem)) { + if (!CheckCond(g, filp, subitem)) { if (vop == OP_OR || nonul) return NULL; else @@ -2626,26 +2689,27 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) } else if (cond->type() == COND::FUNC_ITEM) { unsigned int i; -// int n; bool iscol, neg= FALSE; Item_func *condf= (Item_func *)cond; Item* *args= condf->arguments(); if (trace) htrc("Func type=%d argnum=%d\n", condf->functype(), - condf->argument_count()); - -// neg= condf-> + condf->argument_count()); switch (condf->functype()) { case Item_func::EQUAL_FUNC: - case Item_func::EQ_FUNC: vop= OP_EQ; break; - case Item_func::NE_FUNC: vop= OP_NE; break; - case Item_func::LT_FUNC: vop= OP_LT; break; - case Item_func::LE_FUNC: vop= OP_LE; break; - case Item_func::GE_FUNC: vop= OP_GE; break; - case Item_func::GT_FUNC: vop= OP_GT; break; - case Item_func::IN_FUNC: vop= OP_IN; + case Item_func::EQ_FUNC: vop= OP_EQ; break; + case Item_func::NE_FUNC: vop= OP_NE; break; + case Item_func::LT_FUNC: vop= OP_LT; break; + case Item_func::LE_FUNC: vop= OP_LE; break; + case Item_func::GE_FUNC: vop= OP_GE; break; + case Item_func::GT_FUNC: vop= OP_GT; break; + case Item_func::LIKE_FUNC: vop= OP_LIKE; break; + case Item_func::ISNOTNULL_FUNC: + neg = true; + case Item_func::ISNULL_FUNC: vop= OP_NULL; break; + case Item_func::IN_FUNC: vop= OP_IN; case Item_func::BETWEEN: ismul= true; neg= ((Item_func_opt_neg *)condf)->negated; @@ -2658,7 +2722,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) else if (ismul && tty == TYPE_AM_WMI) return NULL; // Not supported by WQL - if (x && (neg || !(vop == OP_EQ || vop == OP_IN))) + if (x && (neg || !(vop == OP_EQ || vop == OP_IN || vop == OP_NULL))) return NULL; for (i= 0; i < condf->argument_count(); i++) { @@ -2679,9 +2743,10 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) if (x && i) return NULL; - - if (pField->field->table != table) - return NULL; // Field does not belong to this table + else if (pField->field->table != table) + return NULL; // Field does not belong to this table + else if (tty != TYPE_AM_WMI && IsIndexed(pField->field)) + return NULL; // Will be handled by ReadKey else fop= GetFieldOptionStruct(pField->field); @@ -2712,7 +2777,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) strcat(body, fnm); } else if (args[i]->type() == COND::FUNC_ITEM) { if (tty == TYPE_AM_MYSQL) { - if (!CheckCond(g, filp, tty, args[i])) + if (!CheckCond(g, filp, args[i])) return NULL; } else @@ -2901,14 +2966,17 @@ const COND *ha_connect::cond_push(const COND *cond) goto fin; if (b) { - PCFIL filp= (PCFIL)PlugSubAlloc(g, NULL, sizeof(CONDFIL)); + PCFIL filp; + if ((filp= tdbp->GetCondFil()) && filp->Cond == cond && + filp->Idx == active_index && filp->Type == tty) + goto fin; // Already done + + filp= new(g) CONDFIL(cond, active_index, tty); filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0); *filp->Body= 0; - filp->Op= OP_XX; - filp->Cmds= NULL; - if (CheckCond(g, filp, tty, (Item *)cond)) { + if (CheckCond(g, filp, cond)) { if (trace) htrc("cond_push: %s\n", filp->Body); @@ -3372,13 +3440,13 @@ int ha_connect::index_end() /****************************************************************************/ /* This is internally called by all indexed reading functions. */ /****************************************************************************/ -int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len) +int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const key_range *kr) { int rc; //statistic_increment(ha_read_key_count, &LOCK_status); - switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len, mrr)) { + switch (CntIndexRead(xp->g, tdbp, op, kr, mrr)) { case RC_OK: xp->fnd++; rc= MakeRecord((char*)buf); @@ -3400,6 +3468,7 @@ int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len if (trace > 1) htrc("ReadIndexed: op=%d rc=%d\n", op, rc); + table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND; return rc; } // end of ReadIndexed @@ -3443,7 +3512,12 @@ int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len, htrc("%p index_read: op=%d\n", this, op); if (indexing > 0) { - rc= ReadIndexed(buf, op, key, key_len); + start_key.key= key; + start_key.length= key_len; + start_key.flag= find_flag; + start_key.keypart_map= 0; + + rc= ReadIndexed(buf, op, &start_key); if (rc == HA_ERR_INTERNAL_ERROR) { nox= true; // To block making indexes @@ -3516,6 +3590,7 @@ int ha_connect::index_first(uchar *buf) else if (indexing < 0) rc= HA_ERR_INTERNAL_ERROR; else if (CntRewindTable(xp->g, tdbp)) { + table->status= STATUS_NOT_FOUND; rc= HA_ERR_INTERNAL_ERROR; } else rc= rnd_next(buf); @@ -3716,6 +3791,7 @@ int ha_connect::rnd_next(uchar *buf) xp->fnd= xp->nfd= 0; } // endif nrd + table->status= (!rc) ? 0 : STATUS_NOT_FOUND; DBUG_RETURN(rc); } // end of rnd_next @@ -3747,7 +3823,7 @@ void ha_connect::position(const uchar *) //if (((PTDBASE)tdbp)->GetDef()->Indexable()) my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos()); - if (trace) + if (trace > 1) htrc("position: pos=%d\n", ((PTDBASE)tdbp)->GetRecpos()); DBUG_VOID_RETURN; @@ -5040,7 +5116,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, char *nsp= NULL, *cls= NULL; #endif // __WIN__ int port= 0, hdr= 0, mxr= 0, mxe= 0, rc= 0; - int cop __attribute__((unused))= 0; + int cop __attribute__((unused))= 0, lrecl= 0; #if defined(ODBC_SUPPORT) POPARM sop = NULL; char *ucnc = NULL; @@ -6198,6 +6274,10 @@ bool ha_connect::FileExists(const char *fn, bool bf) int n; struct stat info; + if (check_access(ha_thd(), FILE_ACL, table->s->db.str, + NULL, NULL, 0, 0)) + return true; + #if defined(__WIN__) s= "\\"; #else // !__WIN__ @@ -6683,10 +6763,10 @@ maria_declare_plugin(connect) PLUGIN_LICENSE_GPL, connect_init_func, /* Plugin Init */ connect_done_func, /* Plugin Deinit */ - 0x0103, /* version number (1.03) */ + 0x0104, /* version number (1.04) */ NULL, /* status variables */ connect_system_variables, /* system variables */ - "1.03.0007", /* string version */ - MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ + "1.04.0003", /* string version */ + MariaDB_PLUGIN_MATURITY_BETA /* maturity */ } maria_declare_plugin_end; diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h index 611f9ba0b54..05cc872fa2a 100644 --- a/storage/connect/ha_connect.h +++ b/storage/connect/ha_connect.h @@ -241,11 +241,12 @@ public: int MakeRecord(char *buf); int ScanRecord(PGLOBAL g, uchar *buf); int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf); - int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL, - uint key_len= 0); + int ReadIndexed(uchar *buf, OPVAL op, const key_range *kr= NULL); + bool IsIndexed(Field *fp); bool MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q, - const void *key, int klen); - inline char *Strz(LEX_STRING &ls); + const key_range *kr); + inline char *Strz(LEX_STRING &ls); + key_range start_key; /** @brief @@ -374,7 +375,7 @@ public: condition stack. */ virtual const COND *cond_push(const COND *cond); -PCFIL CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond); +PCFIL CheckCond(PGLOBAL g, PCFIL filp, const Item *cond); const char *GetValStr(OPVAL vop, bool neg); PFIL CondFilter(PGLOBAL g, Item *cond); //PFIL CheckFilter(PGLOBAL g); diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp index 3d03bea5d00..19947ab1899 100644 --- a/storage/connect/json.cpp +++ b/storage/connect/json.cpp @@ -1,5 +1,5 @@ /*************** json CPP Declares Source Code File (.H) ***************/ -/* Name: json.cpp Version 1.1 */ +/* Name: json.cpp Version 1.2 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */ /* */ @@ -31,11 +31,12 @@ /***********************************************************************/ /* Parse a json string. */ +/* Note: when pretty is not known, the caller set pretty to 3. */ /***********************************************************************/ -PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) +PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma) { - int i, rc; - bool b = false; + int i, rc, pretty = (ptyp) ? *ptyp : 3; + bool b = false, pty[3] = {true, true, true}; PJSON jsp = NULL; STRG src; @@ -48,6 +49,10 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) src.str = s; src.len = len; + // Trying to guess the pretty format + if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n'))) + pty[0] = false; + // Save stack and allocation environment and prepare error return if (g->jump_level == MAX_JUMP) { strcpy(g->Message, MSG(TOO_MANY_JUMPS)); @@ -58,21 +63,19 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) goto err; } // endif rc - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) switch (s[i]) { case '[': - if (jsp) { - strcpy(g->Message, "More than one item in file"); - goto err; - } else if (!(jsp = ParseArray(g, ++i, src))) + if (jsp) + goto tryit; + else if (!(jsp = ParseArray(g, ++i, src, pty))) goto err; break; case '{': - if (jsp) { - strcpy(g->Message, "More than one item in file"); - goto err; - } else if (!(jsp = ParseObject(g, ++i, src))) + if (jsp) + goto tryit; + else if (!(jsp = ParseObject(g, ++i, src, pty))) goto err; break; @@ -82,20 +85,16 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) case '\r': break; case ',': - if (jsp && pretty == 1) { + if (jsp && (pretty == 1 || pretty == 3)) { if (comma) *comma = true; + pty[0] = pty[2] = false; break; } // endif pretty sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty); goto err; - case '"': - if (!(jsp = ParseValue(g, i, src))) - goto err; - - break; case '(': b = true; break; @@ -106,18 +105,41 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) } // endif b default: - sprintf(g->Message, "Bad '%c' character near %.*s", - s[i], ARGS); - goto err; - }; // endswitch s[i] + if (jsp) + goto tryit; + else if (!(jsp = ParseValue(g, i, src, pty))) + goto err; - if (!jsp) - sprintf(g->Message, "Invalid Json string '%.*s'", 50, s); + break; + }; // endswitch s[i] + + if (!jsp) + sprintf(g->Message, "Invalid Json string '%.*s'", 50, s); + else if (ptyp && pretty == 3) { + *ptyp = 3; // Not recognized pretty + + for (i = 0; i < 3; i++) + if (pty[i]) { + *ptyp = i; + break; + } // endif pty + + } // endif ptyp g->jump_level--; return jsp; - err: +tryit: + if (pty[0] && (!pretty || pretty > 2)) { + if ((jsp = ParseArray(g, (i = 0), src, pty)) && ptyp && pretty == 3) + *ptyp = (pty[0]) ? 0 : 3; + + g->jump_level--; + return jsp; + } else + strcpy(g->Message, "More than one item in file"); + +err: g->jump_level--; return NULL; } // end of ParseJson @@ -125,11 +147,12 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) /***********************************************************************/ /* Parse a JSON Array. */ /***********************************************************************/ -PJAR ParseArray(PGLOBAL g, int& i, STRG& src) +PJAR ParseArray(PGLOBAL g, int& i, STRG& src, bool *pty) { char *s = src.str; int len = src.len; int level = 0; + bool b = (!i); PJAR jarp = new(g) JARRAY; PJVAL jvp = NULL; @@ -151,25 +174,32 @@ PJAR ParseArray(PGLOBAL g, int& i, STRG& src) jarp->InitArray(g); return jarp; - case ' ': + case '\n': + if (!b) + pty[0] = pty[1] = false; + case '\r': + case ' ': case '\t': - case '\n': - case '\r': break; default: if (level == 2) { sprintf(g->Message, "Unexpected value near %.*s", ARGS); return NULL; - } else if ((jvp = ParseValue(g, i, src))) { + } else if ((jvp = ParseValue(g, i, src, pty))) jarp->AddValue(g, jvp); - level = 2; - } else + else return NULL; - level = 2; + level = (b) ? 1 : 2; break; }; // endswitch s[i] + if (b) { + // Case of Pretty == 0 + jarp->InitArray(g); + return jarp; + } // endif b + strcpy(g->Message, "Unexpected EOF in array"); return NULL; } // end of ParseArray @@ -177,7 +207,7 @@ PJAR ParseArray(PGLOBAL g, int& i, STRG& src) /***********************************************************************/ /* Parse a JSON Object. */ /***********************************************************************/ -PJOB ParseObject(PGLOBAL g, int& i, STRG& src) +PJOB ParseObject(PGLOBAL g, int& i, STRG& src, bool *pty) { PSZ key; char *s = src.str; @@ -204,7 +234,7 @@ PJOB ParseObject(PGLOBAL g, int& i, STRG& src) break; case ':': if (level == 1) { - if (!(jpp->Val = ParseValue(g, ++i, src))) + if (!(jpp->Val = ParseValue(g, ++i, src, pty))) return NULL; level = 2; @@ -229,10 +259,11 @@ PJOB ParseObject(PGLOBAL g, int& i, STRG& src) } // endif level return jobp; - case ' ': + case '\n': + pty[0] = pty[1] = false; + case '\r': + case ' ': case '\t': - case '\n': - case '\r': break; default: sprintf(g->Message, "Unexpected character '%c' near %.*s", @@ -247,7 +278,7 @@ PJOB ParseObject(PGLOBAL g, int& i, STRG& src) /***********************************************************************/ /* Parse a JSON Value. */ /***********************************************************************/ -PJVAL ParseValue(PGLOBAL g, int& i, STRG& src) +PJVAL ParseValue(PGLOBAL g, int& i, STRG& src, bool *pty) { char *strval, *s = src.str; int n, len = src.len; @@ -255,10 +286,11 @@ PJVAL ParseValue(PGLOBAL g, int& i, STRG& src) for (; i < len; i++) switch (s[i]) { - case ' ': + case '\n': + pty[0] = pty[1] = false; + case '\r': + case ' ': case '\t': - case '\n': - case '\r': break; default: goto suite; @@ -267,12 +299,12 @@ PJVAL ParseValue(PGLOBAL g, int& i, STRG& src) suite: switch (s[i]) { case '[': - if (!(jvp->Jsp = ParseArray(g, ++i, src))) + if (!(jvp->Jsp = ParseArray(g, ++i, src, pty))) return NULL; break; case '{': - if (!(jvp->Jsp = ParseObject(g, ++i, src))) + if (!(jvp->Jsp = ParseObject(g, ++i, src, pty))) return NULL; break; @@ -319,7 +351,6 @@ PJVAL ParseValue(PGLOBAL g, int& i, STRG& src) }; // endswitch s[i] - jvp->Size = 1; return jvp; err: @@ -481,9 +512,9 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src) valp = AllocateValue(g, &dv, TYPE_DOUBLE, nd); } else { - int iv = strtol(buf, NULL, 10); + long long iv = strtoll(buf, NULL, 10); - valp = AllocateValue(g, &iv, TYPE_INT); + valp = AllocateValue(g, &iv, TYPE_BIGINT); } // endif has i--; // Unstack following character @@ -501,35 +532,44 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src) /***********************************************************************/ /* Serialize a JSON tree: */ /***********************************************************************/ -PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty) +PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty) { bool b = false, err = true; JOUT *jp; + FILE *fs = NULL; - g->Message[0] = 0; + g->Message[0] = 0; if (!jsp) { strcpy(g->Message, "Null json tree"); return NULL; - } else if (!fs) { + } else if (!fn) { // Serialize to a string jp = new(g) JOUTSTR(g); b = pretty == 1; - } else if (pretty == 2) { - // Serialize to a pretty file - jp = new(g) JOUTPRT(g, fs); - } else { - // Serialize to a flat file - jp = new(g) JOUTFILE(g, fs); - b = pretty == 1; - } // endif's + } else { + if (!(fs = fopen(fn, "wb"))) { + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "w", (int)errno, fn); + strcat(strcat(g->Message, ": "), strerror(errno)); + return g->Message; + } else if (pretty >= 2) { + // Serialize to a pretty file + jp = new(g)JOUTPRT(g, fs); + } else { + // Serialize to a flat file + b = true; + jp = new(g)JOUTFILE(g, fs, pretty); + } // endif's + + } // endif's switch (jsp->GetType()) { case TYPE_JAR: err = SerializeArray(jp, (PJAR)jsp, b); break; case TYPE_JOB: - err = (b && jp->WriteChr('\t')); + err = ((b && jp->Prty()) && jp->WriteChr('\t')); err |= SerializeObject(jp, (PJOB)jsp); break; case TYPE_JVAL: @@ -540,7 +580,7 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty) } // endswitch Type if (fs) { - fputc('\n', fs); + fputs(EL, fs); fclose(fs); return (err) ? g->Message : NULL; } else if (!err) { @@ -565,29 +605,40 @@ bool SerializeArray(JOUT *js, PJAR jarp, bool b) { bool first = true; + if (b) { + if (js->Prty()) { + if (js->WriteChr('[')) + return true; + else if (js->Prty() == 1 && (js->WriteStr(EL) || js->WriteChr('\t'))) + return true; - if (js->WriteChr('[')) - return true; - else if (b && (js->WriteStr(EL) || js->WriteChr('\t'))) - return true; + } // endif Prty + + } else if (js->WriteChr('[')) + return true; for (int i = 0; i < jarp->size(); i++) { if (first) first = false; - else if (js->WriteChr(',')) - return true; - else if (b && (js->WriteStr(EL) || js->WriteChr('\t'))) + else if ((!b || js->Prty()) && js->WriteChr(',')) return true; + else if (b) { + if (js->Prty() < 2 && js->WriteStr(EL)) + return true; + else if (js->Prty() == 1 && js->WriteChr('\t')) + return true; + + } // endif b if (SerializeValue(js, jarp->GetValue(i))) return true; } // endfor i - if (b && js->WriteStr(EL)) + if (b && js->Prty() == 1 && js->WriteStr(EL)) return true; - return js->WriteChr(']'); + return ((!b || js->Prty()) && js->WriteChr(']')); } // end of SerializeArray /***********************************************************************/ @@ -647,8 +698,8 @@ bool SerializeValue(JOUT *js, PJVAL jvp) } // endswitch Type -strcpy(js->g->Message, "Unrecognized value"); -return true; + strcpy(js->g->Message, "Unrecognized value"); + return true; } // end of SerializeValue /* -------------------------- Class JOUTSTR -------------------------- */ @@ -866,6 +917,20 @@ PJPR JOBJECT::AddPair(PGLOBAL g, PSZ key) return jpp; } // end of AddPair +/***********************************************************************/ +/* Return all keys as an array. */ +/***********************************************************************/ +PJAR JOBJECT::GetKeyList(PGLOBAL g) +{ + PJAR jarp = new(g) JARRAY(); + + for (PJPR jpp = First; jpp; jpp = jpp->Next) + jarp->AddValue(g, new(g) JVALUE(g, jpp->GetKey())); + + jarp->InitArray(g); + return jarp; +} // end of GetKeyList + /***********************************************************************/ /* Get the value corresponding to the given key. */ /***********************************************************************/ @@ -903,12 +968,30 @@ PSZ JOBJECT::GetText(PGLOBAL g, PSZ text) return text + n; } // end of GetValue; +/***********************************************************************/ +/* Merge two objects. */ +/***********************************************************************/ +bool JOBJECT::Merge(PGLOBAL g, PJSON jsp) +{ + if (jsp->GetType() != TYPE_JOB) { + strcpy(g->Message, "Second argument is not an object"); + return true; + } // endif Type + + PJOB jobp = (PJOB)jsp; + + for (PJPR jpp = jobp->First; jpp; jpp = jpp->Next) + SetValue(g, jpp->GetVal(), jpp->GetKey()); + + return false; +} // end of Marge; + /***********************************************************************/ /* Set or add a value corresponding to the given key. */ /***********************************************************************/ void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key) { - PJPR jp; + PJPR jp; for (jp = First; jp; jp = jp->Next) if (!strcmp(jp->Key, key)) { @@ -923,6 +1006,23 @@ void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key) } // end of SetValue +/***********************************************************************/ +/* Delete a value corresponding to the given key. */ +/***********************************************************************/ +void JOBJECT::DeleteKey(PSZ key) +{ + PJPR jp, *pjp = &First; + + for (jp = First; jp; jp = jp->Next) + if (!strcmp(jp->Key, key)) { + *pjp = jp->Next; + Size--; + break; + } else + pjp = &jp->Next; + +} // end of DeleteKey + /***********************************************************************/ /* True if void or if all members are nulls. */ /***********************************************************************/ @@ -943,23 +1043,25 @@ bool JOBJECT::IsNull(void) void JARRAY::InitArray(PGLOBAL g) { int i; - PJVAL jvp; + PJVAL jvp, *pjvp = &First; for (Size = 0, jvp = First; jvp; jvp = jvp->Next) if (!jvp->Del) Size++; - if (!Size) { - return; - } else if (Size > Alloc) { + if (Size > Alloc) { // No need to realloc after deleting values Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL)); Alloc = Size; } // endif Size for (i = 0, jvp = First; jvp; jvp = jvp->Next) - if (!jvp->Del) - Mvals[i++] = jvp; + if (!jvp->Del) { + Mvals[i++] = jvp; + pjvp = &jvp->Next; + Last = jvp; + } else + *pjvp = jvp->Next; } // end of InitArray @@ -975,31 +1077,64 @@ PJVAL JARRAY::GetValue(int i) } // end of GetValue /***********************************************************************/ -/* Add a Value to the Arrays Value list. */ +/* Add a Value to the Array Value list. */ /***********************************************************************/ -PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp) +PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp, int *x) { if (!jvp) jvp = new(g) JVALUE; - if (Last) - Last->Next = jvp; - else - First = jvp; + if (x) { + int i = 0, n = *x; + PJVAL jp, *jpp = &First; + + for (jp = First; jp && i < n; i++, jp = *(jpp = &jp->Next)); + + (*jpp) = jvp; + + if (!(jvp->Next = jp)) + Last = jvp; + + } else { + if (Last) + Last->Next = jvp; + else + First = jvp; + + Last = jvp; + } // endif x - Last = jvp; return jvp; } // end of AddValue /***********************************************************************/ -/* Add a Value to the Arrays Value list. */ +/* Merge two arrays. */ +/***********************************************************************/ +bool JARRAY::Merge(PGLOBAL g, PJSON jsp) +{ + if (jsp->GetType() != TYPE_JAR) { + strcpy(g->Message, "Second argument is not an array"); + return true; + } // endif Type + + PJAR arp = (PJAR)jsp; + + for (int i = 0; i < jsp->size(); i++) + AddValue(g, arp->GetValue(i)); + + InitArray(g); + return false; +} // end of Merge + +/***********************************************************************/ +/* Set the nth Value of the Array Value list. */ /***********************************************************************/ bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n) { int i = 0; PJVAL jp, *jpp = &First; - for (i = 0, jp = First; i < n; i++, jp = *(jpp = &jp->Next)) + for (jp = First; i < n; i++, jp = *(jpp = &jp->Next)) if (!jp) *jpp = jp = new(g) JVALUE; @@ -1048,6 +1183,17 @@ JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON() Del = false; } // end of JVALUE constructor +/***********************************************************************/ +/* Constructor for a given string. */ +/***********************************************************************/ +JVALUE::JVALUE(PGLOBAL g, PSZ strp) : JSON() +{ + Jsp = NULL; + Value = AllocateValue(g, strp, TYPE_STRING); + Next = NULL; + Del = false; +} // end of JVALUE constructor + /***********************************************************************/ /* Returns the type of the Value's value. */ /***********************************************************************/ @@ -1092,6 +1238,14 @@ int JVALUE::GetInteger(void) return (Value) ? Value->GetIntValue() : 0; } // end of GetInteger +/***********************************************************************/ +/* Return the Value's Big integer value. */ +/***********************************************************************/ +long long JVALUE::GetBigint(void) +{ + return (Value) ? Value->GetBigintValue() : 0; +} // end of GetBigint + /***********************************************************************/ /* Return the Value's Double value. */ /***********************************************************************/ @@ -1134,7 +1288,26 @@ PSZ JVALUE::GetText(PGLOBAL g, PSZ text) void JVALUE::SetInteger(PGLOBAL g, int n) { Value = AllocateValue(g, &n, TYPE_INT); -} // end of AddInteger + Jsp = NULL; +} // end of SetInteger + +/***********************************************************************/ +/* Set the Value's Boolean value as a tiny integer. */ +/***********************************************************************/ +void JVALUE::SetTiny(PGLOBAL g, char n) +{ + Value = AllocateValue(g, &n, TYPE_TINY); + Jsp = NULL; +} // end of SetInteger + +/***********************************************************************/ +/* Set the Value's value as the given big integer. */ +/***********************************************************************/ +void JVALUE::SetBigint(PGLOBAL g, long long ll) +{ + Value = AllocateValue(g, &ll, TYPE_BIGINT); + Jsp = NULL; +} // end of SetBigint /***********************************************************************/ /* Set the Value's value as the given DOUBLE. */ @@ -1142,15 +1315,17 @@ void JVALUE::SetInteger(PGLOBAL g, int n) void JVALUE::SetFloat(PGLOBAL g, double f) { Value = AllocateValue(g, &f, TYPE_DOUBLE, 6); -} // end of AddFloat + Jsp = NULL; +} // end of SetFloat /***********************************************************************/ /* Set the Value's value as the given string. */ /***********************************************************************/ -void JVALUE::SetString(PGLOBAL g, PSZ s) +void JVALUE::SetString(PGLOBAL g, PSZ s, short c) { - Value = AllocateValue(g, s, TYPE_STRING); -} // end of AddFloat + Value = AllocateValue(g, s, TYPE_STRING, c); + Jsp = NULL; +} // end of SetString /***********************************************************************/ /* True when its JSON or normal value is null. */ diff --git a/storage/connect/json.h b/storage/connect/json.h index da45157f124..d2ebc25123d 100644 --- a/storage/connect/json.h +++ b/storage/connect/json.h @@ -1,5 +1,5 @@ /**************** json H Declares Source Code File (.H) ****************/ -/* Name: json.h Version 1.1 */ +/* Name: json.h Version 1.2 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */ /* */ @@ -16,8 +16,9 @@ enum JTYP {TYPE_STRG = 1, TYPE_DBL = 2, TYPE_BOOL = 4, - TYPE_INTG = 7, - TYPE_JSON = 12, + TYPE_BINT = 5, + TYPE_INTG = 7, + TYPE_JSON = 12, TYPE_JAR, TYPE_JOB, TYPE_JVAL}; @@ -40,13 +41,13 @@ typedef struct { int len; } STRG, *PSG; -PJSON ParseJson(PGLOBAL g, char *s, int n, int prty, bool *b = NULL); -PJAR ParseArray(PGLOBAL g, int& i, STRG& src); -PJOB ParseObject(PGLOBAL g, int& i, STRG& src); -PJVAL ParseValue(PGLOBAL g, int& i, STRG& src); +PJSON ParseJson(PGLOBAL g, char *s, int n, int *prty = NULL, bool *b = NULL); +PJAR ParseArray(PGLOBAL g, int& i, STRG& src, bool *pty); +PJOB ParseObject(PGLOBAL g, int& i, STRG& src, bool *pty); +PJVAL ParseValue(PGLOBAL g, int& i, STRG& src, bool *pty); char *ParseString(PGLOBAL g, int& i, STRG& src); PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src); -PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty); +PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty); bool SerializeArray(JOUT *js, PJAR jarp, bool b); bool SerializeObject(JOUT *js, PJOB jobp); bool SerializeValue(JOUT *js, PJVAL jvp); @@ -56,14 +57,16 @@ bool SerializeValue(JOUT *js, PJVAL jvp); /***********************************************************************/ class JOUT : public BLOCK { public: - JOUT(PGLOBAL gp) : BLOCK() {g = gp;} + JOUT(PGLOBAL gp) : BLOCK() {g = gp; Pretty = 3;} virtual bool WriteStr(const char *s) = 0; virtual bool WriteChr(const char c) = 0; virtual bool Escape(const char *s) = 0; + int Prty(void) {return Pretty;} // Member PGLOBAL g; + int Pretty; }; // end of class JOUT /***********************************************************************/ @@ -88,7 +91,7 @@ class JOUTSTR : public JOUT { /***********************************************************************/ class JOUTFILE : public JOUT { public: - JOUTFILE(PGLOBAL g, FILE *str) : JOUT(g) {Stream = str;} + JOUTFILE(PGLOBAL g, FILE *str, int pty) : JOUT(g) {Stream = str; Pretty = pty;} virtual bool WriteStr(const char *s); virtual bool WriteChr(const char c); @@ -103,7 +106,7 @@ class JOUTFILE : public JOUT { /***********************************************************************/ class JOUTPRT : public JOUTFILE { public: - JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str) {M = 0; B = false;} + JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str, 2) {M = 0; B = false;} virtual bool WriteStr(const char *s); virtual bool WriteChr(const char c); @@ -118,7 +121,8 @@ class JOUTPRT : public JOUTFILE { /***********************************************************************/ class JPAIR : public BLOCK { friend class JOBJECT; - friend PJOB ParseObject(PGLOBAL, int&, STRG&); + friend class JSNX; + friend PJOB ParseObject(PGLOBAL, int&, STRG&, bool*); friend bool SerializeObject(JOUT *, PJOB); public: JPAIR(PSZ key) : BLOCK() {Key = key; Val = NULL; Next = NULL;} @@ -145,28 +149,32 @@ class JSON : public BLOCK { virtual JTYP GetType(void) {return TYPE_JSON;} virtual JTYP GetValType(void) {X return TYPE_JSON;} virtual void InitArray(PGLOBAL g) {X} - virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL) {X return NULL;} + virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL, int *x = NULL) {X return NULL;} virtual PJPR AddPair(PGLOBAL g, PSZ key) {X return NULL;} - virtual PJVAL GetValue(const char *key) {X return NULL;} + virtual PJAR GetKeyList(PGLOBAL g) {X return NULL;} + virtual PJVAL GetValue(const char *key) {X return NULL;} virtual PJOB GetObject(void) {return NULL;} virtual PJAR GetArray(void) {return NULL;} - virtual PJVAL GetValue(int i) {X return NULL;} + virtual PJVAL GetValue(int i) {X return NULL;} virtual PVAL GetValue(void) {X return NULL;} - virtual PJSON GetJson(void) {X return NULL;} + virtual PJSON GetJsp(void) { X return NULL; } + virtual PJSON GetJson(void) { X return NULL; } virtual PJPR GetFirst(void) {X return NULL;} virtual int GetInteger(void) {X return 0;} virtual double GetFloat() {X return 0.0;} virtual PSZ GetString() {X return NULL;} virtual PSZ GetText(PGLOBAL g, PSZ text) {X return NULL;} - virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i) {X return true;} + virtual bool Merge(PGLOBAL g, PJSON jsp) { X return true; } + virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i) { X return true; } virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key) {X} virtual void SetValue(PVAL valp) {X} virtual void SetValue(PJSON jsp) {X} - virtual void SetString(PGLOBAL g, PSZ s) {X} + virtual void SetString(PGLOBAL g, PSZ s, short c) {X} virtual void SetInteger(PGLOBAL g, int n) {X} virtual void SetFloat(PGLOBAL g, double f) {X} - virtual bool DeleteValue(int i) {X return true;} - virtual bool IsNull(void) {X return true;} + virtual void DeleteKey(char *k) {X} + virtual bool DeleteValue(int i) {X return true;} + virtual bool IsNull(void) {X return true;} protected: int Size; @@ -176,8 +184,9 @@ class JSON : public BLOCK { /* Class JOBJECT: contains a list of value pairs. */ /***********************************************************************/ class JOBJECT : public JSON { - friend PJOB ParseObject(PGLOBAL, int&, STRG&); + friend PJOB ParseObject(PGLOBAL, int&, STRG&, bool*); friend bool SerializeObject(JOUT *, PJOB); + friend class JSNX; public: JOBJECT(void) : JSON() {First = Last = NULL;} @@ -189,9 +198,12 @@ class JOBJECT : public JSON { virtual PJPR AddPair(PGLOBAL g, PSZ key); virtual PJOB GetObject(void) {return this;} virtual PJVAL GetValue(const char* key); - virtual PSZ GetText(PGLOBAL g, PSZ text); - virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key); - virtual bool IsNull(void); + virtual PJAR GetKeyList(PGLOBAL g); + virtual PSZ GetText(PGLOBAL g, PSZ text); + virtual bool Merge(PGLOBAL g, PJSON jsp); + virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key); + virtual void DeleteKey(char *k); + virtual bool IsNull(void); protected: PJPR First; @@ -202,7 +214,7 @@ class JOBJECT : public JSON { /* Class JARRAY. */ /***********************************************************************/ class JARRAY : public JSON { - friend PJAR ParseArray(PGLOBAL, int&, STRG&); + friend PJAR ParseArray(PGLOBAL, int&, STRG&, bool*); public: JARRAY(void) : JSON() {Alloc = 0; First = Last = NULL; Mvals = NULL;} @@ -211,10 +223,11 @@ class JARRAY : public JSON { virtual void Clear(void) {First = Last = NULL; Size = 0;} virtual JTYP GetType(void) {return TYPE_JAR;} virtual PJAR GetArray(void) {return this;} - virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL); + virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL, int *x = NULL); virtual void InitArray(PGLOBAL g); virtual PJVAL GetValue(int i); - virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i); + virtual bool Merge(PGLOBAL g, PJSON jsp); + virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i); virtual bool DeleteValue(int n); virtual bool IsNull(void); @@ -231,7 +244,8 @@ class JARRAY : public JSON { /***********************************************************************/ class JVALUE : public JSON { friend class JARRAY; - friend PJVAL ParseValue(PGLOBAL, int&, STRG&); + friend class JSNX; + friend PJVAL ParseValue(PGLOBAL, int&, STRG&, bool*); friend bool SerializeValue(JOUT *, PJVAL); public: JVALUE(void) : JSON() @@ -239,6 +253,7 @@ class JVALUE : public JSON { JVALUE(PJSON jsp) : JSON() {Jsp = jsp; Value = NULL; Next = NULL; Del = false;} JVALUE(PGLOBAL g, PVAL valp); + JVALUE(PGLOBAL g, PSZ strp); using JSON::GetValue; using JSON::SetValue; @@ -249,17 +264,21 @@ class JVALUE : public JSON { virtual PJOB GetObject(void); virtual PJAR GetArray(void); virtual PVAL GetValue(void) {return Value;} - virtual PJSON GetJson(void) {return (Jsp ? Jsp : this);} - virtual int GetInteger(void); - virtual double GetFloat(void); + virtual PJSON GetJsp(void) {return Jsp;} + virtual PJSON GetJson(void) { return (Jsp ? Jsp : this); } + virtual int GetInteger(void); + virtual long long GetBigint(void); + virtual double GetFloat(void); virtual PSZ GetString(void); virtual PSZ GetText(PGLOBAL g, PSZ text); - virtual void SetValue(PVAL valp) {Value = valp;} - virtual void SetValue(PJSON jsp) {Jsp = jsp;} - virtual void SetString(PGLOBAL g, PSZ s); + virtual void SetValue(PVAL valp) {Value = valp; Jsp = NULL;} + virtual void SetValue(PJSON jsp) {Jsp = jsp; Value = NULL;} + virtual void SetString(PGLOBAL g, PSZ s, short c = 0); virtual void SetInteger(PGLOBAL g, int n); - virtual void SetFloat(PGLOBAL g, double f); - virtual bool IsNull(void); + virtual void SetBigint(PGLOBAL g, longlong ll); + virtual void SetFloat(PGLOBAL g, double f); + virtual void SetTiny(PGLOBAL g, char f); + virtual bool IsNull(void); protected: PJSON Jsp; // To the json value diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index 2b7d3601c68..0b40f1d18cf 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -1,69 +1,886 @@ -/************* jsonudf C++ Program Source Code File (.CPP) *************/ -/* PROGRAM NAME: jsonudf Version 1.0 */ -/* (C) Copyright to the author Olivier BERTRAND 2015 */ -/* This program are the JSON User Defined Functions . */ -/***********************************************************************/ +/****************** jsonudf C++ Program Source Code File (.CPP) ******************/ +/* PROGRAM NAME: jsonudf Version 1.3 */ +/* (C) Copyright to the author Olivier BERTRAND 2015 */ +/* This program are the JSON User Defined Functions . */ +/*********************************************************************************/ -/***********************************************************************/ -/* Include relevant sections of the MariaDB header file. */ -/***********************************************************************/ +/*********************************************************************************/ +/* Include relevant sections of the MariaDB header file. */ +/*********************************************************************************/ #include #include #include #include +#include -#include "global.h" -#include "plgdbsem.h" -#include "json.h" +#include "jsonudf.h" -#define MEMFIX 512 -#define UDF_EXEC_ARGS \ - UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char* +#if defined(UNIX) || defined(UNIV_LINUX) +#define _O_RDONLY O_RDONLY +#endif + +#define MEMFIX 4096 +#if defined(connect_EXPORTS) +#define PUSH_WARNING(M) push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, M) +#else +#define PUSH_WARNING(M) htrc(M) +#endif +#define M 7 uint GetJsonGrpSize(void); +static int IsJson(UDF_ARGS *args, uint i); +static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i); -extern "C" { -DllExport my_bool Json_Value_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Value(UDF_EXEC_ARGS); -DllExport void Json_Value_deinit(UDF_INIT*); +/* ----------------------------------- JSNX ------------------------------------ */ -DllExport my_bool Json_Array_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Array(UDF_EXEC_ARGS); -DllExport void Json_Array_deinit(UDF_INIT*); +/*********************************************************************************/ +/* JSNX public constructor. */ +/*********************************************************************************/ +JSNX::JSNX(PGLOBAL g, PJSON row, int type, int len, int prec) +{ + Row = row; + Jvalp = NULL; + Jpnp = NULL; + Jp = NULL; + Nodes = NULL; + Value = AllocateValue(g, type, len, prec); + MulVal = NULL; + Jpath = NULL; + Buf_Type = type; + Long = len; + Prec = prec; + Nod = 0; + Xnod = -1; + K = 0; + I = -1; + Imax = 9; + B = 0; + Xpd = false; + Parsed = false; + Found = false; +} // end of JSNX constructor -DllExport my_bool Json_Array_Add_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Array_Add(UDF_EXEC_ARGS); -DllExport void Json_Array_Add_deinit(UDF_INIT*); +/*********************************************************************************/ +/* SetJpath: set and parse the json path. */ +/*********************************************************************************/ +my_bool JSNX::SetJpath(PGLOBAL g, char *path, my_bool jb) +{ + // Check Value was allocated + if (!Value) + return true; -DllExport my_bool Json_Array_Delete_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Array_Delete(UDF_EXEC_ARGS); -DllExport void Json_Array_Delete_deinit(UDF_INIT*); + Value->SetNullable(true); -DllExport my_bool Json_Object_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Object(UDF_EXEC_ARGS); -DllExport void Json_Object_deinit(UDF_INIT*); + if (jb) { + // Path must return a Json item + size_t n = strlen(path); -DllExport my_bool Json_Object_Nonull_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Object_Nonull(UDF_EXEC_ARGS); -DllExport void Json_Object_Nonull_deinit(UDF_INIT*); + if (!n || path[n - 1] != '*') { + Jpath = (char*)PlugSubAlloc(g, NULL, n + 3); + strcat(strcpy(Jpath, path), (n) ? ":*" : "*"); + } else + Jpath = path; -DllExport my_bool Json_Array_Grp_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport void Json_Array_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *); -DllExport char *Json_Array_Grp(UDF_EXEC_ARGS); -DllExport void Json_Array_Grp_clear(UDF_INIT *, char *, char *); -DllExport void Json_Array_Grp_deinit(UDF_INIT*); + } else + Jpath = path; -DllExport my_bool Json_Object_Grp_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport void Json_Object_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *); -DllExport char *Json_Object_Grp(UDF_EXEC_ARGS); -DllExport void Json_Object_Grp_clear(UDF_INIT *, char *, char *); -DllExport void Json_Object_Grp_deinit(UDF_INIT*); -} // extern "C" + // Parse the json path + return ParseJpath(g); +} // end of SetJpath -/***********************************************************************/ -/* Allocate and initialise the memory area. */ -/***********************************************************************/ -static my_bool JsonInit(UDF_INIT *initid, char *message, +/*********************************************************************************/ +/* Analyse array processing options. */ +/*********************************************************************************/ +my_bool JSNX::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm) +{ + int n = (int)strlen(p); + my_bool dg = true, b = false; + PJNODE jnp = &Nodes[i]; + + if (*p) { + if (p[--n] == ']') { + p[n--] = 0; + p++; + } else { + // Wrong array specification + sprintf(g->Message, "Invalid array specification %s", p); + return true; + } // endif p + + } else + b = true; + + // To check whether a numeric Rank was specified + for (int k = 0; dg && p[k]; k++) + dg = isdigit(p[k]) > 0; + + if (!n) { + // Default specifications + if (jnp->Op != OP_EXP) { + if (b) { + // Return 1st value (B is the index base) + jnp->Rank = B; + jnp->Op = OP_LE; + } else if (!Value->IsTypeNum()) { + jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING); + jnp->Op = OP_CNC; + } else + jnp->Op = OP_ADD; + + } // endif OP + + } else if (dg) { + // Return nth value + jnp->Rank = atoi(p) - B; + jnp->Op = OP_EQ; + } else if (n == 1) { + // Set the Op value; + switch (*p) { + case '+': jnp->Op = OP_ADD; break; + case '*': jnp->Op = OP_MULT; break; + case '>': jnp->Op = OP_MAX; break; + case '<': jnp->Op = OP_MIN; break; + case '!': jnp->Op = OP_SEP; break; // Average + case '#': jnp->Op = OP_NUM; break; + case 'x': + case 'X': // Expand this array + strcpy(g->Message, "Expand not supported by this function"); + return true; + default: + sprintf(g->Message, "Invalid function specification %c", *p); + return true; + } // endswitch *p + + } else if (*p == '"' && p[n - 1] == '"') { + // This is a concat specification + jnp->Op = OP_CNC; + + if (n > 2) { + // Set concat intermediate string + p[n - 1] = 0; + jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING); + } // endif n + + } else { + strcpy(g->Message, "Wrong array specification"); + return true; + } // endif's + + // For calculated arrays, a local Value must be used + switch (jnp->Op) { + case OP_NUM: + jnp->Valp = AllocateValue(g, TYPE_INT); + break; + case OP_ADD: + case OP_MULT: + case OP_SEP: + if (!IsTypeChar(Buf_Type)) + jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision()); + else + jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2); + + break; + case OP_MIN: + case OP_MAX: + jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision()); + break; + case OP_CNC: + if (IsTypeChar(Buf_Type)) + jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision()); + else + jnp->Valp = AllocateValue(g, TYPE_STRING, 512); + + break; + default: + break; + } // endswitch Op + + if (jnp->Valp) + MulVal = AllocateValue(g, jnp->Valp); + + return false; +} // end of SetArrayOptions + +/*********************************************************************************/ +/* Parse the eventual passed Jpath information. */ +/* This information can be specified in the Fieldfmt column option when */ +/* creating the table. It permits to indicate the position of the node */ +/* corresponding to that column. */ +/*********************************************************************************/ +my_bool JSNX::ParseJpath(PGLOBAL g) +{ + char *p, *p2 = NULL, *pbuf = NULL; + int i; + my_bool mul = false; + + if (Parsed) + return false; // Already done + else if (!Jpath) + // Jpath = Name; + return true; + + pbuf = PlugDup(g, Jpath); + + // The Jpath must be analyzed + for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++) + Nod++; // One path node found + + Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE)); + memset(Nodes, 0, (Nod)* sizeof(JNODE)); + + // Analyze the Jpath for this column + for (i = 0, p = pbuf; i < Nod; i++, p = (p2 ? p2 + 1 : p + strlen(p))) { + if ((p2 = strchr(p, ':'))) + *p2 = 0; + + // Jpath must be explicit + if (*p == 0 || *p == '[') { + // Analyse intermediate array processing + if (SetArrayOptions(g, p, i, Nodes[i-1].Key)) + return true; + + } else if (*p == '*') { + // Return JSON + Nodes[i].Op = OP_XX; + } else { + Nodes[i].Key = p; + Nodes[i].Op = OP_EXIST; + } // endif's + + } // endfor i, p + + MulVal = AllocateValue(g, Value); + Parsed = true; + return false; +} // end of ParseJpath + +/*********************************************************************************/ +/* MakeJson: Serialize the json item and set value to it. */ +/*********************************************************************************/ +PVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp) +{ + if (Value->IsTypeNum()) { + strcpy(g->Message, "Cannot make Json for a numeric value"); + Value->Reset(); + } else + Value->SetValue_psz(Serialize(g, jsp, NULL, 0)); + + return Value; +} // end of MakeJson + +/*********************************************************************************/ +/* SetValue: Set a value from a JVALUE contains. */ +/*********************************************************************************/ +void JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n) +{ + if (val) { + switch (val->GetValType()) { + case TYPE_STRG: + case TYPE_INTG: + case TYPE_BINT: + case TYPE_DBL: + vp->SetValue_pval(val->GetValue()); + break; + case TYPE_BOOL: + if (vp->IsTypeNum()) + vp->SetValue(val->GetInteger() ? 1 : 0); + else + vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false")); + + break; + case TYPE_JAR: + SetJsonValue(g, vp, val->GetArray()->GetValue(0), n); + break; + case TYPE_JOB: + // if (!vp->IsTypeNum() || !Strict) { + vp->SetValue_psz(val->GetObject()->GetText(g, NULL)); + break; + // } // endif Type + + default: + vp->Reset(); + } // endswitch Type + + } else { + vp->SetNull(true); + vp->Reset(); + } // endif val + +} // end of SetJsonValue + +/*********************************************************************************/ +/* GetJson: */ +/*********************************************************************************/ +PJVAL JSNX::GetJson(PGLOBAL g) +{ + return GetValue(g, Row, 0); +} // end of GetJson + +/*********************************************************************************/ +/* ReadValue: */ +/*********************************************************************************/ +void JSNX::ReadValue(PGLOBAL g) +{ + Value->SetValue_pval(GetColumnValue(g, Row, 0)); +} // end of ReadValue + +/*********************************************************************************/ +/* GetColumnValue: */ +/*********************************************************************************/ +PVAL JSNX::GetColumnValue(PGLOBAL g, PJSON row, int i) +{ + int n = Nod - 1; + PJVAL val = NULL; + + val = GetValue(g, row, i); + SetJsonValue(g, Value, val, n); + return Value; +} // end of GetColumnValue + +/*********************************************************************************/ +/* GetValue: */ +/*********************************************************************************/ +PJVAL JSNX::GetValue(PGLOBAL g, PJSON row, int i, my_bool b) +{ +//int n = Nod - 1; + my_bool expd = false; + PJAR arp; + PJVAL val = NULL; + + for (; i < Nod && row; i++) { + if (Nodes[i].Op == OP_NUM) { + Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1); + val = new(g) JVALUE(g, Value); + return val; + } else if (Nodes[i].Op == OP_XX) { + if (b) + return new(g)JVALUE(g, MakeJson(g, row)); + else + return new(g)JVALUE(row); + + } else switch (row->GetType()) { + case TYPE_JOB: + if (!Nodes[i].Key) { + // Expected Array was not there + if (Nodes[i].Op == OP_LE) { + if (i < Nod-1) + continue; + else + val = new(g)JVALUE(row); + + } else { + strcpy(g->Message, "Unexpected object"); + val = NULL; + } //endif Op + + } else + val = ((PJOB)row)->GetValue(Nodes[i].Key); + + break; + case TYPE_JAR: + arp = (PJAR)row; + + if (!Nodes[i].Key) { + if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE) + val = arp->GetValue(Nodes[i].Rank); + else if (Nodes[i].Op == OP_EXP) + return (PJVAL)ExpandArray(g, arp, i); + else + return new(g) JVALUE(g, CalculateArray(g, arp, i)); + + } else if (i < Nod-1) { + strcpy(g->Message, "Unexpected array"); + val = NULL; // Not an expected array + } else + val = arp->GetValue(0); + + break; + case TYPE_JVAL: + val = (PJVAL)row; + break; + default: + sprintf(g->Message, "Invalid row JSON type %d", row->GetType()); + val = NULL; + } // endswitch Type + + if (i < Nod-1) + row = (val) ? val->GetJson() : NULL; + + } // endfor i + + // SetJsonValue(g, Value, val, n); + return val; +} // end of GetValue + +/*********************************************************************************/ +/* ExpandArray: */ +/*********************************************************************************/ +PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n) +{ + strcpy(g->Message, "Expand cannot be done by this function"); + return NULL; +} // end of ExpandArray + +/*********************************************************************************/ +/* CalculateArray: */ +/*********************************************************************************/ +PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n) +{ +//int i, ars, nv = 0, nextsame = Tjp->NextSame; + int i, ars, nv = 0, nextsame = 0; + my_bool err; + OPVAL op = Nodes[n].Op; + PVAL val[2], vp = Nodes[n].Valp; + PJVAL jvrp, jvp; + JVALUE jval; + + vp->Reset(); +//ars = MY_MIN(Tjp->Limit, arp->size()); + ars = arp->size(); + + for (i = 0; i < ars; i++) { + jvrp = arp->GetValue(i); + +// do { + if (n < Nod - 1 && jvrp->GetJson()) { +// Tjp->NextSame = nextsame; + jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1)); + jvp = &jval; + } else + jvp = jvrp; + + if (!nv++) { + SetJsonValue(g, vp, jvp, n); + continue; + } else + SetJsonValue(g, MulVal, jvp, n); + + if (!MulVal->IsZero()) { + switch (op) { + case OP_CNC: + if (Nodes[n].CncVal) { + val[0] = Nodes[n].CncVal; + err = vp->Compute(g, val, 1, op); + } // endif CncVal + + val[0] = MulVal; + err = vp->Compute(g, val, 1, op); + break; + // case OP_NUM: + case OP_SEP: + val[0] = Nodes[n].Valp; + val[1] = MulVal; + err = vp->Compute(g, val, 2, OP_ADD); + break; + default: + val[0] = Nodes[n].Valp; + val[1] = MulVal; + err = vp->Compute(g, val, 2, op); + } // endswitch Op + + if (err) + vp->Reset(); + + } // endif Zero + +// } while (Tjp->NextSame > nextsame); + + } // endfor i + + if (op == OP_SEP) { + // Calculate average + MulVal->SetValue(nv); + val[0] = vp; + val[1] = MulVal; + + if (vp->Compute(g, val, 2, OP_DIV)) + vp->Reset(); + + } // endif Op + +//Tjp->NextSame = nextsame; + return vp; +} // end of CalculateArray + +/*********************************************************************************/ +/* Locate a value in a JSON tree: */ +/*********************************************************************************/ +PSZ JSNX::Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k) +{ + my_bool b = false, err = true; + + g->Message[0] = 0; + + if (!jsp) { + strcpy(g->Message, "Null json tree"); + return NULL; + } // endif jsp + + // Write to the path string + Jp = new(g) JOUTSTR(g); + Jvalp = jvp; + K = k; + + switch (jsp->GetType()) { + case TYPE_JAR: + err = LocateArray((PJAR)jsp); + break; + case TYPE_JOB: + err = LocateObject((PJOB)jsp); + break; + case TYPE_JVAL: + err = LocateValue((PJVAL)jsp); + break; + default: + err = true; + } // endswitch Type + + if (err) { + if (!g->Message[0]) + strcpy(g->Message, "Invalid json tree"); + + } else if (Found) { + Jp->WriteChr('\0'); + PlugSubAlloc(g, NULL, Jp->N); + return Jp->Strp; + } // endif's + + return NULL; +} // end of Locate + +/*********************************************************************************/ +/* Locate in a JSON Array. */ +/*********************************************************************************/ +my_bool JSNX::LocateArray(PJAR jarp) +{ + char s[16]; + size_t m = Jp->N; + + for (int i = 0; i < jarp->size() && !Found; i++) { + Jp->N = m; + sprintf(s, "[%d]", i + B); + + if (Jp->WriteStr(s)) + return true; + + if (LocateValue(jarp->GetValue(i))) + return true; + + } // endfor i + + return false; +} // end of LocateArray + +/*********************************************************************************/ +/* Locate in a JSON Object. */ +/*********************************************************************************/ +my_bool JSNX::LocateObject(PJOB jobp) +{ + size_t m = Jp->N; + + for (PJPR pair = jobp->First; pair && !Found; pair = pair->Next) { + Jp->N = m; + + if (Jp->WriteStr(pair->Key)) + return true; + + if (LocateValue(pair->Val)) + return true; + + } // endfor i + + return false; +} // end of LocateObject + +/*********************************************************************************/ +/* Locate a JSON Value. */ +/*********************************************************************************/ +my_bool JSNX::LocateValue(PJVAL jvp) +{ + if (CompareTree(Jvalp, jvp)) { + Found = (--K == 0); + } else if (jvp->GetArray()) { + if (Jp->WriteChr(':')) + return true; + + return LocateArray(jvp->GetArray()); + } else if (jvp->GetObject()) { + if (Jp->WriteChr(':')) + return true; + + return LocateObject(jvp->GetObject()); + } // endif's + + return false; +} // end of LocateValue + +/*********************************************************************************/ +/* Locate all occurrences of a value in a JSON tree: */ +/*********************************************************************************/ +PSZ JSNX::LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx) +{ + my_bool b = false, err = true; + PJPN jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx); + + memset(jnp, 0, sizeof(JPN) * mx); + g->Message[0] = 0; + + if (!jsp) { + strcpy(g->Message, "Null json tree"); + return NULL; + } // endif jsp + + // Write to the path string + Jp = new(g)JOUTSTR(g); + Jvalp = jvp; + Imax = mx - 1; + Jpnp = jnp; + Jp->WriteChr('['); + + switch (jsp->GetType()) { + case TYPE_JAR: + err = LocateArrayAll((PJAR)jsp); + break; + case TYPE_JOB: + err = LocateObjectAll((PJOB)jsp); + break; + case TYPE_JVAL: + err = LocateValueAll((PJVAL)jsp); + break; + default: + err = true; + } // endswitch Type + + if (err) { + if (!g->Message[0]) + strcpy(g->Message, "Invalid json tree"); + + } else { + if (Jp->N > 1) + Jp->N--; + + Jp->WriteChr(']'); + Jp->WriteChr('\0'); + PlugSubAlloc(g, NULL, Jp->N); + return Jp->Strp; + } // endif's + + return NULL; +} // end of LocateAll + +/*********************************************************************************/ +/* Locate in a JSON Array. */ +/*********************************************************************************/ +my_bool JSNX::LocateArrayAll(PJAR jarp) +{ + if (I < Imax) { + Jpnp[++I].Type = TYPE_JAR; + + for (int i = 0; i < jarp->size(); i++) { + Jpnp[I].N = i; + + if (LocateValueAll(jarp->GetValue(i))) + return true; + + } // endfor i + + I--; + } // endif I + + return false; +} // end of LocateArrayAll + +/*********************************************************************************/ +/* Locate in a JSON Object. */ +/*********************************************************************************/ +my_bool JSNX::LocateObjectAll(PJOB jobp) +{ + if (I < Imax) { + Jpnp[++I].Type = TYPE_JOB; + + for (PJPR pair = jobp->First; pair; pair = pair->Next) { + Jpnp[I].Key = pair->Key; + + if (LocateValueAll(pair->Val)) + return true; + + } // endfor i + + I--; + } // endif I + + return false; +} // end of LocateObjectAll + +/*********************************************************************************/ +/* Locate a JSON Value. */ +/*********************************************************************************/ +my_bool JSNX::LocateValueAll(PJVAL jvp) +{ + if (CompareTree(Jvalp, jvp)) + return AddPath(); + else if (jvp->GetArray()) + return LocateArrayAll(jvp->GetArray()); + else if (jvp->GetObject()) + return LocateObjectAll(jvp->GetObject()); + + return false; +} // end of LocateValueAll + +/*********************************************************************************/ +/* Compare two JSON trees. */ +/*********************************************************************************/ +my_bool JSNX::CompareTree(PJSON jp1, PJSON jp2) +{ + if (!jp1 || !jp2 || jp1->GetType() != jp2->GetType() + || jp1->size() != jp2->size()) + return false; + + my_bool found = true; + + if (jp1->GetType() == TYPE_JVAL) { + PVAL v1 = jp1->GetValue(), v2 = jp2->GetValue(); + + if (v1 && v2) { + if (v1->GetType() == v2->GetType()) + found = !v1->CompareValue(v2); + else + found = false; + + } else + found = CompareTree(jp1->GetJsp(), jp2->GetJsp()); + + } else if (jp1->GetType() == TYPE_JAR) { + for (int i = 0; found && i < jp1->size(); i++) + found = (CompareTree(jp1->GetValue(i), jp2->GetValue(i))); + + } else if (jp1->GetType() == TYPE_JOB) { + PJPR p1 = jp1->GetFirst(), p2 = jp2->GetFirst(); + + for (; found && p1 && p2; p1 = p1->Next, p2 = p2->Next) + found = CompareTree(p1->Val, p2->Val); + + } else + found = false; + + return found; +} // end of CompareTree + +/*********************************************************************************/ +/* Add the found path to the list. */ +/*********************************************************************************/ +my_bool JSNX::AddPath(void) +{ + char s[16]; + my_bool b = false; + + if (Jp->WriteChr('"')) + return true; + + for (int i = 0; i <= I; i++) { + if (b) { + if (Jp->WriteChr(':')) return true; + } else + b = true; + + if (Jpnp[i].Type == TYPE_JAR) { + sprintf(s, "[%d]", Jpnp[i].N + B); + + if (Jp->WriteStr(s)) + return true; + + } else if (Jp->WriteStr(Jpnp[i].Key)) + return true; + + } // endfor i + + if (Jp->WriteStr("\",")) + return true; + + return false; +} // end of AddPath + +/* --------------------------------- JSON UDF ---------------------------------- */ + +#define BMX (_MAX_PATH - 1) +typedef struct BSON *PBSON; + +/*********************************************************************************/ +/* Structure used to return binary json. */ +/*********************************************************************************/ +struct BSON { + char Msg[_MAX_PATH]; + char *Filename; + int Pretty; + ulong Reslen; + my_bool Changed; + PJSON Top; + PJSON Jsp; + PBSON Bsp; +}; // end of struct BSON + +/*********************************************************************************/ +/* Allocate and initialize a BSON structure. */ +/*********************************************************************************/ +static PBSON JbinAlloc(PGLOBAL g, UDF_ARGS *args, ulong len, PJSON jsp) +{ + PBSON bsp = (PBSON)PlugSubAlloc(g, NULL, sizeof(BSON)); + + strcpy(bsp->Msg, "Binary Json"); + bsp->Msg[BMX] = 0; + bsp->Filename = NULL; + bsp->Pretty = 2; + bsp->Reslen = len; + bsp->Changed = false; + bsp->Top = bsp->Jsp = jsp; + bsp->Bsp = (IsJson(args, 0) == 3) ? (PBSON)args->args[0] : NULL; + return bsp; +} /* end of JbinAlloc */ + +/*********************************************************************************/ +/* Set the BSON chain as changed. */ +/*********************************************************************************/ +static void SetChanged(PBSON bsp) +{ + if (bsp->Bsp) + SetChanged(bsp->Bsp); + + bsp->Changed = true; +} /* end of SetChanged */ + +/*********************************************************************************/ +/* Program for SubSet re-initialization of the memory pool. */ +/*********************************************************************************/ +static my_bool JsonSubSet(PGLOBAL g) +{ + PPOOLHEADER pph = (PPOOLHEADER)g->Sarea; + + pph->To_Free = (OFFSET)((g->Createas) ? g->Createas : sizeof(POOLHEADER)); + pph->FreeBlk = g->Sarea_Size - pph->To_Free; + return FALSE; +} /* end of JsonSubSet */ + +/*********************************************************************************/ +/* Program for saving the status of the memory pools. */ +/*********************************************************************************/ +inline void JsonMemSave(PGLOBAL g) +{ + g->Createas = (int)((PPOOLHEADER)g->Sarea)->To_Free; +} /* end of JsonMemSave */ + +/*********************************************************************************/ +/* Program for freeing the memory pools. */ +/*********************************************************************************/ +inline void JsonFreeMem(PGLOBAL g) +{ + PlugExit(g); +} /* end of JsonFreeMem */ + +/*********************************************************************************/ +/* Allocate and initialise the memory area. */ +/*********************************************************************************/ +static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args, + char *message, my_bool mbn, unsigned long reslen, unsigned long memlen) { PGLOBAL g = PlugInit(NULL, memlen); @@ -72,37 +889,217 @@ static my_bool JsonInit(UDF_INIT *initid, char *message, strcpy(message, "Allocation error"); return true; } else if (g->Sarea_Size == 0) { - strcpy(message, g->Message); - PlugExit(g); - return true; - } else - initid->ptr = (char*)g; + strcpy(message, g->Message); + PlugExit(g); + return true; + } // endif g - initid->maybe_null = false; + g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0; + initid->maybe_null = mbn; initid->max_length = reslen; - return false; -} // end of Json_Object_init + initid->ptr = (char*)g; + return false; +} // end of JsonInit -/***********************************************************************/ -/* Returns true if the argument is a JSON string. */ -/***********************************************************************/ -static my_bool IsJson(UDF_ARGS *args, int i) +/*********************************************************************************/ +/* Check if a path was specified and set jvp according to it. */ +/*********************************************************************************/ +static my_bool CheckPath(PGLOBAL g, UDF_ARGS *args, PJSON jsp, PJVAL& jvp, int n) { - return (args->arg_type[i] == STRING_RESULT && - !strnicmp(args->attributes[i], "Json_", 5)); + for (uint i = n; i < args->arg_count; i++) + if (args->arg_type[i] == STRING_RESULT && args->args[i]) { + // A path to a subset of the json tree is given + char *path = MakePSZ(g, args, i); + + if (path) { + PJSNX jsx = new(g)JSNX(g, jsp, TYPE_STRING); + + if (jsx->SetJpath(g, path)) + return true; + + if (!(jvp = jsx->GetJson(g))) { + sprintf(g->Message, "No sub-item at '%s'", path); + return true; + } // endif jvp + + } else { + strcpy(g->Message, "Path argument is null"); + return true; + } // endif path + + break; + } // endif type + + return false; +} // end of CheckPath + +/*********************************************************************************/ +/* Make the result according to the first argument type. */ +/*********************************************************************************/ +static char *MakeResult(PGLOBAL g, UDF_ARGS *args, PJSON top, uint n = 2) +{ + char *str; + + if (IsJson(args, 0) == 2) { + // Make the change in the json file + char *msg; + int pretty = 2; + + for (uint i = n; i < args->arg_count; i++) + if (args->arg_type[i] == INT_RESULT) { + pretty = (int)*(longlong*)args->args[i]; + break; + } // endif type + + if ((msg = Serialize(g, top, MakePSZ(g, args, 0), pretty))) + PUSH_WARNING(msg); + + str = NULL; + } else if (IsJson(args, 0) == 3) { + PBSON bsp = (PBSON)args->args[0]; + + if (bsp->Filename) { + // Make the change in the json file + char *msg; + + if ((msg = Serialize(g, top, bsp->Filename, bsp->Pretty))) + PUSH_WARNING(msg); + + str = bsp->Filename; + } else if (!(str = Serialize(g, top, NULL, 0))) + PUSH_WARNING(g->Message); + + SetChanged(bsp); + } else if (!(str = Serialize(g, top, NULL, 0))) + PUSH_WARNING(g->Message); + + return str; +} // end of MakeResult + +/*********************************************************************************/ +/* Make the binary result according to the first argument type. */ +/*********************************************************************************/ +static PBSON MakeBinResult(PGLOBAL g, UDF_ARGS *args, PJSON top, ulong len, int n = 2) +{ + PBSON bsnp = JbinAlloc(g, args, len, top); + + if (IsJson(args, 0) == 2) { + int pretty = 2; + + for (uint i = n; i < args->arg_count; i++) + if (args->arg_type[i] == INT_RESULT) { + pretty = (int)*(longlong*)args->args[i]; + break; + } // endif type + + bsnp->Pretty = pretty; + + if (bsnp->Filename = (char*)args->args[0]) + strncpy(bsnp->Msg, (char*)args->args[0], BMX); + else + strncpy(bsnp->Msg, "null filename", BMX); + + } else if (IsJson(args, 0) == 3) { + PBSON bsp = (PBSON)args->args[0]; + + if (bsp->Filename) { + bsnp->Filename = bsp->Filename; + strncpy(bsnp->Msg, bsp->Filename, BMX); + bsnp->Pretty = bsp->Pretty; + } else + strcpy(bsnp->Msg, "Json Binary item"); + + } else + strcpy(bsnp->Msg, "Json Binary item"); + + return bsnp; +} // end of MakeBinResult + +/*********************************************************************************/ +/* Returns a pointer to the first integer argument found from the nth argument. */ +/*********************************************************************************/ +static int *GetIntArgPtr(PGLOBAL g, UDF_ARGS *args, uint& n) +{ + int *x = NULL; + + for (uint i = n; i < args->arg_count; i++) + if (args->arg_type[i] == INT_RESULT) { + if (args->args[i]) { + x = (int*)PlugSubAlloc(g, NULL, sizeof(int)); + *x = (int)*(longlong*)args->args[i]; + } // endif args + + n = i + 1; + break; + } // endif arg_type + + return x; +} // end of GetIntArgPtr + +/*********************************************************************************/ +/* Returns not 0 if the argument is a JSON item or file name. */ +/*********************************************************************************/ +static int IsJson(UDF_ARGS *args, uint i) +{ + int n = 0; + + if (i >= args->arg_count || args->arg_type[i] != STRING_RESULT) { + } else if (!strnicmp(args->attributes[i], "Json_", 5)) { + if (!args->args[i] || strchr("[{ \t\r\n", *args->args[i])) + n = 1; // arg should be is a json item + else + n = 2; // A file name may have been returned + + } else if (!strnicmp(args->attributes[i], "Jbin_", 5)) { + if (args->lengths[i] == sizeof(BSON)) + n = 3; // arg is a binary json item + else + n = 2; // A file name may have been returned + + } else if (!strnicmp(args->attributes[i], "Jfile_", 6)) + n = 2; // arg is a json file name + + return n; } // end of IsJson -/***********************************************************************/ -/* Calculate the reslen and memlen needed by a function. */ -/***********************************************************************/ -static my_bool CalcLen(UDF_ARGS *args, my_bool obj, - unsigned long& reslen, unsigned long& memlen) +/*********************************************************************************/ +/* GetFileLength: returns file size in number of bytes. */ +/*********************************************************************************/ +static long GetFileLength(char *fn) { - unsigned long i, k; + int h; + long len; + + h= open(fn, _O_RDONLY); + + if (h != -1) { + if ((len = _filelength(h)) < 0) + len = 0; + + close(h); + } else + len = 0; + + return len; +} // end of GetFileLength + +/*********************************************************************************/ +/* Calculate the reslen and memlen needed by a function. */ +/*********************************************************************************/ +static my_bool CalcLen(UDF_ARGS *args, my_bool obj, + unsigned long& reslen, unsigned long& memlen, + my_bool mod = false) +{ + char fn[_MAX_PATH]; + unsigned long i, k, m, n; + long fl, j = -1; + reslen = args->arg_count + 2; // Calculate the result max length for (i = 0; i < args->arg_count; i++) { + n = IsJson(args, i); + if (obj) { if (!(k = args->attribute_lengths[i])) k = strlen(args->attributes[i]); @@ -112,9 +1109,22 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj, switch (args->arg_type[i]) { case STRING_RESULT: - if (IsJson(args, i)) + if (n == 2 && args->args[i]) { + if (!mod) { + m = MY_MIN(args->lengths[i], sizeof(fn) - 1); + memcpy(fn, args->args[i], m); + fn[m] = 0; + j = i; + fl = GetFileLength(fn); + reslen += fl; + } else + reslen += args->lengths[i]; + + } else if (n == 3 && args->args[i]) + reslen += ((PBSON)args->args[i])->Reslen; + else if (n == 1) reslen += args->lengths[i]; - else + else reslen += (args->lengths[i] + 1) * 2; // Pessimistic ! break; @@ -136,11 +1146,12 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj, } // endfor i - // Calculate the amount of memory needed - memlen = MEMFIX + sizeof(JOUTSTR) + reslen; + // Calculate the amount of memory needed + memlen = MEMFIX + sizeof(JOUTSTR) + reslen; for (i = 0; i < args->arg_count; i++) { - memlen += (args->lengths[i] + sizeof(JVALUE)); + n = IsJson(args, i); + memlen += (args->lengths[i] + sizeof(JVALUE)); if (obj) { if (!(k = args->attribute_lengths[i])) @@ -152,8 +1163,20 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj, switch (args->arg_type[i]) { case STRING_RESULT: - if (IsJson(args, i)) - memlen += args->lengths[i] * 5; // Estimate parse memory + if (n == 2 && args->args[i]) { + if ((signed)i != j) { + m = MY_MIN(args->lengths[i], sizeof(fn) - 1); + memcpy(fn, args->args[i], m); + fn[m] = 0; + j = -1; + fl = GetFileLength(fn); + } // endif i + + memlen += fl * M; + } else if (IsJson(args, i) == 3) + memlen += sizeof(JVALUE); + else if (IsJson(args, i) == 1) + memlen += args->lengths[i] * M; // Estimate parse memory memlen += sizeof(TYPVAL); break; @@ -173,15 +1196,54 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj, } // endfor i - return false; + return false; } // end of CalcLen -/***********************************************************************/ -/* Make a zero terminated string from the passed argument. */ -/***********************************************************************/ +/*********************************************************************************/ +/* Check if the calculated memory is enough. */ +/*********************************************************************************/ +static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, + uint n, my_bool obj, my_bool mod = false) +{ + unsigned long rl, ml; + + n = MY_MIN(n, args->arg_count); + + for (uint i = 0; i < n; i++) + if (IsJson(args, i) == 2) { + if (CalcLen(args, obj, rl, ml, mod)) + return true; + else if (ml > g->Sarea_Size) { + free(g->Sarea); + + if (!(g->Sarea = PlugAllocMem(g, ml))) { + char errmsg[256]; + + sprintf(errmsg, MSG(WORK_AREA), g->Message); + strcpy(g->Message, errmsg); + g->Sarea_Size = 0; + return true; + } // endif Alloc + + g->Sarea_Size = ml; + g->Createas = 0; + g->Xchk = NULL; + initid->max_length = rl; + } // endif Size + + break; + } // endif IsJson + + JsonSubSet(g); + return false; +} // end of CheckMemory + +/*********************************************************************************/ +/* Make a zero terminated string from the passed argument. */ +/*********************************************************************************/ static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i) { - if (args->args[i]) { + if (args->arg_count > (unsigned)i && args->args[i]) { int n = args->lengths[i]; PSZ s = (PSZ)PlugSubAlloc(g, NULL, n + 1); @@ -193,73 +1255,205 @@ static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i) } // end of MakePSZ -/***********************************************************************/ -/* Make a valid key from the passed argument. */ -/***********************************************************************/ +/*********************************************************************************/ +/* Make a valid key from the passed argument. */ +/*********************************************************************************/ static PSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i) { - int n = args->attribute_lengths[i]; - bool b; // true if attribute is zero terminated - PSZ p, s = args->attributes[i]; + if (args->arg_count > (unsigned)i) { + int j = 0, n = args->attribute_lengths[i]; + my_bool b; // true if attribute is zero terminated + PSZ p, s = args->attributes[i]; - if (s && *s && (n || *s == '\'')) { - if ((b = (!n || !s[n]))) - n = strlen(s); + if (s && *s && (n || *s == '\'')) { + if ((b = (!n || !s[n]))) + n = strlen(s); - if (n > 5 && IsJson(args, i)) { - s += 5; - n -= 5; - } else if (*s == '\'' && s[n-1] == '\'') { - s++; - n -= 2; - b = false; - } // endif *s + if (IsJson(args, i)) + j = strchr(s, '_') - s + 1; - if (n < 1) - return "Key"; + if (j && n > j) { + s += j; + n -= j; + } else if (*s == '\'' && s[n-1] == '\'') { + s++; + n -= 2; + b = false; + } // endif *s - if (!b) { - p = (PSZ)PlugSubAlloc(g, NULL, n + 1); - memcpy(p, s, n); - p[n] = 0; - s = p; - } // endif b + if (n < 1) + return "Key"; - } // endif s + if (!b) { + p = (PSZ)PlugSubAlloc(g, NULL, n + 1); + memcpy(p, s, n); + p[n] = 0; + s = p; + } // endif b - return s; + } // endif s + + return s; + } // endif count + + return "Key"; } // end of MakeKey -/***********************************************************************/ -/* Make a JSON value from the passed argument. */ -/***********************************************************************/ -static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i) +/*********************************************************************************/ +/* Parse a json file. */ +/*********************************************************************************/ +static PJSON ParseJsonFile(PGLOBAL g, char *fn, int *pretty, int& len) { - char *sap = (args->arg_count > (unsigned)i) ? args->args[i] : NULL; + char *memory; + HANDLE hFile; + MEMMAP mm; + PJSON jsp; + + /*******************************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************************/ + hFile = CreateFileMap(g, fn, &mm, MODE_READ, false); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)rc, fn); + + return NULL; + } // endif hFile + + /*******************************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*******************************************************************************/ + len = mm.lenL; + memory = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + return NULL; + } // endif len + + if (!memory) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, GetLastError()); + return NULL; + } // endif Memory + + CloseFileHandle(hFile); // Not used anymore + + /*********************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*********************************************************************************/ + g->Message[0] = 0; + jsp = ParseJson(g, memory, len, pretty); + CloseMemMap(memory, len); + return jsp; +} // end of ParseJsonFile + +/*********************************************************************************/ +/* Return a json file contains. */ +/*********************************************************************************/ +static char *GetJsonFile(PGLOBAL g, char *fn) +{ + char *str; + int h, n, len; + +#if defined(UNIX) || defined(UNIV_LINUX) + h= open(fn, O_RDONLY); +#else + h= open(fn, _O_RDONLY, _O_TEXT); +#endif + + if (h == -1) { + sprintf(g->Message, "Error %d opening %s", errno, fn); + return NULL; + } // endif h + + if ((len = _filelength(h)) < 0) { + sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn); + close(h); + return NULL; + } // endif len + + str = (char*)PlugSubAlloc(g, NULL, len + 1); + + if ((n = read(h, str, len)) < 0) { + sprintf(g->Message, "Error %d reading %d bytes from %s", errno, len, fn); + return NULL; + } // endif n + + str[n] = 0; + close(h); + return str; +} // end of GetJsonFile + +/*********************************************************************************/ +/* Make a JSON value from the passed argument. */ +/*********************************************************************************/ +static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, uint i, PJSON *top = NULL) +{ + char *sap = (args->arg_count > i) ? args->args[i] : NULL; + int n, len; + short c; + long long bigint; PJSON jsp; PJVAL jvp = new(g) JVALUE; + if (top) + *top = NULL; + if (sap) switch (args->arg_type[i]) { case STRING_RESULT: - if (args->lengths[i]) { - if (IsJson(args, i)) { - if (!(jsp = ParseJson(g, sap, args->lengths[i], 0))) - push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, - g->Message); + if ((len = args->lengths[i])) { + if ((n = IsJson(args, i)) < 3) + sap = MakePSZ(g, args, i); + + if (n) { + if (n == 3) { + if (top) + *top = ((PBSON)sap)->Top; + + jsp = ((PBSON)sap)->Jsp; + } else { + if (n == 2) { + if (!(sap = GetJsonFile(g, sap))) { + PUSH_WARNING(g->Message); + return jvp; + } // endif sap + + len = strlen(sap); + } // endif n + + if (!(jsp = ParseJson(g, sap, strlen(sap)))) + PUSH_WARNING(g->Message); + else if (top) + *top = jsp; + + } // endif's n if (jsp && jsp->GetType() == TYPE_JVAL) jvp = (PJVAL)jsp; else jvp->SetValue(jsp); - } else - jvp->SetString(g, MakePSZ(g, args, i)); + } else { + c = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1; + jvp->SetString(g, sap, c); + } // endif n - } // endif str + } // endif len break; case INT_RESULT: - jvp->SetInteger(g, (int)*(long long*)sap); + bigint = *(long long*)sap; + + if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) || + (bigint == 1LL && !strcmp(args->attributes[i], "TRUE"))) + jvp->SetTiny(g, (char)bigint); + else + jvp->SetBigint(g, bigint); + break; case REAL_RESULT: jvp->SetFloat(g, *(double*)sap); @@ -276,291 +1470,661 @@ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i) return jvp; } // end of MakeValue -/***********************************************************************/ -/* Make a Json value containing the parameter. */ -/***********************************************************************/ -my_bool Json_Value_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Make a Json value containing the parameter. */ +/*********************************************************************************/ +my_bool jsonvalue_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen; if (args->arg_count > 1) { - strcpy(message, "Json_Value cannot accept more than 1 argument"); + strcpy(message, "Cannot accept more than 1 argument"); return true; } else CalcLen(args, false, reslen, memlen); - return JsonInit(initid, message, reslen, memlen); -} // end of Json_Value_init + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jsonvalue_init -char *Json_Value(UDF_INIT *initid, UDF_ARGS *args, char *result, +char *jsonvalue(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *res_length, char *, char *) { char *str; - PJVAL jvp; PGLOBAL g = (PGLOBAL)initid->ptr; - PlugSubSet(g, g->Sarea, g->Sarea_Size); - jvp = MakeValue(g, args, 0); + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, 1, false)) { + PJVAL jvp = MakeValue(g, args, 0); - if (!(str = Serialize(g, jvp, NULL, 0))) - str = strcpy(result, g->Message); + if (!(str = Serialize(g, jvp, NULL, 0))) + str = strcpy(result, g->Message); - *res_length = strlen(str); + } else + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); return str; -} // end of Json_Value +} // end of JsonValue -void Json_Value_deinit(UDF_INIT* initid) +void jsonvalue_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Value_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonvalue_deinit -/***********************************************************************/ -/* Make a Json array containing all the parameters. */ -/***********************************************************************/ -my_bool Json_Array_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Make a Json array containing all the parameters. */ +/*********************************************************************************/ +my_bool json_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen; CalcLen(args, false, reslen, memlen); - return JsonInit(initid, message, reslen, memlen); -} // end of Json_Array_init + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_array_init -char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *, char *) +char *json_array(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) { char *str; - uint i; - PJAR arp; PGLOBAL g = (PGLOBAL)initid->ptr; - PlugSubSet(g, g->Sarea, g->Sarea_Size); - arp = new(g) JARRAY; + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, false)) { + PJAR arp = new(g)JARRAY; - for (i = 0; i < args->arg_count; i++) - arp->AddValue(g, MakeValue(g, args, i)); + for (uint i = 0; i < args->arg_count; i++) + arp->AddValue(g, MakeValue(g, args, i)); - arp->InitArray(g); + arp->InitArray(g); - if (!(str = Serialize(g, arp, NULL, 0))) - str = strcpy(result, g->Message); + if (!(str = Serialize(g, arp, NULL, 0))) + str = strcpy(result, g->Message); - *res_length = strlen(str); + } else + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); return str; -} // end of Json_Array +} // end of json_array -void Json_Array_deinit(UDF_INIT* initid) +void json_array_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Array_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_deinit -/***********************************************************************/ -/* Add values to a Json array. */ -/***********************************************************************/ -my_bool Json_Array_Add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Add one or several values to a Json array. */ +/*********************************************************************************/ +my_bool json_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json string or item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_array_add_values_init + +char *json_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, false)) { + char *p; + PJSON top; + PJAR arp; + PJVAL jvp = MakeValue(g, args, 0, &top); + + if ((p = jvp->GetString())) { + if (!(top = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + jvp->SetValue(top); + } // endif p + + if (jvp->GetValType() != TYPE_JAR) { + arp = new(g)JARRAY; + arp->AddValue(g, jvp); + } else + arp = jvp->GetArray(); + + for (uint i = 1; i < args->arg_count; i++) + arp->AddValue(g, MakeValue(g, args, i)); + + arp->InitArray(g); +// str = Serialize(g, arp, NULL, 0); + str = MakeResult(g, args, top, args->arg_count); + } // endif CheckMemory + + if (!str) { + PUSH_WARNING(g->Message); + str = args->args[0]; + } // endif str + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_array_add_values + +void json_array_add_values_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_add_values_deinit + +/*********************************************************************************/ +/* Add one value to a Json array. */ +/*********************************************************************************/ +my_bool json_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen; - if (args->arg_count < 2) { - strcpy(message, "Json_Value_Add must have at least 2 arguments"); + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); return true; } else if (!IsJson(args, 0)) { - strcpy(message, "Json_Value_Add first argument must be a json item"); + strcpy(message, "First argument must be a json item"); return true; - } else - CalcLen(args, false, reslen, memlen); + } else + CalcLen(args, false, reslen, memlen, true); - return JsonInit(initid, message, reslen, memlen); -} // end of Json_Array_Add_init + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_array_add_init -char *Json_Array_Add(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *, char *) +char *json_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) { - char *str; - PJVAL jvp; - PJAR arp; + char *str = NULL; PGLOBAL g = (PGLOBAL)initid->ptr; - PlugSubSet(g, g->Sarea, g->Sarea_Size); - jvp = MakeValue(g, args, 0); + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + goto fin; + } // endif Xchk - if (jvp->GetValType() != TYPE_JAR) { - arp = new(g) JARRAY; - arp->AddValue(g, jvp); - } else - arp = jvp->GetArray(); + if (!CheckMemory(g, initid, args, 2, false, true)) { + int *x; + uint n = 2; + PJSON jsp, top; + PJVAL jvp; + PJAR arp; - for (uint i = 1; i < args->arg_count; i++) - arp->AddValue(g, MakeValue(g, args, i)); + jvp = MakeValue(g, args, 0, &top); + jsp = jvp->GetJson(); + x = GetIntArgPtr(g, args, n); - arp->InitArray(g); + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JAR) { + arp = jvp->GetArray(); + arp->AddValue(g, MakeValue(g, args, 1), x); + arp->InitArray(g); + str = MakeResult(g, args, top, n); + } else { + PUSH_WARNING("First argument target is not an array"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp - if (!(str = Serialize(g, arp, NULL, 0))) - str = strcpy(result, g->Message); + } // endif CheckMemory - *res_length = strlen(str); - return str; -} // end of Json_Array_Add + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); -void Json_Array_Add_deinit(UDF_INIT* initid) + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + +fin: + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_array_add + +void json_array_add_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Array_Add_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_add_deinit -/***********************************************************************/ -/* Delete a value from a Json array. */ -/***********************************************************************/ -my_bool Json_Array_Delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Delete a value from a Json array. */ +/*********************************************************************************/ +my_bool json_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen; - if (args->arg_count != 2) { - strcpy(message, "Json_Value_Delete must have 2 arguments"); + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); return true; - } else if (!IsJson(args, 0)) { - strcpy(message, "Json_Value_Delete first argument must be a json item"); - return true; - } else - CalcLen(args, false, reslen, memlen); + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); - return JsonInit(initid, message, reslen, memlen); -} // end of Json_Array_Delete_init + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_array_delete_init -char *Json_Array_Delete(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *, char *) +char *json_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) { - char *str; - int n; - PJVAL jvp; - PJAR arp; + char *str = NULL; PGLOBAL g = (PGLOBAL)initid->ptr; - PlugSubSet(g, g->Sarea, g->Sarea_Size); - jvp = MakeValue(g, args, 0); + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + *res_length = strlen(str); + return str; + } // endif Xchk - if (jvp->GetValType() != TYPE_JAR) { - push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, - "First argument is not an array"); - str = args->args[0]; - } else if (args->arg_type[1] != INT_RESULT) { - push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, - "Second argument is not an integer"); - str = args->args[0]; - } else { - n = *(int*)args->args[1]; - arp = jvp->GetArray(); - arp->DeleteValue(n); - arp->InitArray(g); + if (!CheckMemory(g, initid, args, 1, false, true)) { + int *x; + uint n = 1; + PJSON top; + PJAR arp; + PJVAL jvp = MakeValue(g, args, 0, &top); - if (!(str = Serialize(g, arp, NULL, 0))) { - str = strcpy(result, g->Message); - push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, str); - } // endif str + if (!(x = GetIntArgPtr(g, args, n))) + PUSH_WARNING("Missing or null array index"); + else if (CheckPath(g, args, jvp->GetJson(), jvp, 1)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JAR) { + arp = jvp->GetArray(); + arp->DeleteValue(*x); + arp->InitArray(g); + str = MakeResult(g, args, top, n); + } else { + PUSH_WARNING("First argument target is not an array"); +// if (g->Mrr) *error = 1; + } // endif jvp - } // endif's + } // endif CheckMemory - *res_length = strlen(str); - return str; -} // end of Json_Array_Delete + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); -void Json_Array_Delete_deinit(UDF_INIT* initid) + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + + *res_length = (str) ? strlen(str) : 0; + return str; +} // end of json_array_delete + +void json_array_delete_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Array_Delete_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_delete_deinit -/***********************************************************************/ -/* Make a Json Oject containing all the parameters. */ -/***********************************************************************/ -my_bool Json_Object_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Make a Json Oject containing all the parameters. */ +/*********************************************************************************/ +my_bool json_object_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen; CalcLen(args, true, reslen, memlen); - return JsonInit(initid, message, reslen, memlen); -} // end of Json_Object_init + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_object_init -char *Json_Object(UDF_INIT *initid, UDF_ARGS *args, char *result, +char *json_object(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *res_length, char *, char *) { - char *str; - uint i; - PJOB objp; + char *str = NULL; PGLOBAL g = (PGLOBAL)initid->ptr; - PlugSubSet(g, g->Sarea, g->Sarea_Size); - objp = new(g) JOBJECT; + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJOB objp = new(g)JOBJECT; - for (i = 0; i < args->arg_count; i++) - objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); + for (uint i = 0; i < args->arg_count; i++) + objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); - if (!(str = Serialize(g, objp, NULL, 0))) - str = strcpy(result, g->Message); + str = Serialize(g, objp, NULL, 0); + } // endif CheckMemory - *res_length = strlen(str); + if (!str) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); return str; -} // end of Json_Object +} // end of json_object -void Json_Object_deinit(UDF_INIT* initid) +void json_object_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Object_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_deinit -/***********************************************************************/ -/* Make a Json Oject containing all not null parameters. */ -/***********************************************************************/ -my_bool Json_Object_Nonull_init(UDF_INIT *initid, UDF_ARGS *args, +/*********************************************************************************/ +/* Make a Json Oject containing all not null parameters. */ +/*********************************************************************************/ +my_bool json_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen; CalcLen(args, true, reslen, memlen); - return JsonInit(initid, message, reslen, memlen); -} // end of Json_Object_Nonull_init + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_object_nonull_init -char *Json_Object_Nonull(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *, char *) +char *json_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) { char *str; - uint i; - PJOB objp; - PJVAL jvp; PGLOBAL g = (PGLOBAL)initid->ptr; - PlugSubSet(g, g->Sarea, g->Sarea_Size); - objp = new(g) JOBJECT; + if (!g->Xchk) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJVAL jvp; + PJOB objp = new(g)JOBJECT; - for (i = 0; i < args->arg_count; i++) - if (!(jvp = MakeValue(g, args, i))->IsNull()) - objp->SetValue(g, jvp, MakeKey(g, args, i)); + for (uint i = 0; i < args->arg_count; i++) + if (!(jvp = MakeValue(g, args, i))->IsNull()) + objp->SetValue(g, jvp, MakeKey(g, args, i)); - if (!(str = Serialize(g, objp, NULL, 0))) - str = strcpy(result, g->Message); + str = Serialize(g, objp, NULL, 0); + } // endif CheckMemory - *res_length = strlen(str); + if (!str) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); return str; -} // end of Json_Object_Nonull +} // end of json_object_nonull -void Json_Object_Nonull_deinit(UDF_INIT* initid) +void json_object_nonull_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Object_nonull_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_nonull_deinit -/***********************************************************************/ -/* Make a Json array from values comming from rows. */ -/***********************************************************************/ -my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Add or replace a value in a Json Object. */ +/*********************************************************************************/ +my_bool json_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_object_add_init + +char *json_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + char *key, *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + *res_length = strlen(str); + return str; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 2, false, true)) { + PJOB jobp; + PJVAL jvp; + PJSON jsp, top; + + jvp = MakeValue(g, args, 0, &top); + jsp = jvp->GetJson(); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + jobp = jvp->GetObject(); + jvp = MakeValue(g, args, 1); + key = MakeKey(g, args, 1); + jobp->SetValue(g, jvp, key); + str = MakeResult(g, args, top); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + + *res_length = strlen(str); + return str; +} // end of json_object_add + +void json_object_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json object. */ +/*********************************************************************************/ +my_bool json_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "This function must have 2 or 3 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument must be a key string"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_object_delete_init + +char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + *res_length = strlen(str); + return str; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 1, false, true)) { + char *key; + PJOB jobp; + PJSON jsp, top; + PJVAL jvp = MakeValue(g, args, 0, &top); + + jsp = jvp->GetJson(); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + key = MakeKey(g, args, 1); + jobp = jvp->GetObject(); + jobp->DeleteKey(key); + str = MakeResult(g, args, top); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + + *res_length = strlen(str); + return str; +} // end of json_object_delete + +void json_object_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_delete_deinit + +/*********************************************************************************/ +/* Returns an array of the Json object keys. */ +/*********************************************************************************/ +my_bool json_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "Argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_object_list_init + +char *json_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->N) { + if (!CheckMemory(g, initid, args, 1, false)) { + char *p; + PJSON jsp; + PJVAL jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (jsp->GetType() == TYPE_JOB) { + PJAR jarp = ((PJOB)jsp)->GetKeyList(g); + + if (!(str = Serialize(g, jarp, NULL, 0))) + PUSH_WARNING(g->Message); + + } else { + PUSH_WARNING("First argument is not an object"); + if (g->Mrr) *error = 1; + } // endif jvp + + } // endif CheckMemory + + if (initid->const_item) { + // Keep result of constant function + g->Xchk = str; + g->N = 1; // str can be NULL + } // endif const_item + + } else + str = (char*)g->Xchk; + + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_object_list + +void json_object_list_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_list_deinit + +/*********************************************************************************/ +/* Make a Json array from values coming from rows. */ +/*********************************************************************************/ +my_bool json_array_grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen, n = GetJsonGrpSize(); if (args->arg_count != 1) { - strcpy(message, "Json_Array_Grp can only accept 1 argument"); + strcpy(message, "This function can only accept 1 argument"); return true; - } else + } else if (IsJson(args, 0) == 3) { + strcpy(message, "This function does not support Jbin arguments"); + return true; + } else CalcLen(args, false, reslen, memlen); reslen *= n; memlen += ((memlen - MEMFIX) * (n - 1)); - if (JsonInit(initid, message, reslen, memlen)) + if (JsonInit(initid, args, message, false, reslen, memlen)) return true; PGLOBAL g = (PGLOBAL)initid->ptr; @@ -569,9 +2133,9 @@ my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) g->Activityp = (PACTIVITY)new(g) JARRAY; g->N = (int)n; return false; -} // end of Json_Array_Grp_init +} // end of json_array_grp_init -void Json_Array_Grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) +void json_array_grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) { PGLOBAL g = (PGLOBAL)initid->ptr; PJAR arp = (PJAR)g->Activityp; @@ -579,9 +2143,9 @@ void Json_Array_Grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) if (g->N-- > 0) arp->AddValue(g, MakeValue(g, args, 0)); -} // end of Json_Array_Grp_add +} // end of json_array_grp_add -char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result, +char *json_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result, unsigned long *res_length, char *, char *) { char *str; @@ -589,8 +2153,7 @@ char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result, PJAR arp = (PJAR)g->Activityp; if (g->N < 0) - push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, - "Result truncated to json_grp_size values"); + PUSH_WARNING("Result truncated to json_grp_size values"); arp->InitArray(g); @@ -599,39 +2162,42 @@ char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result, *res_length = strlen(str); return str; -} // end of Json_Array_Grp +} // end of json_array_grp -void Json_Array_Grp_clear(UDF_INIT *initid, char*, char*) +void json_array_grp_clear(UDF_INIT *initid, char*, char*) { PGLOBAL g = (PGLOBAL)initid->ptr; PlugSubSet(g, g->Sarea, g->Sarea_Size); g->Activityp = (PACTIVITY)new(g) JARRAY; g->N = GetJsonGrpSize(); -} // end of Json_Array_Grp_clear +} // end of json_array_grp_clear -void Json_Array_Grp_deinit(UDF_INIT* initid) +void json_array_grp_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Array_Grp_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_array_grp_deinit -/***********************************************************************/ -/* Make a Json object from values comming from rows. */ -/***********************************************************************/ -my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +/*********************************************************************************/ +/* Make a Json object from values coming from rows. */ +/*********************************************************************************/ +my_bool json_object_grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { unsigned long reslen, memlen, n = GetJsonGrpSize(); - if (args->arg_count != 2) { - strcpy(message, "Json_Array_Grp can only accept 2 arguments"); + if (args->arg_count != 2) { + strcpy(message, "This function requires 2 arguments (value, key)"); return true; - } else + } else if (IsJson(args, 0) == 3) { + strcpy(message, "This function does not support Jbin arguments"); + return true; + } else CalcLen(args, true, reslen, memlen); reslen *= n; memlen += ((memlen - MEMFIX) * (n - 1)); - if (JsonInit(initid, message, reslen, memlen)) + if (JsonInit(initid, args, message, false, reslen, memlen)) return true; PGLOBAL g = (PGLOBAL)initid->ptr; @@ -640,19 +2206,20 @@ my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) g->Activityp = (PACTIVITY)new(g) JOBJECT; g->N = (int)n; return false; -} // end of Json_Object_Grp_init +} // end of json_object_grp_init -void Json_Object_Grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) +void json_object_grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*) { PGLOBAL g = (PGLOBAL)initid->ptr; PJOB objp = (PJOB)g->Activityp; - if (g->N-- > 0) - objp->SetValue(g, MakeValue(g, args, 0), MakePSZ(g, args, 1)); + if (g->N-- > 0) + objp->SetValue(g, MakeValue(g, args, 0), + (args->arg_count == 1) ? MakeKey(g, args, 0) : MakePSZ(g, args, 1)); -} // end of Json_Object_Grp_add +} // end of json_object_grp_add -char *Json_Object_Grp(UDF_INIT *initid, UDF_ARGS *, char *result, +char *json_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result, unsigned long *res_length, char *, char *) { char *str; @@ -660,28 +2227,1830 @@ char *Json_Object_Grp(UDF_INIT *initid, UDF_ARGS *, char *result, PJOB objp = (PJOB)g->Activityp; if (g->N < 0) - push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, - "Result truncated to json_grp_size values"); + PUSH_WARNING("Result truncated to json_grp_size values"); if (!(str = Serialize(g, objp, NULL, 0))) str = strcpy(result, g->Message); *res_length = strlen(str); return str; -} // end of Json_Object_Grp +} // end of json_object_grp -void Json_Object_Grp_clear(UDF_INIT *initid, char*, char*) +void json_object_grp_clear(UDF_INIT *initid, char*, char*) { PGLOBAL g = (PGLOBAL)initid->ptr; PlugSubSet(g, g->Sarea, g->Sarea_Size); g->Activityp = (PACTIVITY)new(g) JOBJECT; g->N = GetJsonGrpSize(); -} // end of Json_Object_Grp_clear +} // end of json_object_grp_clear -void Json_Object_Grp_deinit(UDF_INIT* initid) +void json_object_grp_deinit(UDF_INIT* initid) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Object_Grp_deinit + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_object_grp_deinit +/*********************************************************************************/ +/* Merge two arrays or objects. */ +/*********************************************************************************/ +my_bool json_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!IsJson(args, 0)) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (!IsJson(args, 1)) { + strcpy(message, "Second argument must be a json item"); + return true; + } else + CalcLen(args, false, reslen, memlen, true); + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_item_merge_init + +char *json_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->Xchk) { + // This constant function was recalled + str = (char*)g->Xchk; + *res_length = strlen(str); + return str; + } // endif Xchk + + if (!CheckMemory(g, initid, args, 2, false, true)) { + PJSON top; + PJVAL jvp; + PJSON jsp[2] = {NULL, NULL}; + + for (int i = 0; i < 2; i++) { + jvp = MakeValue(g, args, i); + if (!i) top = jvp->GetJson(); + + if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) { + sprintf(g->Message, "Argument %d is not an array or object", i); + PUSH_WARNING(g->Message); + } else + jsp[i] = jvp->GetJsp(); + + } // endfor i + + if (jsp[0]) { + if (jsp[0]->Merge(g, jsp[1])) + PUSH_WARNING(g->Message); + else + str = MakeResult(g, args, top); + + } // endif jsp + + } // endif CheckMemory + + // In case of error or file, return unchanged argument + if (!str) + str = MakePSZ(g, args, 0); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + + *res_length = strlen(str); + return str; +} // end of json_item_merge + +void json_item_merge_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_item_merge_deinit + +/*********************************************************************************/ +/* Get a Json item from a Json document. */ +/*********************************************************************************/ +my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + int n = IsJson(args, 0); + + if (args->arg_count < 2) { + strcpy(message, "This function must have at least 2 arguments"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string (jpath)"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + if (n == 2) { + char fn[_MAX_PATH]; + long fl; + + memcpy(fn, args->args[0], args->lengths[0]); + fn[args->lengths[0]] = 0; + fl = GetFileLength(fn); + memlen += fl * 3; + } else if (n != 3) + memlen += args->lengths[0] * 3; + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_get_item_init + +char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length); + + if (jsx->SetJpath(g, path, true)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return NULL; + } // endif SetJpath + + jsx->ReadValue(g); + + if (!jsx->GetValue()->IsNull()) + str = jsx->GetValue()->GetCharValue(); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + } // endif CheckMemory + + fin: + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of json_get_item + +void json_get_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_get_item_deinit + +/*********************************************************************************/ +/* Get a string value from a Json item. */ +/*********************************************************************************/ +my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1024; + int n = IsJson(args, 0); + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!n && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string (jpath)"); + return true; + } else if (args->arg_count > 2) { + if (args->arg_type[2] == INT_RESULT && args->args[2]) + more += (unsigned long)*(long long*)args->args[2]; + else + strcpy(message, "Third argument is not an integer (memory)"); + + } // endif's + + CalcLen(args, false, reslen, memlen); + memlen += more; + + if (n == 2) { + char fn[_MAX_PATH]; + long fl; + + memcpy(fn, args->args[0], args->lengths[0]); + fn[args->lengths[0]] = 0; + fl = GetFileLength(fn); + memlen += fl * 3; + } else if (n != 3) + memlen += args->lengths[0] * 3; + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jsonget_string_init + +char *jsonget_string(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + int rc; + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + PUSH_WARNING(MSG(TOO_MANY_JUMPS)); + *is_null = 1; + return NULL; + } // endif jump_level + + if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { + PUSH_WARNING(g->Message); + str = NULL; + goto err; + } // endif rc + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto err; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length); + + if (jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + goto err; + } // endif SetJpath + + jsx->ReadValue(g); + + if (!jsx->GetValue()->IsNull()) + str = jsx->GetValue()->GetCharValue(); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + err: + g->jump_level--; + } // endif CheckMemory + +fin: + if (!str) { + *is_null = 1; + *res_length = 0; + } else + *res_length = strlen(str); + + return str; +} // end of jsonget_string + +void jsonget_string_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonget_string_deinit + +/*********************************************************************************/ +/* Get an integer value from a Json item. */ +/*********************************************************************************/ +my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 2) { + strcpy(message, "This function must have 2 arguments"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a (jpath) string"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + if (IsJson(args, 0) != 3) + memlen += 1000; // TODO: calculate this + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jsonget_int_init + +long long jsonget_int(UDF_INIT *initid, UDF_ARGS *args, + char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0LL; + } else + return *(long long*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + long long n; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + if (g->Mrr) *error = 1; + *is_null = 1; + return 0; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = new(g) JSNX(g, jsp, TYPE_BIGINT); + + if (jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0; + } // endif SetJpath + + jsx->ReadValue(g); + + if (jsx->GetValue()->IsNull()) { + PUSH_WARNING("Value not found"); + *is_null = 1; + return 0; + } // endif IsNull + + n = jsx->GetValue()->GetBigintValue(); + + if (initid->const_item) { + // Keep result of constant function + long long *np = (long long*)PlugSubAlloc(g, NULL, sizeof(long long)); + *np = n; + g->Activityp = (PACTIVITY)np; + } // endif const_item + + return n; + } // endif CheckMemory + + if (g->Mrr) *error = 1; + *is_null = 1; + return 0LL; +} // end of jsonget_int + +void jsonget_int_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonget_int_deinit + +/*********************************************************************************/ +/* Get a double value from a Json item. */ +/*********************************************************************************/ +my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a (jpath) string"); + return true; + } else if (args->arg_count > 2) { + if (args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (decimals)"); + return true; + } else + initid->decimals = (uint)*(longlong*)args->args[2]; + + } else + initid->decimals = 15; + + CalcLen(args, false, reslen, memlen); + + if (IsJson(args, 0) != 3) + memlen += 1000; // TODO: calculate this + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jsonget_real_init + +double jsonget_real(UDF_INIT *initid, UDF_ARGS *args, + char *is_null, char *error) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (!g->Activityp) { + *is_null = 1; + return 0.0; + } else + return *(double*)g->Activityp; + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + double d; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0.0; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = new(g) JSNX(g, jsp, TYPE_DOUBLE); + + if (jsx->SetJpath(g, path)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return 0.0; + } // endif SetJpath + + jsx->ReadValue(g); + + if (jsx->GetValue()->IsNull()) { + PUSH_WARNING("Value not found"); + *is_null = 1; + return 0.0; + } // endif IsNull + + d = jsx->GetValue()->GetFloatValue(); + + if (initid->const_item) { + // Keep result of constant function + double *dp = (double*)PlugSubAlloc(g, NULL, sizeof(double)); + *dp = d; + g->Activityp = (PACTIVITY)dp; + } // endif const_item + + return d; + } // endif CheckMemory + + if (g->Mrr) *error = 1; + *is_null = 1; + return 0.0; +} // end of jsonget_real + +void jsonget_real_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonget_real_deinit + +/*********************************************************************************/ +/* Locate a value in a Json tree. */ +/*********************************************************************************/ +my_bool jsonlocate_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1000; + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (rank)"); + return true; + } else if (args->arg_count > 3) + if (args->arg_type[3] != INT_RESULT) { + strcpy(message, "Fourth argument is not an integer (memory)"); + return true; + } else + more += (ulong)*(longlong*)args->args[2]; + + CalcLen(args, false, reslen, memlen); + + if (IsJson(args, 0) != 3) + memlen += more; // TODO: calculate this + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jsonlocate_init + +char *jsonlocate(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *path = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (g->Activityp) { + path = (char*)g->Activityp; + *res_length = strlen(path); + return path; + } else { + *res_length = 0; + *is_null = 1; + return NULL; + } // endif Activityp + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p; + int k, rc; + PJVAL jvp, jvp2; + PJSON jsp; + PJSNX jsx; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + PUSH_WARNING(MSG(TOO_MANY_JUMPS)); + *error = 1; + *is_null = 1; + return NULL; + } // endif jump_level + + if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { + PUSH_WARNING(g->Message); + *error = 1; + path = NULL; + goto err; + } // endif rc + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto err; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + // The item to locate + jvp2 = MakeValue(g, args, 1); + + k = (args->arg_count > 2) ? (int)*(long long*)args->args[2] : 1; + + jsx = new(g) JSNX(g, jsp, TYPE_STRING); + path = jsx->Locate(g, jsp, jvp2, k); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)path; + + err: + g->jump_level--; + + if (!path) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(path); + + return path; + } // endif CheckMemory + + *error = 1; + *is_null = 1; + return NULL; +} // end of jsonlocate + +void jsonlocate_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jsonlocate_deinit + +/*********************************************************************************/ +/* Locate all occurences of a value in a Json tree. */ +/*********************************************************************************/ +my_bool json_locate_all_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1000; + + if (args->arg_count < 2) { + strcpy(message, "At least 2 arguments required"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (Depth)"); + return true; + } else if (args->arg_count > 3) + if (args->arg_type[3] != INT_RESULT) { + strcpy(message, "Fourth argument is not an integer (memory)"); + return true; + } else + more += (ulong)*(longlong*)args->args[2]; + + CalcLen(args, false, reslen, memlen); + + if (IsJson(args, 0) != 3) + memlen += more; // TODO: calculate this + + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of json_locate_all_init + +char *json_locate_all(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *path = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + if (g->Activityp) { + path = (char*)g->Activityp; + *res_length = strlen(path); + return path; + } else { + *error = 1; + *res_length = 0; + *is_null = 1; + return NULL; + } // endif Activityp + + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p; + int rc, mx = 10; + PJVAL jvp, jvp2; + PJSON jsp; + PJSNX jsx; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + PUSH_WARNING(MSG(TOO_MANY_JUMPS)); + *error = 1; + *is_null = 1; + return NULL; + } // endif jump_level + + if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { + PUSH_WARNING(g->Message); + *error = 1; + path = NULL; + goto err; + } // endif rc + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + goto err; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + // The item to locate + jvp2 = MakeValue(g, args, 1); + + if (args->arg_count > 2) + mx = (int)*(long long*)args->args[2]; + + jsx = new(g) JSNX(g, jsp, TYPE_STRING); + path = jsx->LocateAll(g, jsp, jvp2, mx); + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)path; + + err: + g->jump_level--; + + if (!path) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(path); + + return path; + } // endif CheckMemory + + *error = 1; + *is_null = 1; + return NULL; +} // end of json_locate_all + +void json_locate_all_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_locate_all_deinit + +/*********************************************************************************/ +/* Returns a json file as a json string. */ +/*********************************************************************************/ +my_bool json_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, fl, more = 1024; + + if (args->arg_count < 1 || args->arg_count > 4) { + strcpy(message, "This function only accepts 1 to 4 arguments"); + return true; + } else if (!args->args[0] || args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a constant string (file name)"); + return true; + } // endif's args[0] + + for (unsigned int i = 1; i < args->arg_count; i++) { + if (!(args->arg_type[i] == INT_RESULT || args->arg_type[i] == STRING_RESULT)) { + sprintf(message, "Argument %d is not an integer or a string (pretty or path)", i); + return true; + } // endif arg_type + + // Take care of eventual memory argument + if (args->arg_type[i] == INT_RESULT && args->args[i]) + more += (ulong)*(longlong*)args->args[i]; + + } // endfor i + + initid->maybe_null = 1; + CalcLen(args, false, reslen, memlen); + fl = GetFileLength(args->args[0]); + reslen += fl; + + if (initid->const_item) + more += fl; + + if (args->arg_count > 1) + more += fl * M; + + memlen += more; + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_file_init + +char *json_file(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *str, *fn; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Xchk; + goto fin; + } else if (initid->const_item) + g->N = 1; + + PlugSubSet(g, g->Sarea, g->Sarea_Size); + fn = MakePSZ(g, args, 0); + + if (args->arg_count > 1) { + int len, pretty, pty = 3; + PJSON jsp; + PJVAL jvp = NULL; + + pretty = (args->arg_type[1] == INT_RESULT) ? (int)*(longlong*)args->args[1] + : (args->arg_count > 2 && args->arg_type[2] == INT_RESULT) + ? (int)*(longlong*)args->args[2] : 3; + + /*******************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*******************************************************************************/ + if (!(jsp = ParseJsonFile(g, fn, &pty, len))) { + PUSH_WARNING(g->Message); + str = NULL; + goto fin; + } // endif jsp + + if (pty == 3) + PUSH_WARNING("File pretty format cannot be determined"); + else if (pretty != 3 && pty != pretty) + PUSH_WARNING("File pretty format doesn't match the specified pretty value"); + else if (pretty == 3) + pretty = pty; + + // Check whether a path was specified + if (CheckPath(g, args, jsp, jvp, 1)) { + PUSH_WARNING(g->Message); + str = NULL; + goto fin; + } else if (jvp) + jsp = jvp->GetJson(); + + if (!(str = Serialize(g, jsp, NULL, 0))) + PUSH_WARNING(g->Message); + + } else + str = GetJsonFile(g, fn); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = str; + +fin: + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of json_file + +void json_file_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_file_deinit + +/*********************************************************************************/ +/* Make a json file from a json item. */ +/*********************************************************************************/ +my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, more = 1024; + + if (args->arg_count < 1 || args->arg_count > 3) { + strcpy(message, "Wrong number of arguments"); + return true; + } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be a json item"); + return true; + } // endif + + CalcLen(args, false, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen); +} // end of jfile_make_init + +char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + char *p, *str, *msg, *fn = NULL; + int n, pretty = 2; + PJSON jsp; + PJVAL jvp; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + PlugSubSet(g, g->Sarea, g->Sarea_Size); + + if ((n = IsJson(args, 0)) == 3) { + // Get default file name and pretty + PBSON bsp = (PBSON)args->args[0]; + + fn = bsp->Filename; + pretty = bsp->Pretty; + } else if (n == 2) + fn = args->args[0]; + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!strchr("[{ \t\r\n", *p)) { + // Is this a file name? + if (!(p = GetJsonFile(g, p))) { + PUSH_WARNING(g->Message); + return NULL; + } else + fn = jvp->GetString(); + + } // endif p + + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + jvp->SetValue(jsp); + } // endif p + + if (g->Mrr) { // First argument is a constant + g->Xchk = jvp; + JsonMemSave(g); + } // endif Mrr + + } else + jvp = (PJVAL)g->Xchk; + + for (uint i = 1; i < args->arg_count; i++) + switch (args->arg_type[i]) { + case STRING_RESULT: + fn = MakePSZ(g, args, i); + break; + case INT_RESULT: + pretty = (int)*(longlong*)args->args[i]; + break; + default: + PUSH_WARNING("Unexpected argument type in jfile_make"); + } // endswitch arg_type + + if (fn) { + if ((msg = Serialize(g, jvp->GetJson(), fn, pretty))) + PUSH_WARNING(msg); + } else + PUSH_WARNING("Missing file name"); + + str= fn; + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)str; + + fin: + if (!str) { + *res_length = 0; + *is_null = 1; + } else + *res_length = strlen(str); + + return str; +} // end of jfile_make + +void jfile_make_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jfile_make_deinit + +/*********************************************************************************/ +/* Make and return a binary Json array containing all the parameters. */ +/*********************************************************************************/ +my_bool jbin_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, false, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jbin_array_init + +char *jbin_array(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, false)) { + PJAR arp = new(g)JARRAY; + + for (uint i = 0; i < args->arg_count; i++) + arp->AddValue(g, MakeValue(g, args, i)); + + arp->InitArray(g); + bsp = JbinAlloc(g, args, initid->max_length, arp); + strcat(bsp->Msg, " array"); + } else { + bsp = JbinAlloc(g, args, initid->max_length, NULL); + strncpy(bsp->Msg, g->Message, 139); + } // endif CheckMemory + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_array + +void jbin_array_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_deinit + +/*********************************************************************************/ +/* Add one or several values to a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_array_add_values_init(initid, args, message); +} // end of jbin_array_add_values_init + +char *jbin_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, false)) { + char *p; + PJSON top; + PJAR arp; + PJVAL jvp = MakeValue(g, args, 0, &top); + + if ((p = jvp->GetString())) { + if (!(top = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + jvp->SetValue(top); + } // endif p + + if (jvp->GetValType() != TYPE_JAR) { + arp = new(g)JARRAY; + arp->AddValue(g, jvp); + } else + arp = jvp->GetArray(); + + for (uint i = 1; i < args->arg_count; i++) + arp->AddValue(g, MakeValue(g, args, i)); + + arp->InitArray(g); + bsp = JbinAlloc(g, args, initid->max_length, top); + strcat(bsp->Msg, " array"); + bsp->Jsp = arp; + } else { + bsp = JbinAlloc(g, args, initid->max_length, NULL); + strncpy(bsp->Msg, g->Message, BMX); + } // endif CheckMemory + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_array_add_values + +void jbin_array_add_values_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_add_values_deinit + +/*********************************************************************************/ +/* Add one value to a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_array_add_init(initid, args, message); +} // end of jbin_array_add_init + +char *jbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + int n = 2; + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 2, false, true)) { + int *x = NULL; + uint n = 2; +// PJSON jsp; + PJVAL jvp; + PJAR arp; + + jvp = MakeValue(g, args, 0, &top); +// jsp = jvp->GetJson(); + x = GetIntArgPtr(g, args, n); + + if (CheckPath(g, args, top, jvp, n)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JAR) { + arp = jvp->GetArray(); + arp->AddValue(g, MakeValue(g, args, 1), x); + arp->InitArray(g); + } else { + PUSH_WARNING("First argument is not an array"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length, n); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_array_add + +void jbin_array_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_array_delete_init(initid, args, message); +} // end of jbin_array_delete_init + +char *jbin_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 1, false, true)) { + int *x; + uint n = 1; + PJAR arp; + PJVAL jvp = MakeValue(g, args, 0, &top); + + if (CheckPath(g, args, top, jvp, 1)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JAR) { + if ((x = GetIntArgPtr(g, args, n))) { + arp = jvp->GetArray(); + arp->DeleteValue(*x); + arp->InitArray(g); + } else + PUSH_WARNING("Missing or null array index"); + + } else { + PUSH_WARNING("First argument is not an array"); + if (g->Mrr) *error = 1; + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_array_delete + +void jbin_array_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_array_delete_deinit + +/*********************************************************************************/ +/* Make a Json Oject containing all the parameters. */ +/*********************************************************************************/ +my_bool jbin_object_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jbin_object_init + +char *jbin_object(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJOB objp = new(g)JOBJECT; + + for (uint i = 0; i < args->arg_count; i++) + objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i)); + + bsp = JbinAlloc(g, args, initid->max_length, objp); + strcat(bsp->Msg, " object"); + } else { + bsp = JbinAlloc(g, args, initid->max_length, NULL); + strncpy(bsp->Msg, g->Message, BMX); + } // endif CheckMemory + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_object + +void jbin_object_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_deinit + +/*********************************************************************************/ +/* Make a Json Oject containing all not null parameters. */ +/*********************************************************************************/ +my_bool jbin_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jbin_object_nonull_init + +char *jbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, args->arg_count, true)) { + PJVAL jvp; + PJOB objp = new(g)JOBJECT; + + for (uint i = 0; i < args->arg_count; i++) + if (!(jvp = MakeValue(g, args, i))->IsNull()) + objp->SetValue(g, jvp, MakeKey(g, args, i)); + + bsp = JbinAlloc(g, args, initid->max_length, objp); + strcat(bsp->Msg, " object"); + } else { + bsp = JbinAlloc(g, args, initid->max_length, NULL); + strncpy(bsp->Msg, g->Message, BMX); + } // endif CheckMemory + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_object_nonull + +void jbin_object_nonull_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_nonull_deinit + +/*********************************************************************************/ +/* Add or replace a value in a Json Object. */ +/*********************************************************************************/ +my_bool jbin_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_object_add_init(initid, args, message); +} // end of jbin_object_add_init + +char *jbin_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + bsp = (PBSON)g->Xchk; + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 2, false, true)) { + char *key; + PJOB jobp; + PJVAL jvp = MakeValue(g, args, 0, &top); + PJSON jsp = jvp->GetJson(); + + if (CheckPath(g, args, jsp, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + jobp = jvp->GetObject(); + jvp = MakeValue(g, args, 1); + key = MakeKey(g, args, 1); + jobp->SetValue(g, jvp, key); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_object_add + +void jbin_object_add_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_add_deinit + +/*********************************************************************************/ +/* Delete a value from a Json object. */ +/*********************************************************************************/ +my_bool jbin_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_object_delete_init(initid, args, message); +} // end of jbin_object_delete_init + +char *jbin_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + bsp = (PBSON)g->Xchk; + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 1, false, true)) { + char *key; + PJOB jobp; + PJVAL jvp = MakeValue(g, args, 0, &top); + PJSON jsp = jvp->GetJson(); + + if (CheckPath(g, args, top, jvp, 2)) + PUSH_WARNING(g->Message); + else if (jvp && jvp->GetValType() == TYPE_JOB) { + key = MakeKey(g, args, 1); + jobp = jvp->GetObject(); + jobp->DeleteKey(key); + } else { + PUSH_WARNING("First argument target is not an object"); +// if (g->Mrr) *error = 1; (only if no path) + } // endif jvp + + } // endif CheckMemory + + // In case of error unchanged argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_object_delete + +void jbin_object_delete_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_delete_deinit + +/*********************************************************************************/ +/* Returns an array of the Json object keys. */ +/*********************************************************************************/ +my_bool jbin_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_object_list_init(initid, args, message); +} // end of jbin_object_list_init + +char *jbin_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + PJAR jarp = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (!bsp || bsp->Changed) { + if (!CheckMemory(g, initid, args, 1, false)) { + char *p; + PJSON jsp; + PJVAL jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (jsp->GetType() == TYPE_JOB) { + jarp = ((PJOB)jsp)->GetKeyList(g); + } else { + PUSH_WARNING("First argument is not an object"); + if (g->Mrr) *error = 1; + } // endif jsp type + + } // endif CheckMemory + + bsp = JbinAlloc(g, args, initid->max_length, jarp); + strcat(bsp->Msg, " array"); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? bsp : NULL; + } // endif bsp + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_object_list + +void jbin_object_list_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_object_list_deinit + +/*********************************************************************************/ +/* Get a Json item from a Json document. */ +/*********************************************************************************/ +my_bool jbin_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_get_item_init(initid, args, message); +} // end of jbin_get_item_init + +char *jbin_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = NULL; + + if (g->N) { + bsp = (PBSON)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + jvp = MakeValue(g, args, 0); + + if ((p = jvp->GetString())) { + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // endif jsp + + } else + jsp = jvp->GetJson(); + + if (g->Mrr) { // First argument is a constant + g->Xchk = jsp; + JsonMemSave(g); + } // endif Mrr + + } else + jsp = (PJSON)g->Xchk; + + path = MakePSZ(g, args, 1); + jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length); + + if (jsx->SetJpath(g, path, true)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return NULL; + } // endif SetJpath + + // Get the json tree + jvp = jsx->GetValue(g, jsp, 0, false); + + if (jvp->GetJsp()) { + bsp = JbinAlloc(g, args, initid->max_length, jvp->GetJsp()); + strcat(bsp->Msg, " item"); + } // end of Jsp + + if (initid->const_item) + // Keep result of constant function + g->Activityp = (PACTIVITY)bsp; + + } // endif CheckMemory + +fin: + if (!bsp) { + *is_null = 1; + *res_length = 0; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_get_item + +void jbin_get_item_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_get_item_deinit + +/*********************************************************************************/ +/* Merge two arrays or objects. */ +/*********************************************************************************/ +my_bool jbin_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return json_item_merge_init(initid, args, message); +} // end of jbin_item_merge_init + +char *jbin_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *error) +{ + PJSON top = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) { + // This constant function was recalled + *res_length = sizeof(BSON); + return (char*)bsp; + } // endif bsp + + if (!CheckMemory(g, initid, args, 2, false, true)) { + PJVAL jvp; + PJSON jsp[2] = {NULL, NULL}; + + for (int i = 0; i < 2; i++) { + jvp = MakeValue(g, args, i); + if (!i) top = jvp->GetJson(); + + if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) { + sprintf(g->Message, "Argument %d is not an array or object", i); + PUSH_WARNING(g->Message); + } else + jsp[i] = jvp->GetJsp(); + + } // endfor i + + if (jsp[0] && jsp[0]->Merge(g, jsp[1])) + PUSH_WARNING(g->Message); + + } // endif CheckMemory + + // In case of error unchanged first argument will be returned + bsp = MakeBinResult(g, args, top, initid->max_length); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + + *res_length = sizeof(BSON); + return (char*)bsp; +} // end of jbin_item_merge + +void jbin_item_merge_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_item_merge_deinit + +/*********************************************************************************/ +/* Returns a json file as a json item. */ +/*********************************************************************************/ +my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen, fl, more = 1024; + + if (args->arg_count < 1 || args->arg_count > 4) { + strcpy(message, "This function only accepts 1 to 4 arguments"); + return true; + } else if (args->arg_type[0] != STRING_RESULT || !args->args[0]) { + strcpy(message, "First argument must be a constant string (file name)"); + return true; + } else if (args->arg_count > 1 && args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string (path)"); + return true; + } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) { + strcpy(message, "Third argument is not an integer (pretty)"); + return true; + } else if (args->arg_count > 3) { + if (args->arg_type[3] != INT_RESULT) { + strcpy(message, "Fourth argument is not an integer (memory)"); + return true; + } else + more += (ulong)*(longlong*)args->args[3]; + + } // endifs + + initid->maybe_null = 1; + CalcLen(args, false, reslen, memlen); + fl = GetFileLength(args->args[0]); + reslen += fl; + more += fl * M; + memlen += more; + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of jbin_file_init + +char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *error) +{ + char *fn; + int pretty, len = 0, pty = 3; + PJSON jsp; + PJVAL jvp = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = (PBSON)g->Xchk; + + if (bsp && !bsp->Changed) + goto fin; + + PlugSubSet(g, g->Sarea, g->Sarea_Size); + g->Xchk = NULL; + fn = MakePSZ(g, args, 0); + pretty = (args->arg_count > 2 && args->args[2]) ? (int)*(longlong*)args->args[2] : 3; + + /*********************************************************************************/ + /* Parse the json file and allocate its tree structure. */ + /*********************************************************************************/ + if (!(jsp = ParseJsonFile(g, fn, &pty, len))) { + PUSH_WARNING(g->Message); + *error = 1; + goto fin; + } // endif jsp + + if (pty == 3) + PUSH_WARNING("File pretty format cannot be determined"); + else if (pretty != 3 && pty != pretty) + PUSH_WARNING("File pretty format doesn't match the specified pretty value"); + else if (pretty == 3) + pretty = pty; + + bsp = JbinAlloc(g, args, len, jsp); + strcat(bsp->Msg, " file"); + bsp->Filename = fn; + bsp->Pretty = pretty; + + // Check whether a path was specified + if (CheckPath(g, args, jsp, jvp, 1)) { + PUSH_WARNING(g->Message); + bsp = NULL; + goto fin; + } else if (jvp) + bsp->Jsp = jvp->GetJsp(); + + if (initid->const_item) + // Keep result of constant function + g->Xchk = bsp; + +fin: + if (!bsp) { + *res_length = 0; + *is_null = 1; + } else + *res_length = sizeof(BSON); + + return (char*)bsp; +} // end of jbin_file + +void jbin_file_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of jbin_file_deinit + +/*********************************************************************************/ +/* Serialize a Json document. . */ +/*********************************************************************************/ +my_bool json_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + unsigned long reslen, memlen; + + if (args->arg_count != 1) { + strcpy(message, "This function must have 1 argument"); + return true; + } else if (IsJson(args, 0) != 3) { + strcpy(message, "Argument must be a Jbin tree"); + return true; + } else + CalcLen(args, false, reslen, memlen); + + return JsonInit(initid, args, message, false, reslen, memlen); +} // end of json_serialize_init + +char *json_serialize(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + char *str; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (!g->Xchk) { + PBSON bsp = (PBSON)args->args[0]; + + JsonSubSet(g); + + if (!(str = Serialize(g, bsp->Jsp, NULL, 0))) + str = strcpy(result, g->Message); + + // Keep result of constant function + g->Xchk = (initid->const_item) ? str : NULL; + } else + str = (char*)g->Xchk; + + *res_length = strlen(str); + return str; +} // end of json_serialize + +void json_serialize_deinit(UDF_INIT* initid) +{ + JsonFreeMem((PGLOBAL)initid->ptr); +} // end of json_serialize_deinit diff --git a/storage/connect/jsonudf.h b/storage/connect/jsonudf.h new file mode 100644 index 00000000000..0903b789fb6 --- /dev/null +++ b/storage/connect/jsonudf.h @@ -0,0 +1,247 @@ +/******************** tabjson H Declares Source Code File (.H) *******************/ +/* Name: jsonudf.h Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2015 */ +/* */ +/* This file contains the JSON UDF function and class declares. */ +/*********************************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "block.h" +#include "osutil.h" +#include "maputil.h" +#include "json.h" + +#define UDF_EXEC_ARGS \ + UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char* + +/*********************************************************************************/ +/* The JSON tree node. Can be an Object or an Array. */ +/*********************************************************************************/ +typedef struct _jnode { + PSZ Key; // The key used for object + OPVAL Op; // Operator used for this node + PVAL CncVal; // To cont value used for OP_CNC + PVAL Valp; // The internal array VALUE + int Rank; // The rank in array + int Rx; // Read row number + int Nx; // Next to read row number +} JNODE, *PJNODE; + +typedef class JSNX *PJSNX; +typedef class JOUTPATH *PJTP; +typedef class JOUTALL *PJTA; + +extern "C" { + DllExport my_bool jsonvalue_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jsonvalue(UDF_EXEC_ARGS); + DllExport void jsonvalue_deinit(UDF_INIT*); + + DllExport my_bool json_array_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_array(UDF_EXEC_ARGS); + DllExport void json_array_deinit(UDF_INIT*); + + DllExport my_bool json_array_add_values_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_array_add_values(UDF_EXEC_ARGS); + DllExport void json_array_add_values_deinit(UDF_INIT*); + + DllExport my_bool json_array_add_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_array_add(UDF_EXEC_ARGS); + DllExport void json_array_add_deinit(UDF_INIT*); + + DllExport my_bool json_array_delete_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_array_delete(UDF_EXEC_ARGS); + DllExport void json_array_delete_deinit(UDF_INIT*); + + DllExport my_bool json_object_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_object(UDF_EXEC_ARGS); + DllExport void json_object_deinit(UDF_INIT*); + + DllExport my_bool json_object_nonull_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_object_nonull(UDF_EXEC_ARGS); + DllExport void json_object_nonull_deinit(UDF_INIT*); + + DllExport my_bool json_object_add_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_object_add(UDF_EXEC_ARGS); + DllExport void json_object_add_deinit(UDF_INIT*); + + DllExport my_bool json_object_delete_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_object_delete(UDF_EXEC_ARGS); + DllExport void json_object_delete_deinit(UDF_INIT*); + + DllExport my_bool json_object_list_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_object_list(UDF_EXEC_ARGS); + DllExport void json_object_list_deinit(UDF_INIT*); + + DllExport my_bool json_array_grp_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport void json_array_grp_add(UDF_INIT *, UDF_ARGS *, char *, char *); + DllExport char *json_array_grp(UDF_EXEC_ARGS); + DllExport void json_array_grp_clear(UDF_INIT *, char *, char *); + DllExport void json_array_grp_deinit(UDF_INIT*); + + DllExport my_bool json_object_grp_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport void json_object_grp_add(UDF_INIT *, UDF_ARGS *, char *, char *); + DllExport char *json_object_grp(UDF_EXEC_ARGS); + DllExport void json_object_grp_clear(UDF_INIT *, char *, char *); + DllExport void json_object_grp_deinit(UDF_INIT*); + + DllExport my_bool json_item_merge_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_item_merge(UDF_EXEC_ARGS); + DllExport void json_item_merge_deinit(UDF_INIT*); + + DllExport my_bool json_get_item_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_get_item(UDF_EXEC_ARGS); + DllExport void json_get_item_deinit(UDF_INIT*); + + DllExport my_bool jsonget_string_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jsonget_string(UDF_EXEC_ARGS); + DllExport void jsonget_string_deinit(UDF_INIT*); + + DllExport my_bool jsonget_int_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport long long jsonget_int(UDF_INIT*, UDF_ARGS*, char*, char*); + DllExport void jsonget_int_deinit(UDF_INIT*); + + DllExport my_bool jsonget_real_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport double jsonget_real(UDF_INIT*, UDF_ARGS*, char*, char*); + DllExport void jsonget_real_deinit(UDF_INIT*); + + DllExport my_bool jsonlocate_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jsonlocate(UDF_EXEC_ARGS); + DllExport void jsonlocate_deinit(UDF_INIT*); + + DllExport my_bool json_locate_all_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_locate_all(UDF_EXEC_ARGS); + DllExport void json_locate_all_deinit(UDF_INIT*); + + DllExport my_bool json_file_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_file(UDF_EXEC_ARGS); + DllExport void json_file_deinit(UDF_INIT*); + + DllExport my_bool jfile_make_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jfile_make(UDF_EXEC_ARGS); + DllExport void jfile_make_deinit(UDF_INIT*); + + DllExport my_bool jbin_array_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_array(UDF_EXEC_ARGS); + DllExport void jbin_array_deinit(UDF_INIT*); + + DllExport my_bool jbin_array_add_values_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_array_add_values(UDF_EXEC_ARGS); + DllExport void jbin_array_add_values_deinit(UDF_INIT*); + + DllExport my_bool jbin_array_add_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_array_add(UDF_EXEC_ARGS); + DllExport void jbin_array_add_deinit(UDF_INIT*); + + DllExport my_bool jbin_array_delete_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_array_delete(UDF_EXEC_ARGS); + DllExport void jbin_array_delete_deinit(UDF_INIT*); + + DllExport my_bool jbin_object_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_object(UDF_EXEC_ARGS); + DllExport void jbin_object_deinit(UDF_INIT*); + + DllExport my_bool jbin_object_nonull_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_object_nonull(UDF_EXEC_ARGS); + DllExport void jbin_object_nonull_deinit(UDF_INIT*); + + DllExport my_bool jbin_object_add_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_object_add(UDF_EXEC_ARGS); + DllExport void jbin_object_add_deinit(UDF_INIT*); + + DllExport my_bool jbin_object_delete_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_object_delete(UDF_EXEC_ARGS); + DllExport void jbin_object_delete_deinit(UDF_INIT*); + + DllExport my_bool jbin_object_list_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_object_list(UDF_EXEC_ARGS); + DllExport void jbin_object_list_deinit(UDF_INIT*); + + DllExport my_bool jbin_get_item_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_get_item(UDF_EXEC_ARGS); + DllExport void jbin_get_item_deinit(UDF_INIT*); + + DllExport my_bool jbin_item_merge_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_item_merge(UDF_EXEC_ARGS); + DllExport void jbin_item_merge_deinit(UDF_INIT*); + + DllExport my_bool jbin_file_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *jbin_file(UDF_EXEC_ARGS); + DllExport void jbin_file_deinit(UDF_INIT*); + + DllExport my_bool json_serialize_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *json_serialize(UDF_EXEC_ARGS); + DllExport void json_serialize_deinit(UDF_INIT*); +} // extern "C" + +/*********************************************************************************/ +/* Structure JPN. Used to make the locate path. */ +/*********************************************************************************/ +typedef struct _jpn { + enum JTYP Type; + PSZ Key; + int N; +} JPN, *PJPN; + +/*********************************************************************************/ +/* Class JSNX: JSON access method. */ +/*********************************************************************************/ +class JSNX : public BLOCK { +public: + // Constructors + JSNX(PGLOBAL g, PJSON row, int type, int len = 64, int prec = 0); + + // Implementation + int GetPrecision(void) {return Prec;} + PVAL GetValue(void) {return Value;} + + // Methods + my_bool SetJpath(PGLOBAL g, char *path, my_bool jb = false); + my_bool ParseJpath(PGLOBAL g); + void ReadValue(PGLOBAL g); + PJVAL GetValue(PGLOBAL g, PJSON row, int i, my_bool b = true); + PJVAL GetJson(PGLOBAL g); + char *Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k = 1); + char *LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx = 10); + +protected: + my_bool SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm); + PVAL GetColumnValue(PGLOBAL g, PJSON row, int i); + PVAL ExpandArray(PGLOBAL g, PJAR arp, int n); + PVAL CalculateArray(PGLOBAL g, PJAR arp, int n); + PVAL MakeJson(PGLOBAL g, PJSON jsp); + void SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n); + my_bool LocateArray(PJAR jarp); + my_bool LocateObject(PJOB jobp); + my_bool LocateValue(PJVAL jvp); + my_bool LocateArrayAll(PJAR jarp); + my_bool LocateObjectAll(PJOB jobp); + my_bool LocateValueAll(PJVAL jvp); + my_bool CompareTree(PJSON jp1, PJSON jp2); + my_bool AddPath(void); + + // Default constructor not to be used + JSNX(void) {} + + // Members + PJSON Row; + PJVAL Jvalp; + PJPN Jpnp; + JOUTSTR *Jp; + JNODE *Nodes; // The intermediate objects + PVAL Value; + PVAL MulVal; // To value used by multiple column + char *Jpath; // The json path + int Buf_Type; + int Long; + int Prec; + int Nod; // The number of intermediate objects + int Xnod; // Index of multiple values + int K; // Kth item to locate + int I; // Index of JPN + int Imax; // Max number of JPN's + int B; // Index base + my_bool Xpd; // True for expandable column + my_bool Parsed; // True when parsed + my_bool Found; // Item found by locate +}; // end of class JSNX diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index 9c72e9cd665..76a00c7e7f6 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -299,13 +299,13 @@ int GetIndexType(TABTYPE type) xtyp= 1; break; case TAB_MYSQL: -// case TAB_ODBC: + case TAB_ODBC: xtyp= 2; break; case TAB_VIR: xtyp= 3; break; - case TAB_ODBC: +// case TAB_ODBC: default: xtyp= 0; break; diff --git a/storage/connect/mysql-test/connect/r/datest.result b/storage/connect/mysql-test/connect/r/datest.result index 203a7419a8e..586741f09ad 100644 --- a/storage/connect/mysql-test/connect/r/datest.result +++ b/storage/connect/mysql-test/connect/r/datest.result @@ -30,3 +30,30 @@ SELECT id, TIME(tim) FROM t1 LIMIT 1; id TIME(tim) 1 09:35:08.000000 DROP TABLE t1; +# +# Testing use of dates in where clause (MDEV-8926) +# +CREATE TABLE t1 (col1 DATE) ENGINE=CONNECT TABLE_TYPE=CSV; +Warnings: +Warning 1105 No file name. Table will use t1.csv +INSERT INTO t1 VALUES('2015-01-01'),('2015-02-01'),('2015-03-01'),('2015-04-01'); +SELECT * FROM t1 WHERE col1 = '2015-02-01'; +col1 +2015-02-01 +SELECT * FROM t1 WHERE col1 > '2015-02-01'; +col1 +2015-03-01 +2015-04-01 +SELECT * FROM t1 WHERE col1 >= '2015-02-01'; +col1 +2015-02-01 +2015-03-01 +2015-04-01 +SELECT * FROM t1 WHERE col1 < '2015-02-01'; +col1 +2015-01-01 +SELECT * FROM t1 WHERE col1 <= '2015-02-01'; +col1 +2015-01-01 +2015-02-01 +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/json.result b/storage/connect/mysql-test/connect/r/json.result index acb74c38e26..aa6b04c58c7 100644 --- a/storage/connect/mysql-test/connect/r/json.result +++ b/storage/connect/mysql-test/connect/r/json.result @@ -171,6 +171,40 @@ line ] DROP TABLE t1; # +# Testing a pretty=0 file +# +CREATE TABLE t1 +( +ISBN CHAR(15) NOT NULL, +Language CHAR(2) FIELD_FORMAT='LANG', +Subject CHAR(32) FIELD_FORMAT='SUBJECT', +AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME', +AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME', +Title CHAR(32) FIELD_FORMAT='TITLE', +Translation CHAR(32) FIELD_FORMAT='TRANSLATED:PREFIX', +TranslatorFN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:FIRSTNAME', +TranslatorLN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:LASTNAME', +Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME', +Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE', +Year int(4) FIELD_FORMAT='DATEPUB', +INDEX IX(ISBN) +) +ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bib0.json' LRECL=320 OPTION_LIST='Pretty=0'; +SHOW INDEX FROM t1; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment +t1 1 IX 1 ISBN A NULL NULL NULL XINDEX +SELECT * FROM t1; +ISBN Language Subject AuthorFN AuthorLN Title Translation TranslatorFN TranslatorLN Publisher Location Year +9782212090819 fr applications Jean-Michel Bernadac Construire une application XML NULL NULL NULL Eyrolles Paris 1999 +9782212090819 fr applications François Knab Construire une application XML NULL NULL NULL Eyrolles Paris 1999 +9782840825685 fr applications William J. Pardi XML en Action adapté de l'anglais par James Guerin Microsoft Press Paris 2001 +DESCRIBE SELECT * FROM t1 WHERE ISBN = '9782212090819'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref IX IX 15 const 1 Using where +UPDATE t1 SET AuthorFN = 'Philippe' WHERE ISBN = '9782212090819'; +ERROR HY000: Got error 122 'Cannot write expanded column when Pretty is not 2' from CONNECT +DROP TABLE t1; +# # A file with 2 arrays # CREATE TABLE t1 ( diff --git a/storage/connect/mysql-test/connect/r/json_udf.result b/storage/connect/mysql-test/connect/r/json_udf.result index 1455bac9017..81acbea91a6 100644 --- a/storage/connect/mysql-test/connect/r/json_udf.result +++ b/storage/connect/mysql-test/connect/r/json_udf.result @@ -1,63 +1,147 @@ +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=VIR BLOCK_SIZE=5; # # Test UDF's with constant arguments # +SELECT JsonValue(56,3.1416,'foo',NULL); +ERROR HY000: Can't initialize function 'jsonvalue'; Cannot accept more than 1 argument +SELECT JsonValue(3.1416); +JsonValue(3.1416) +3.141600 +SELECT JsonValue('foo'); +JsonValue('foo') +"foo" +SELECT JsonValue(9223372036854775807); +JsonValue(9223372036854775807) +9223372036854775807 +SELECT JsonValue(NULL); +JsonValue(NULL) +null +SELECT JsonValue(TRUE); +JsonValue(TRUE) +true +SELECT JsonValue(FALSE); +JsonValue(FALSE) +false +SELECT JsonValue(); +JsonValue() +null +SELECT JsonValue('[11,22,33]' json_) FROM t1; +JsonValue('[11,22,33]' json_) +[11,22,33] +[11,22,33] +[11,22,33] +[11,22,33] +[11,22,33] SELECT Json_Array(); Json_Array() [] -SELECT Json_Object(56,3.1416,'foo',NULL); -Json_Object(56,3.1416,'foo',NULL) -{"56":56,"3.1416":3.141600,"foo":"foo","NULL":null} -SELECT Json_Object(56 qty,3.1416 price,'foo' truc, NULL garanty); -Json_Object(56 qty,3.1416 price,'foo' truc, NULL garanty) -{"qty":56,"price":3.141600,"truc":"foo","garanty":null} SELECT Json_Array(56,3.1416,'My name is "Foo"',NULL); Json_Array(56,3.1416,'My name is "Foo"',NULL) [56,3.141600,"My name is \"Foo\"",null] -SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL)) Array; -ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Value_Add must have at least 2 arguments -SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL),'One more') Array; -Array -[56,3.141600,"foo",null,"One more"] -SELECT Json_Array_Add(Json_Value('one value'),'One more'); -Json_Array_Add(Json_Value('one value'),'One more') -["one value","One more"] -SELECT Json_Array_Add('one value','One more'); -ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Value_Add first argument must be a json item -SELECT Json_Array_Add('one value' json_,'One more'); -Json_Array_Add('one value' json_,'One more') -[null,"One more"] -Warnings: -Warning 1105 Bad 'o' character near one value -SELECT Json_Value(56,3.1416,'foo',NULL); -ERROR HY000: Can't initialize function 'Json_Value'; Json_Value cannot accept more than 1 argument -SELECT Json_Value(3.1416); -Json_Value(3.1416) -3.141600 -SELECT Json_Value('foo'); -Json_Value('foo') -"foo" -SELECT Json_Value(NULL); -Json_Value(NULL) -null -SELECT Json_Value(); -Json_Value() -null -SELECT Json_Object(); -Json_Object() -{} -SELECT Json_Object(Json_Array(56,3.1416,'foo'),NULL); -Json_Object(Json_Array(56,3.1416,'foo'),NULL) -{"Array(56,3.1416,'foo')":[56,3.141600,"foo"],"NULL":null} SELECT Json_Array(Json_Array(56,3.1416,'foo'),NULL); Json_Array(Json_Array(56,3.1416,'foo'),NULL) [[56,3.141600,"foo"],null] -SELECT Json_Array(Json_Object(56 "qty",3.1416 "price",'foo'),NULL); -Json_Array(Json_Object(56 "qty",3.1416 "price",'foo'),NULL) +SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL)) Array; +ERROR HY000: Can't initialize function 'json_array_add'; This function must have at least 2 arguments +SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL),'One more') Array; +Array +[56,3.141600,"foo",null,"One more"] +SELECT Json_Array_Add(JsonValue('one value'),'One more'); +ERROR HY000: Can't initialize function 'json_array_add'; First argument must be a json item +SELECT Json_Array_Add('one value','One more'); +ERROR HY000: Can't initialize function 'json_array_add'; First argument must be a json item +SELECT Json_Array_Add('one value' json_,'One more'); +Json_Array_Add('one value' json_,'One more') +one value +Warnings: +Warning 1105 Error 2 opening one value +Warning 1105 First argument target is not an array +SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 0); +Json_Array_Add('[5,3,8,7,9]' json_, 4, 0) +[4,5,3,8,7,9] +SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 2) Array; +Array +[5,3,4,8,7,9] +SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 9); +Json_Array_Add('[5,3,8,7,9]' json_, 4, 9) +[5,3,8,7,9,4] +SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin', NULL), 'One more', 'Two more') Array; +Array +[56,3.141600,"machin",null,"One more","Two more"] +SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), 'One more', 'Two more') Array FROM t1; +Array +[56,3.141600,"machin","One more","Two more"] +[56,3.141600,"machin","One more","Two more"] +[56,3.141600,"machin","One more","Two more"] +[56,3.141600,"machin","One more","Two more"] +[56,3.141600,"machin","One more","Two more"] +SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), n) Array FROM t1; +Array +[56,3.141600,"machin",1] +[56,3.141600,"machin",2] +[56,3.141600,"machin",3] +[56,3.141600,"machin",4] +[56,3.141600,"machin",5] +SELECT Json_Array_Add_Values(Json_Array(n, 3.1416, 'machin'), n) Array FROM t1; +Array +[1,3.141600,"machin",1] +[2,3.141600,"machin",2] +[3,3.141600,"machin",3] +[4,3.141600,"machin",4] +[5,3.141600,"machin",5] +SELECT Json_Array_Add_Values('[56]', 3.1416, 'machin') Array; +Array +[56,3.141600,"machin"] +SELECT Json_Array_Delete(Json_Array(56,3.1416,'My name is "Foo"',NULL),0); +Json_Array_Delete(Json_Array(56,3.1416,'My name is "Foo"',NULL),0) +[3.141600,"My name is \"Foo\"",null] +SELECT Json_Array_Delete(Json_Object(56,3.1416,'My name is Foo',NULL),2); +Json_Array_Delete(Json_Object(56,3.1416,'My name is Foo',NULL),2) +{"56":56,"3.1416":3.141600,"My name is Foo":"My name is Foo","NULL":null} +Warnings: +Warning 1105 First argument target is not an array +SELECT Json_Array_Delete(Json_Array(56,3.1416,'My name is "Foo"',NULL),'2'); +Json_Array_Delete(Json_Array(56,3.1416,'My name is "Foo"',NULL),'2') +[56,3.141600,"My name is \"Foo\"",null] +Warnings: +Warning 1105 Missing or null array index +SELECT Json_Object(56, 3.1416, 'foo', NULL); +Json_Object(56, 3.1416, 'foo', NULL) +{"56":56,"3.1416":3.141600,"foo":"foo","NULL":null} +SELECT Json_Object(56 qty, 3.1416 price, 'foo' truc, NULL garanty); +Json_Object(56 qty, 3.1416 price, 'foo' truc, NULL garanty) +{"qty":56,"price":3.141600,"truc":"foo","garanty":null} +SELECT Json_Object(); +Json_Object() +{} +SELECT Json_Object(Json_Array(56, 3.1416, 'foo'), NULL); +Json_Object(Json_Array(56, 3.1416, 'foo'), NULL) +{"Array(56, 3.1416, 'foo')":[56,3.141600,"foo"],"NULL":null} +SELECT Json_Array(Json_Object(56 "qty", 3.1416 "price", 'foo') ,NULL); +Json_Array(Json_Object(56 "qty", 3.1416 "price", 'foo') ,NULL) [{"qty":56,"price":3.141600,"foo":"foo"},null] +SELECT Json_Object_Add(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'blue' color); +Json_Object_Add(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'blue' color) +{"qty":56,"price":3.141600,"truc":"machin","garanty":null,"color":"blue"} +SELECT Json_Object_Add(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 45.99 price); +Json_Object_Add(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 45.99 price) +{"qty":56,"price":45.990000,"truc":"machin","garanty":null} +SELECT Json_Object_Delete(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'truc'); +Json_Object_Delete(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'truc') +{"qty":56,"price":3.141600,"garanty":null} +SELECT Json_Object_Delete(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'chose'); +Json_Object_Delete(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'chose') +{"qty":56,"price":3.141600,"truc":"machin","garanty":null} +SELECT Json_Object_List(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty)) "Key List"; +Key List +["qty","price","truc","garanty"] +SELECT Json_Object_List('{"qty":56, "price":3.1416, "truc":"machin", "garanty":null}') "Key List"; +Key List +["qty","price","truc","garanty"] # # Test UDF's with column arguments # -CREATE TABLE t1 +CREATE TABLE t2 ( ISBN CHAR(15), LANG CHAR(2), @@ -69,21 +153,20 @@ TRANSLATOR CHAR(80), PUBLISHER CHAR(32), DATEPUB int(4) ) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.json'; -SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t1; +SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t2; Json_Array(AUTHOR, TITLE, DATEPUB) ["Jean-Christophe Bernadac","Construire une application XML",1999] ["William J. Pardi","XML en Action",1999] -SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t1; +SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t2; Json_Object(AUTHOR, TITLE, DATEPUB) {"AUTHOR":"Jean-Christophe Bernadac","TITLE":"Construire une application XML","DATEPUB":1999} {"AUTHOR":"William J. Pardi","TITLE":"XML en Action","DATEPUB":1999} -SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t1; -ERROR HY000: Can't initialize function 'Json_Array_Grp'; Json_Array_Grp can only accept 1 argument -SELECT Json_Array_Grp(TITLE) FROM t1; +SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t2; +ERROR HY000: Can't initialize function 'json_array_grp'; This function can only accept 1 argument +SELECT Json_Array_Grp(TITLE) FROM t2; Json_Array_Grp(TITLE) ["Construire une application XML","XML en Action"] -DROP TABLE t1; -CREATE TABLE t1 ( +CREATE TABLE t3 ( SERIALNO CHAR(5) NOT NULL, NAME VARCHAR(12) NOT NULL FLAG=6, SEX SMALLINT(1) NOT NULL, @@ -93,10 +176,10 @@ DEPARTMENT CHAr(4) NOT NULL FLAG=41, SECRETARY CHAR(5) DEFAULT NULL FLAG=46, SALARY DOUBLE(8,2) NOT NULL FLAG=52 ) ENGINE=CONNECT TABLE_TYPE=FIX BLOCK_SIZE=8 FILE_NAME='employee.dat' ENDING=1; -SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t1 WHERE NAME = 'MERCHANT'; +SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t3 WHERE NAME = 'MERCHANT'; Json_Object(SERIALNO, NAME, TITLE, SALARY) {"SERIALNO":"78943","NAME":"MERCHANT","TITLE":"SALESMAN","SALARY":8700.000000} -SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t1 GROUP BY DEPARTMENT; +SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t3 GROUP BY DEPARTMENT; DEPARTMENT Json_Array_Grp(NAME) 0021 ["STRONG","SHORTSIGHT"] 0318 ["BANCROFT","PLUMHEAD","HONEY","TONGHO","WALTER","SHRINKY","WERTHER","MERCHANT","WHEELFOR"] @@ -104,26 +187,26 @@ DEPARTMENT Json_Array_Grp(NAME) 2452 ["BIGHEAD","ORELLY","BIGHORN","SMITH","CHERRY"] Warnings: Warning 1105 Result truncated to json_grp_size values -set connect_json_grp_size=30; -SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t1 GROUP BY DEPARTMENT; +SET connect_json_grp_size=30; +SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t3 GROUP BY DEPARTMENT; Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) ["0021",["STRONG","SHORTSIGHT"]] ["0318",["BANCROFT","PLUMHEAD","HONEY","TONGHO","WALTER","SHRINKY","WERTHER","MERCHANT","WHEELFOR"]] ["0319",["BULLOZER","QUINN","BROWNY","KITTY","MONAPENNY","MARTIN","FUNNIGUY","BUGHAPPY","FODDERMAN","MESSIFUL","GOOSEPEN"]] ["2452",["BIGHEAD","ORELLY","BIGHORN","SMITH","CHERRY"]] -SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t1 GROUP BY DEPARTMENT; +SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t3 GROUP BY DEPARTMENT; Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) {"DEPARTMENT":"0021","NAMES":["STRONG","SHORTSIGHT"]} {"DEPARTMENT":"0318","NAMES":["BANCROFT","PLUMHEAD","HONEY","TONGHO","WALTER","SHRINKY","WERTHER","MERCHANT","WHEELFOR"]} {"DEPARTMENT":"0319","NAMES":["BULLOZER","QUINN","BROWNY","KITTY","MONAPENNY","MARTIN","FUNNIGUY","BUGHAPPY","FODDERMAN","MESSIFUL","GOOSEPEN"]} {"DEPARTMENT":"2452","NAMES":["BIGHEAD","ORELLY","BIGHORN","SMITH","CHERRY"]} -SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT; +SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT; Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) {"DEPARTMENT":"0021","EMPLOYES":[{"SERIALNO":"87777","NAME":"STRONG","TITLE":"DIRECTOR","SALARY":23000.000000},{"SERIALNO":"22222","NAME":"SHORTSIGHT","TITLE":"SECRETARY","SALARY":5500.000000}]} {"DEPARTMENT":"0318","EMPLOYES":[{"SERIALNO":"74200","NAME":"BANCROFT","TITLE":"SALESMAN","SALARY":9600.000000},{"SERIALNO":"24888","NAME":"PLUMHEAD","TITLE":"TYPIST","SALARY":2800.000000},{"SERIALNO":"27845","NAME":"HONEY","TITLE":"SECRETARY","SALARY":4900.000000},{"SERIALNO":"73452","NAME":"TONGHO","TITLE":"ENGINEER","SALARY":6800.000000},{"SERIALNO":"74234","NAME":"WALTER","TITLE":"ENGINEER","SALARY":7400.000000},{"SERIALNO":"77777","NAME":"SHRINKY","TITLE":"ADMINISTRATOR","SALARY":7500.000000},{"SERIALNO":"70012","NAME":"WERTHER","TITLE":"DIRECTOR","SALARY":14500.000000},{"SERIALNO":"78943","NAME":"MERCHANT","TITLE":"SALESMAN","SALARY":8700.000000},{"SERIALNO":"73111","NAME":"WHEELFOR","TITLE":"SALESMAN","SALARY":10030.000000}]} {"DEPARTMENT":"0319","EMPLOYES":[{"SERIALNO":"76543","NAME":"BULLOZER","TITLE":"SALESMAN","SALARY":14800.000000},{"SERIALNO":"40567","NAME":"QUINN","TITLE":"DIRECTOR","SALARY":14000.000000},{"SERIALNO":"00137","NAME":"BROWNY","TITLE":"ENGINEER","SALARY":10500.000000},{"SERIALNO":"12345","NAME":"KITTY","TITLE":"TYPIST","SALARY":3000.450000},{"SERIALNO":"33333","NAME":"MONAPENNY","TITLE":"SECRETARY","SALARY":3800.000000},{"SERIALNO":"00023","NAME":"MARTIN","TITLE":"ENGINEER","SALARY":10000.000000},{"SERIALNO":"07654","NAME":"FUNNIGUY","TITLE":"ADMINISTRATOR","SALARY":8500.000000},{"SERIALNO":"45678","NAME":"BUGHAPPY","TITLE":"PROGRAMMER","SALARY":8500.000000},{"SERIALNO":"56789","NAME":"FODDERMAN","TITLE":"SALESMAN","SALARY":7000.000000},{"SERIALNO":"55555","NAME":"MESSIFUL","TITLE":"SECRETARY","SALARY":5000.500000},{"SERIALNO":"98765","NAME":"GOOSEPEN","TITLE":"ADMINISTRATOR","SALARY":4700.000000}]} {"DEPARTMENT":"2452","EMPLOYES":[{"SERIALNO":"34567","NAME":"BIGHEAD","TITLE":"SCIENTIST","SALARY":8000.000000},{"SERIALNO":"31416","NAME":"ORELLY","TITLE":"ENGINEER","SALARY":13400.000000},{"SERIALNO":"36666","NAME":"BIGHORN","TITLE":"SCIENTIST","SALARY":11000.000000},{"SERIALNO":"02345","NAME":"SMITH","TITLE":"ENGINEER","SALARY":9000.000000},{"SERIALNO":"11111","NAME":"CHERRY","TITLE":"SECRETARY","SALARY":4500.000000}]} -SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT, TITLE; +SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT, TITLE; Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) {"DEPARTMENT":"0021","TITLE":"DIRECTOR","EMPLOYES":[{"SERIALNO":"87777","NAME":"STRONG","SALARY":23000.000000}]} {"DEPARTMENT":"0021","TITLE":"SECRETARY","EMPLOYES":[{"SERIALNO":"22222","NAME":"SHORTSIGHT","SALARY":5500.000000}]} @@ -143,25 +226,372 @@ Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY {"DEPARTMENT":"2452","TITLE":"ENGINEER","EMPLOYES":[{"SERIALNO":"31416","NAME":"ORELLY","SALARY":13400.000000},{"SERIALNO":"02345","NAME":"SMITH","SALARY":9000.000000}]} {"DEPARTMENT":"2452","TITLE":"SCIENTIST","EMPLOYES":[{"SERIALNO":"34567","NAME":"BIGHEAD","SALARY":8000.000000},{"SERIALNO":"36666","NAME":"BIGHORN","SALARY":11000.000000}]} {"DEPARTMENT":"2452","TITLE":"SECRETARY","EMPLOYES":[{"SERIALNO":"11111","NAME":"CHERRY","SALARY":4500.000000}]} -SELECT Json_Object_Grp(SALARY) FROM t1; -ERROR HY000: Can't initialize function 'Json_Object_Grp'; Json_Array_Grp can only accept 2 arguments -SELECT Json_Object_Grp(SALARY, NAME) FROM t1; +SELECT Json_Object_Grp(SALARY) FROM t3; +ERROR HY000: Can't initialize function 'json_object_grp'; This function requires 2 arguments (value, key) +SELECT Json_Object_Grp(SALARY, NAME) FROM t3; Json_Object_Grp(SALARY, NAME) {"BANCROFT":9600.000000,"SMITH":9000.000000,"MERCHANT":8700.000000,"FUNNIGUY":8500.000000,"BUGHAPPY":8500.000000,"BIGHEAD":8000.000000,"SHRINKY":7500.000000,"WALTER":7400.000000,"FODDERMAN":7000.000000,"TONGHO":6800.000000,"SHORTSIGHT":5500.000000,"MESSIFUL":5000.500000,"HONEY":4900.000000,"GOOSEPEN":4700.000000,"CHERRY":4500.000000,"MONAPENNY":3800.000000,"KITTY":3000.450000,"PLUMHEAD":2800.000000,"STRONG":23000.000000,"BULLOZER":14800.000000,"WERTHER":14500.000000,"QUINN":14000.000000,"ORELLY":13400.000000,"BIGHORN":11000.000000,"BROWNY":10500.000000,"WHEELFOR":10030.000000,"MARTIN":10000.000000} -SELECT Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") FROM t1 GROUP BY DEPARTMENT; +SELECT Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") FROM t3 GROUP BY DEPARTMENT; Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") {"DEPARTMENT":"0021","SALARIES":{"STRONG":23000.000000,"SHORTSIGHT":5500.000000}} {"DEPARTMENT":"0318","SALARIES":{"BANCROFT":9600.000000,"PLUMHEAD":2800.000000,"HONEY":4900.000000,"TONGHO":6800.000000,"WALTER":7400.000000,"SHRINKY":7500.000000,"WERTHER":14500.000000,"MERCHANT":8700.000000,"WHEELFOR":10030.000000}} {"DEPARTMENT":"0319","SALARIES":{"BULLOZER":14800.000000,"QUINN":14000.000000,"BROWNY":10500.000000,"KITTY":3000.450000,"MONAPENNY":3800.000000,"MARTIN":10000.000000,"FUNNIGUY":8500.000000,"BUGHAPPY":8500.000000,"FODDERMAN":7000.000000,"MESSIFUL":5000.500000,"GOOSEPEN":4700.000000}} {"DEPARTMENT":"2452","SALARIES":{"BIGHEAD":8000.000000,"ORELLY":13400.000000,"BIGHORN":11000.000000,"SMITH":9000.000000,"CHERRY":4500.000000}} -SELECT Json_Array_Grp(NAME) from t1; +SELECT Json_Array_Grp(NAME) FROM t3; Json_Array_Grp(NAME) ["BANCROFT","SMITH","MERCHANT","FUNNIGUY","BUGHAPPY","BIGHEAD","SHRINKY","WALTER","FODDERMAN","TONGHO","SHORTSIGHT","MESSIFUL","HONEY","GOOSEPEN","CHERRY","MONAPENNY","KITTY","PLUMHEAD","STRONG","BULLOZER","WERTHER","QUINN","ORELLY","BIGHORN","BROWNY","WHEELFOR","MARTIN"] +# +# Test value getting UDF's +# +SELECT JsonGet_String(Json_Array_Grp(name),'[#]') FROM t3; +JsonGet_String(Json_Array_Grp(name),'[#]') +27 +SELECT JsonGet_String(Json_Array_Grp(name),'[","]') FROM t3; +JsonGet_String(Json_Array_Grp(name),'[","]') +BANCROFT,SMITH,MERCHANT,FUNNIGUY,BUGHAPPY,BIGHEAD,SHRINKY,WALTER,FODDERMAN,TONGHO,SHORTSIGHT,MESSIFUL,HONEY,GOOSEPEN,CHERRY,MONAPENNY,KITTY,PLUMHEAD,STRONG,BULLOZER,WERTHER,QUINN,ORELLY,BIGHORN,BROWNY,WHEELFOR,MARTIN +SELECT JsonGet_String(Json_Array_Grp(name),'[>]') FROM t3; +JsonGet_String(Json_Array_Grp(name),'[>]') +WHEELFOR +SET @j1 = '[45,28,36,45,89]'; +SELECT JsonGet_String(@j1,'[1]'); +JsonGet_String(@j1,'[1]') +28 +SELECT JsonGet_String(@j1 json_,'[3]'); +JsonGet_String(@j1 json_,'[3]') +45 +SELECT JsonGet_String(Json_Array(45,28,36,45,89),'[3]'); +JsonGet_String(Json_Array(45,28,36,45,89),'[3]') +45 +SELECT JsonGet_String(Json_Array(45,28,36,45,89),'["+"]') "list",'=' as "egal",JsonGet_String(Json_Array(45,28,36,45,89),'[+]') "sum"; +list egal sum +45+28+36+45+89 = 243.00 +SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]'); +JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]') +36 +SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:*'); +JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:*') +[36,45,89] +SELECT JsonGet_String(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'truc'); +JsonGet_String(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'truc') +machin +SET @j2 = '{"qty":56,"price":3.141600,"truc":"machin","garanty":null}'; +SELECT JsonGet_String(@j2 json_,'truc'); +JsonGet_String(@j2 json_,'truc') +machin +SELECT JsonGet_String(@j2,'truc'); +JsonGet_String(@j2,'truc') +machin +SELECT JsonGet_String(@j2,'chose'); +JsonGet_String(@j2,'chose') +NULL +SELECT JsonGet_String(NULL json_, NULL); +JsonGet_String(NULL json_, NULL) +NULL +Warnings: +Warning 1105 +SELECT department, JsonGet_String(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department; +department Sumsal +0021 28500.00 +0318 72230.00 +0319 89800.95 +2452 45900.00 +SELECT JsonGet_Int(@j1, '[4]'); +JsonGet_Int(@j1, '[4]') +89 +SELECT JsonGet_Int(@j1, '[#]'); +JsonGet_Int(@j1, '[#]') +5 +SELECT JsonGet_Int(@j1, '[+]'); +JsonGet_Int(@j1, '[+]') +243 +SELECT JsonGet_Int(@j1 json_,'[3]'); +JsonGet_Int(@j1 json_,'[3]') +45 +SELECT JsonGet_Int(Json_Array(45,28,36,45,89),'[3]'); +JsonGet_Int(Json_Array(45,28,36,45,89),'[3]') +45 +SELECT JsonGet_Int(Json_Array(45,28,36,45,89),'["+"]'); +JsonGet_Int(Json_Array(45,28,36,45,89),'["+"]') +45 +SELECT JsonGet_Int(Json_Array(45,28,36,45,89),'[+]'); +JsonGet_Int(Json_Array(45,28,36,45,89),'[+]') +243 +SELECT JsonGet_Int(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]'); +JsonGet_Int(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]') +36 +SELECT JsonGet_Int(Json_Array(json_array(45,28),json_array(36,45,89)),'[0]:[1]'); +JsonGet_Int(Json_Array(json_array(45,28),json_array(36,45,89)),'[0]:[1]') +28 +SELECT JsonGet_Int(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'qty'); +JsonGet_Int(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'qty') +56 +SELECT JsonGet_Int(@j2 json_,'price'); +JsonGet_Int(@j2 json_,'price') +3 +SELECT JsonGet_Int(@j2,'qty'); +JsonGet_Int(@j2,'qty') +56 +SELECT JsonGet_Int('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','chose'); +JsonGet_Int('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','chose') +NULL +Warnings: +Warning 1105 Value not found +SELECT JsonGet_Int(JsonGet_String(Json_Array(Json_Array(45,28),Json_Array(36,45,89)),'[1]:*'),'[+]') sum; +sum +170 +SELECT department, JsonGet_Int(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department; +department Sumsal +0021 28500 +0318 72230 +0319 89800 +2452 45900 +SELECT JsonGet_Real(@j1, '[2]'); +JsonGet_Real(@j1, '[2]') +36.000000000000000 +SELECT JsonGet_Real(@j1 json_,'[3]',2); +JsonGet_Real(@j1 json_,'[3]',2) +45.00 +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'[3]'); +JsonGet_Real(Json_Array(45,28,36,45,89),'[3]') +45.000000000000000 +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'["+"]'); +JsonGet_Real(Json_Array(45,28,36,45,89),'["+"]') +45.000000000000000 +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'[+]'); +JsonGet_Real(Json_Array(45,28,36,45,89),'[+]') +243.000000000000000 +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'[!]'); +JsonGet_Real(Json_Array(45,28,36,45,89),'[!]') +48.600000000000000 +SELECT JsonGet_Real(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]'); +JsonGet_Real(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]') +36.000000000000000 +SELECT JsonGet_Real(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'price'); +JsonGet_Real(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'price') +3.141600000000000 +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}' json_,'qty'); +JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}' json_,'qty') +56.000000000000000 +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','price'); +JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','price') +3.141600000000000 +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','price', 4); +JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','price', 4) +3.1416 +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','chose'); +JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','chose') +NULL +Warnings: +Warning 1105 Value not found +SELECT department, JsonGet_Real(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department; +department Sumsal +0021 28500.000000000000000 +0318 72230.000000000000000 +0319 89800.950000000000000 +2452 45900.000000000000000 +# +# Documentation examples +# +SELECT +JsonGet_Int(Json_Array(45,28,36,45,89), '[4]') "Rank", +JsonGet_Int(Json_Array(45,28,36,45,89), '[#]') "Number", +JsonGet_String(Json_Array(45,28,36,45,89), '[","]') "Concat", +JsonGet_Int(Json_Array(45,28,36,45,89), '[+]') "Sum", +JsonGet_Real(Json_Array(45,28,36,45,89), '[!]', 2) "Avg"; +Rank Number Concat Sum Avg +89 5 45,28,36,45,89 243 48.60 +SELECT +JsonGet_String('{"qty":7,"price":29.50,"garanty":null}','price') "String", +JsonGet_Int('{"qty":7,"price":29.50,"garanty":null}','price') "Int", +JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}','price') "Real"; +String Int Real +29.50 29 29.500000000000000 +SELECT JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}','price',3) "Real"; +Real +29.500 +# +# Testing Locate +# +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'machin'); +JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'machin') +truc +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),56); +JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),56) +qty +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),3.1416); +JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),3.1416) +price +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'chose'); +JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'chose') +NULL +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'Jack') Path; +Path +AUTHORS:[1]:FN +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'jack' ci) Path; +Path +AUTHORS:[1]:FN +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"Jack", "LN":"London"}' json_) Path; +Path +AUTHORS:[1] +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"jack", "LN":"London"}' json_) Path; +Path +NULL +SELECT JsonLocate('[45,28,36,45,89]',36); +JsonLocate('[45,28,36,45,89]',36) +[2] +SELECT JsonLocate('[45,28,36,45,89]' json_,28.0); +JsonLocate('[45,28,36,45,89]' json_,28.0) +NULL +SELECT Json_Locate_All('[45,28,36,45,89]',10); +Json_Locate_All('[45,28,36,45,89]',10) +[] +SELECT Json_Locate_All('[45,28,36,45,89]',45); +Json_Locate_All('[45,28,36,45,89]',45) +["[0]","[3]"] +SELECT Json_Locate_All('[[45,28],36,45,89]',45); +Json_Locate_All('[[45,28],36,45,89]',45) +["[0]:[0]","[2]"] +SELECT Json_Locate_All('[[45,28,45],36,45,89]',45); +Json_Locate_All('[[45,28,45],36,45,89]',45) +["[0]:[0]","[0]:[2]","[2]"] +SELECT Json_Locate_All('[[45,28,45],36,45,89]',JsonGet_Int('[3,45]','[1]')); +Json_Locate_All('[[45,28,45],36,45,89]',JsonGet_Int('[3,45]','[1]')) +["[0]:[0]","[0]:[2]","[2]"] +SELECT JsonLocate('[[45,28,45],36,45,89]',45,n) from t1; +JsonLocate('[[45,28,45],36,45,89]',45,n) +[0]:[0] +[0]:[2] +[2] +NULL +NULL +SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) FROM t1; +JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) +[0]:[0] +[0]:[2] +[2] +NULL +NULL +SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) AS `Path` FROM t1 GROUP BY n HAVING `Path` IS NOT NULL; +Path +[0]:[0] +[0]:[2] +[2] +SELECT Json_Locate_All('[45,28,[36,45,89]]',45); +Json_Locate_All('[45,28,[36,45,89]]',45) +["[0]","[2]:[1]"] +SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',JsonValue(45.0)); +Json_Locate_All('[[45,28],[36,45.0,89]]',JsonValue(45.0)) +[] +SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',45.0); +Json_Locate_All('[[45,28],[36,45.0,89]]',45.0) +["[1]:[1]"] +SELECT JsonLocate('[[45,28],[36,45,89]]','[36,45,89]' json_); +JsonLocate('[[45,28],[36,45,89]]','[36,45,89]' json_) +[1] +SELECT JsonLocate('[[45,28],[36,45,89]]','[45,28]' json_); +JsonLocate('[[45,28],[36,45,89]]','[45,28]' json_) +[0] +SELECT Json_Locate_All('[[45,28],[[36,45],89]]','45') "All paths"; +All paths +[] +SELECT Json_Locate_All('[[45,28],[[36,45],89]]','[36,45]' json_); +Json_Locate_All('[[45,28],[[36,45],89]]','[36,45]' json_) +["[1]:[0]"] +SELECT JsonGet_Int(Json_Locate_All('[[45,28],[[36,45],89]]',45), '[#]') "Nb of occurs"; +Nb of occurs +2 +SELECT Json_Locate_All('[[45,28],[[36,45],89]]',45,2); +Json_Locate_All('[[45,28],[[36,45],89]]',45,2) +["[0]:[0]"] +SELECT JsonGet_String(Json_Locate_All('[45,28,36,45,89]',45),'[0]'); +JsonGet_String(Json_Locate_All('[45,28,36,45,89]',45),'[0]') +[0] +SELECT JsonLocate(Json_File('test/biblio.json'), 'Knab'); +JsonLocate(Json_File('test/biblio.json'), 'Knab') +[0]:AUTHOR:[1]:LASTNAME +SELECT Json_Locate_All('test/biblio.json' jfile_, 'Knab'); +Json_Locate_All('test/biblio.json' jfile_, 'Knab') +["[0]:AUTHOR:[1]:LASTNAME"] +# +# Testing json files +# +select Jfile_Make('[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]}, +{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]}, +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]}, +{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]', 'test/fx.json', 0) AS NewFile; +NewFile +test/fx.json +SELECT Jfile_Make('test/fx.json', 1); +Jfile_Make('test/fx.json', 1) +test/fx.json +SELECT Jfile_Make('test/fx.json' jfile_); +Jfile_Make('test/fx.json' jfile_) +test/fx.json +SELECT Jfile_Make(Jbin_File('test/fx.json'), 0); +Jfile_Make(Jbin_File('test/fx.json'), 0) +test/fx.json +SELECT Json_File('test/fx.json', 1); +Json_File('test/fx.json', 1) +[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}] +Warnings: +Warning 1105 File pretty format doesn't match the specified pretty value +SELECT Json_File('test/fx.json', 2); +Json_File('test/fx.json', 2) +[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}] +Warnings: +Warning 1105 File pretty format doesn't match the specified pretty value +SELECT Json_File('test/fx.json', 0); +Json_File('test/fx.json', 0) +[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}] +SELECT Json_File('test/fx.json', '[0]'); +Json_File('test/fx.json', '[0]') +{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]} +SELECT Json_File('test/fx.json', '[?]'); +Json_File('test/fx.json', '[?]') +NULL +Warnings: +Warning 1105 Invalid function specification ? +SELECT JsonGet_String(Json_File('test/fx.json'), '[1]:*'); +JsonGet_String(Json_File('test/fx.json'), '[1]:*') +{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]} +SELECT JsonGet_String(Json_File('test/fx.json'), '[1]'); +JsonGet_String(Json_File('test/fx.json'), '[1]') +6 car roadster 56000 ??? +SELECT JsonGet_Int(Json_File('test/fx.json'), '[1]:mileage') AS Mileage; +Mileage +56000 +SELECT JsonGet_Real(Json_File('test/fx.json'), '[0]:price', 2) AS Price; +Price +5.65 +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings'); +Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings') +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4,6]} +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 1, 'ratings'); +Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 1, 'ratings') +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,6,4]} +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings', 1); +Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings', 1) +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,6,4]} +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]:ratings'), 6, 0); +Json_Array_Add(Json_File('test/fx.json', '[2]:ratings'), 6, 0) +[6,2,4] +SELECT Json_Array_Delete(Json_File('test/fx.json', '[2]'), 'ratings', 1); +Json_Array_Delete(Json_File('test/fx.json', '[2]'), 'ratings', 1) +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2]} +SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 'france' origin); +Json_Object_Add(Json_File('test/fx.json', '[2]'), 'france' origin) +{"_id":7,"type":"food","item":"meat","origin":"france","ratings":[2,4]} +SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 70 H, 'size'); +Json_Object_Add(Json_File('test/fx.json', '[2]'), 70 H, 'size') +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]} +Warnings: +Warning 1105 No sub-item at 'size' +SELECT Json_Object_Add(Json_File('test/fx.json', '[3]'), 70 H, 'size'); +Json_Object_Add(Json_File('test/fx.json', '[3]'), 70 H, 'size') +{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":70},"ratings":[5,8,7]} +SELECT Json_Object_List(Json_File('test/fx.json', '[3]:size')); +Json_Object_List(Json_File('test/fx.json', '[3]:size')) +["W","L","H"] DROP TABLE t1; -DROP FUNCTION Json_Array; -DROP FUNCTION Json_Array_Add; -DROP FUNCTION Json_Object; -DROP FUNCTION Json_Object_Nonull; -DROP FUNCTION Json_Value; -DROP FUNCTION Json_Array_Grp; -DROP FUNCTION Json_Object_Grp; +DROP TABLE t2; +DROP TABLE t3; diff --git a/storage/connect/mysql-test/connect/r/odbc_firebird.result b/storage/connect/mysql-test/connect/r/odbc_firebird.result new file mode 100644 index 00000000000..7688c188f59 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/odbc_firebird.result @@ -0,0 +1,100 @@ +SET NAMES utf8; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='Bad connection string'; +ERROR HY000: SQLDriverConnect: [unixODBC][Driver Manager]Data source name not found, and no default driver specified +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Sources; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `Name` varchar(256) NOT NULL, + `Description` varchar(256) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`='ODBC' `CATFUNC`='Sources' +SELECT * FROM t1; +Name Description +Firebird Firebird +DROP TABLE t1; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Drivers; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `Description` char(128) NOT NULL, + `Attributes` varchar(256) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`='ODBC' `CATFUNC`='Drivers' +SELECT * FROM t1; +Description Attributes +Firebird Description=Firebird ODBC Driver in usr;Driver=/usr/local/lib/libOdbcFb.so;Setup=/usr/local/lib/libOdbcFb.so;FileUsage=1; +DROP TABLE t1; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Tables CONNECTION='Not important'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `Table_Cat` char(128) NOT NULL, + `Table_Schema` char(128) NOT NULL, + `Table_Name` char(128) NOT NULL, + `Table_Type` char(16) NOT NULL, + `Remark` char(255) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='Not important' `TABLE_TYPE`='ODBC' `CATFUNC`='Tables' +DROP TABLE t1; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Columns CONNECTION='Not important'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `Table_Cat` char(128) NOT NULL, + `Table_Schema` char(128) NOT NULL, + `Table_Name` char(128) NOT NULL, + `Column_Name` char(128) NOT NULL, + `Data_Type` smallint(6) NOT NULL, + `Type_Name` char(30) NOT NULL, + `Column_Size` int(10) NOT NULL, + `Buffer_Length` int(10) NOT NULL, + `Decimal_Digits` smallint(6) NOT NULL, + `Radix` smallint(6) NOT NULL, + `Nullable` smallint(6) NOT NULL, + `Remarks` char(255) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='Not important' `TABLE_TYPE`='ODBC' `CATFUNC`='Columns' +DROP TABLE t1; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC TABNAME='EMPLOYEE' CONNECTION='DSN=Firebird;UID=SYSDBA;PWD=manager'; +SELECT * FROM t1; +EMP_NO FIRST_NAME LAST_NAME PHONE_EXT HIRE_DATE DEPT_NO JOB_CODE JOB_GRADE JOB_COUNTRY SALARY FULL_NAME +2 Robert Nelson 250 1988-12-28 00:00:00 600 VP 2 USA 105900.00 Nelson, Robert +4 Bruce Young 233 1988-12-28 00:00:00 621 Eng 2 USA 97500.00 Young, Bruce +5 Kim Lambert 22 1989-02-06 00:00:00 130 Eng 2 USA 102750.00 Lambert, Kim +8 Leslie Johnson 410 1989-04-05 00:00:00 180 Mktg 3 USA 64635.00 Johnson, Leslie +9 Phil Forest 229 1989-04-17 00:00:00 622 Mngr 3 USA 75060.00 Forest, Phil +11 K. J. Weston 34 1990-01-17 00:00:00 130 SRep 4 USA 86292.94 Weston, K. J. +12 Terri Lee 256 1990-05-01 00:00:00 000 Admin 4 USA 53793.00 Lee, Terri +14 Stewart Hall 227 1990-06-04 00:00:00 900 Finan 3 USA 69482.63 Hall, Stewart +15 Katherine Young 231 1990-06-14 00:00:00 623 Mngr 3 USA 67241.25 Young, Katherine +20 Chris Papadopoulos 887 1990-01-01 00:00:00 671 Mngr 3 USA 89655.00 Papadopoulos, Chris +24 Pete Fisher 888 1990-09-12 00:00:00 671 Eng 3 USA 81810.19 Fisher, Pete +28 Ann Bennet 5 1991-02-01 00:00:00 120 Admin 5 England 22935.00 Bennet, Ann +29 Roger De Souza 288 1991-02-18 00:00:00 623 Eng 3 USA 69482.63 De Souza, Roger +34 Janet Baldwin 2 1991-03-21 00:00:00 110 Sales 3 USA 61637.81 Baldwin, Janet +36 Roger Reeves 6 1991-04-25 00:00:00 120 Sales 3 England 33620.63 Reeves, Roger +37 Willie Stansbury 7 1991-04-25 00:00:00 120 Eng 4 England 39224.06 Stansbury, Willie +44 Leslie Phong 216 1991-06-03 00:00:00 623 Eng 4 USA 56034.38 Phong, Leslie +45 Ashok Ramanathan 209 1991-08-01 00:00:00 621 Eng 3 USA 80689.50 Ramanathan, Ashok +46 Walter Steadman 210 1991-08-09 00:00:00 900 CFO 1 USA 116100.00 Steadman, Walter +52 Carol Nordstrom 420 1991-10-02 00:00:00 180 PRel 4 USA 42742.50 Nordstrom, Carol +61 Luke Leung 3 1992-02-18 00:00:00 110 SRep 4 USA 68805.00 Leung, Luke +65 Sue Anne O'Brien 877 1992-03-23 00:00:00 670 Admin 5 USA 31275.00 O'Brien, Sue Anne +71 Jennifer M. Burbank 289 1992-04-15 00:00:00 622 Eng 3 USA 53167.50 Burbank, Jennifer M. +72 Claudia Sutherland NULL 1992-04-20 00:00:00 140 SRep 4 Canada 100914.00 Sutherland, Claudia +83 Dana Bishop 290 1992-06-01 00:00:00 621 Eng 3 USA 62550.00 Bishop, Dana +85 Mary S. MacDonald 477 1992-06-01 00:00:00 100 VP 2 USA 111262.50 MacDonald, Mary S. +94 Randy Williams 892 1992-08-08 00:00:00 672 Mngr 4 USA 56295.00 Williams, Randy +105 Oliver H. Bender 255 1992-10-08 00:00:00 000 CEO 1 USA 212850.00 Bender, Oliver H. +107 Kevin Cook 894 1993-02-01 00:00:00 670 Dir 2 USA 111262.50 Cook, Kevin +109 Kelly Brown 202 1993-02-04 00:00:00 600 Admin 5 USA 27000.00 Brown, Kelly +110 Yuki Ichida 22 1993-02-04 00:00:00 115 Eng 3 Japan 6000000.00 Ichida, Yuki +113 Mary Page 845 1993-04-12 00:00:00 671 Eng 4 USA 48000.00 Page, Mary +114 Bill Parker 247 1993-06-01 00:00:00 623 Eng 5 USA 35000.00 Parker, Bill +118 Takashi Yamamoto 23 1993-07-01 00:00:00 115 SRep 4 Japan 7480000.00 Yamamoto, Takashi +121 Roberto Ferrari 1 1993-07-12 00:00:00 125 SRep 4 Italy 99000000.00 Ferrari, Roberto +127 Michael Yanowski 492 1993-08-09 00:00:00 100 SRep 4 USA 44000.00 Yanowski, Michael +134 Jacques Glon NULL 1993-08-23 00:00:00 123 SRep 4 France 390500.00 Glon, Jacques +136 Scott Johnson 265 1993-09-13 00:00:00 623 Doc 3 USA 60000.00 Johnson, Scott +138 T.J. Green 218 1993-11-01 00:00:00 621 Eng 4 USA 36000.00 Green, T.J. +141 Pierre Osborne NULL 1994-01-03 00:00:00 121 SRep 4 Switzerland 110000.00 Osborne, Pierre +144 John Montgomery 820 1994-03-30 00:00:00 672 Eng 5 USA 35000.00 Montgomery, John +145 Mark Guckenheimer 221 1994-05-02 00:00:00 622 Eng 5 USA 32000.00 Guckenheimer, Mark +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/std_data/bib0.json b/storage/connect/mysql-test/connect/std_data/bib0.json new file mode 100644 index 00000000000..2dabf0fd4b3 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/bib0.json @@ -0,0 +1,2 @@ +{"ISBN":"9782212090819","LANG":"fr","SUBJECT":"applications","AUTHOR":[{"FIRSTNAME":"Jean-Michel","LASTNAME":"Bernadac"},{"FIRSTNAME":"François","LASTNAME":"Knab"}],"TITLE":"Construire une application XML","PUBLISHER":{"NAME":"Eyrolles","PLACE":"Paris"},"DATEPUB":1999} +{"ISBN":"9782840825685","LANG":"fr","SUBJECT":"applications","AUTHOR":[{"FIRSTNAME":"William J.","LASTNAME":"Pardi"}],"TITLE":"XML en Action","TRANSLATED":{"PREFIX":"adapté de l'anglais par","TRANSLATOR":{"FIRSTNAME":"James","LASTNAME":"Guerin"}},"PUBLISHER":{"NAME":"Microsoft Press","PLACE":"Paris"},"DATEPUB":2001} diff --git a/storage/connect/mysql-test/connect/std_data/biblio.json b/storage/connect/mysql-test/connect/std_data/biblio.json index cc24b162cd9..dcf9592296c 100644 --- a/storage/connect/mysql-test/connect/std_data/biblio.json +++ b/storage/connect/mysql-test/connect/std_data/biblio.json @@ -24,12 +24,10 @@ "ISBN": "9782840825685", "LANG": "fr", "SUBJECT": "applications", - "AUTHOR": [ - { - "FIRSTNAME": "William J.", - "LASTNAME": "Pardi" - } - ], + "AUTHOR": { + "FIRSTNAME": "William J.", + "LASTNAME": "Pardi" + }, "TITLE": "XML en Action", "TRANSLATION": "adapté de l'anglais par", "TRANSLATOR": { diff --git a/storage/connect/mysql-test/connect/t/datest.test b/storage/connect/mysql-test/connect/t/datest.test index 7fe5fcea8ff..e9ee28ed550 100644 --- a/storage/connect/mysql-test/connect/t/datest.test +++ b/storage/connect/mysql-test/connect/t/datest.test @@ -14,3 +14,15 @@ SELECT id, DAYNAME(dat) FROM t1; SELECT id, DAYNAME(datim) FROM t1 LIMIT 1; SELECT id, TIME(tim) FROM t1 LIMIT 1; DROP TABLE t1; + +--echo # +--echo # Testing use of dates in where clause (MDEV-8926) +--echo # +CREATE TABLE t1 (col1 DATE) ENGINE=CONNECT TABLE_TYPE=CSV; +INSERT INTO t1 VALUES('2015-01-01'),('2015-02-01'),('2015-03-01'),('2015-04-01'); +SELECT * FROM t1 WHERE col1 = '2015-02-01'; +SELECT * FROM t1 WHERE col1 > '2015-02-01'; +SELECT * FROM t1 WHERE col1 >= '2015-02-01'; +SELECT * FROM t1 WHERE col1 < '2015-02-01'; +SELECT * FROM t1 WHERE col1 <= '2015-02-01'; +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/t/json.test b/storage/connect/mysql-test/connect/t/json.test index 1cc2c054db4..b743071b1be 100644 --- a/storage/connect/mysql-test/connect/t/json.test +++ b/storage/connect/mysql-test/connect/t/json.test @@ -4,6 +4,7 @@ let $MYSQLD_DATADIR= `select @@datadir`; --copy_file $MTR_SUITE_DIR/std_data/biblio.json $MYSQLD_DATADIR/test/biblio.json +--copy_file $MTR_SUITE_DIR/std_data/bib0.json $MYSQLD_DATADIR/test/bib0.json --copy_file $MTR_SUITE_DIR/std_data/expense.json $MYSQLD_DATADIR/test/expense.json --copy_file $MTR_SUITE_DIR/std_data/mulexp3.json $MYSQLD_DATADIR/test/mulexp3.json --copy_file $MTR_SUITE_DIR/std_data/mulexp4.json $MYSQLD_DATADIR/test/mulexp4.json @@ -115,6 +116,33 @@ ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='biblio.json'; SELECT * FROM t1; DROP TABLE t1; +--echo # +--echo # Testing a pretty=0 file +--echo # +CREATE TABLE t1 +( + ISBN CHAR(15) NOT NULL, + Language CHAR(2) FIELD_FORMAT='LANG', + Subject CHAR(32) FIELD_FORMAT='SUBJECT', + AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME', + AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME', + Title CHAR(32) FIELD_FORMAT='TITLE', + Translation CHAR(32) FIELD_FORMAT='TRANSLATED:PREFIX', + TranslatorFN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:FIRSTNAME', + TranslatorLN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:LASTNAME', + Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME', + Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE', + Year int(4) FIELD_FORMAT='DATEPUB', + INDEX IX(ISBN) +) +ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bib0.json' LRECL=350 OPTION_LIST='Pretty=0'; +SHOW INDEX FROM t1; +SELECT * FROM t1; +DESCRIBE SELECT * FROM t1 WHERE ISBN = '9782212090819'; +--error ER_GET_ERRMSG +UPDATE t1 SET AuthorFN = 'Philippe' WHERE ISBN = '9782212090819'; +DROP TABLE t1; + --echo # --echo # A file with 2 arrays --echo # @@ -258,6 +286,8 @@ DROP TABLE t1, t2, t3, t4; # Clean up # --remove_file $MYSQLD_DATADIR/test/biblio.json +--remove_file $MYSQLD_DATADIR/test/bib0.dnx +--remove_file $MYSQLD_DATADIR/test/bib0.json --remove_file $MYSQLD_DATADIR/test/expense.json --remove_file $MYSQLD_DATADIR/test/mulexp3.json --remove_file $MYSQLD_DATADIR/test/mulexp4.json diff --git a/storage/connect/mysql-test/connect/t/json_udf.inc b/storage/connect/mysql-test/connect/t/json_udf.inc index 098fff3663e..de411696885 100644 --- a/storage/connect/mysql-test/connect/t/json_udf.inc +++ b/storage/connect/mysql-test/connect/t/json_udf.inc @@ -9,28 +9,40 @@ if (!$HA_CONNECT_SO) { --skip Needs a dynamically built ha_connect.so } -let $is_win = `select convert(@@version_compile_os using latin1) IN ("Win32","Win64","Windows")`; +CREATE FUNCTION json_array RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_array_add RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_array_add_values RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_array_delete RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_object RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_object_nonull RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_object_add RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_object_delete RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_object_list RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jsonvalue RETURNS STRING SONAME 'ha_connect'; +CREATE AGGREGATE FUNCTION json_array_grp RETURNS STRING SONAME 'ha_connect'; +CREATE AGGREGATE FUNCTION json_object_grp RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_item_merge RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_get_item RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jsonget_string RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jsonget_int RETURNS INTEGER SONAME 'ha_connect'; +CREATE FUNCTION jsonget_real RETURNS REAL SONAME 'ha_connect'; +CREATE FUNCTION jsonlocate RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_locate_all RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_file RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jfile_make RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION json_serialize RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_array RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_array_add_values RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_array_add RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_array_delete RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_object RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_object_nonull RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_object_add RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_object_delete RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_object_list RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_get_item RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_item_merge RETURNS STRING SONAME 'ha_connect'; +CREATE FUNCTION jbin_file RETURNS STRING SONAME 'ha_connect'; -if ($is_win) -{ ---eval CREATE FUNCTION Json_Array RETURNS STRING SONAME 'ha_connect.dll'; ---eval CREATE FUNCTION Json_Array_Add RETURNS STRING SONAME 'ha_connect.dll'; ---eval CREATE FUNCTION Json_Object RETURNS STRING SONAME 'ha_connect.dll'; ---eval CREATE FUNCTION Json_Object_Nonull RETURNS STRING SONAME 'ha_connect.dll'; ---eval CREATE FUNCTION Json_Value returns STRING SONAME 'ha_connect.dll'; ---eval CREATE AGGREGATE FUNCTION Json_Array_Grp RETURNS STRING SONAME 'ha_connect.dll'; ---eval CREATE AGGREGATE FUNCTION Json_Object_Grp RETURNS STRING SONAME 'ha_connect.dll'; -} - -if (!$is_win) -{ ---eval CREATE FUNCTION Json_Array RETURNS STRING SONAME 'ha_connect.so'; ---eval CREATE FUNCTION Json_Array_Add RETURNS STRING SONAME 'ha_connect.so'; ---eval CREATE FUNCTION Json_Object RETURNS STRING SONAME 'ha_connect.so'; ---eval CREATE FUNCTION Json_Object_Nonull RETURNS STRING SONAME 'ha_connect.so'; ---eval CREATE FUNCTION Json_Value returns STRING SONAME 'ha_connect.so'; ---eval CREATE AGGREGATE FUNCTION Json_Array_Grp RETURNS STRING SONAME 'ha_connect.so'; ---eval CREATE AGGREGATE FUNCTION Json_Object_Grp RETURNS STRING SONAME 'ha_connect.so'; -} --enable_query_log diff --git a/storage/connect/mysql-test/connect/t/json_udf.test b/storage/connect/mysql-test/connect/t/json_udf.test index b4427517ca5..d05dd28f16c 100644 --- a/storage/connect/mysql-test/connect/t/json_udf.test +++ b/storage/connect/mysql-test/connect/t/json_udf.test @@ -5,35 +5,62 @@ let $MYSQLD_DATADIR= `select @@datadir`; --copy_file $MTR_SUITE_DIR/std_data/biblio.json $MYSQLD_DATADIR/test/biblio.json --copy_file $MTR_SUITE_DIR/std_data/employee.dat $MYSQLD_DATADIR/test/employee.dat +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=VIR BLOCK_SIZE=5; + --echo # --echo # Test UDF's with constant arguments --echo # +--error ER_CANT_INITIALIZE_UDF +SELECT JsonValue(56,3.1416,'foo',NULL); +SELECT JsonValue(3.1416); +SELECT JsonValue('foo'); +SELECT JsonValue(9223372036854775807); +SELECT JsonValue(NULL); +SELECT JsonValue(TRUE); +SELECT JsonValue(FALSE); +SELECT JsonValue(); +SELECT JsonValue('[11,22,33]' json_) FROM t1; +# SELECT Json_Array(); -SELECT Json_Object(56,3.1416,'foo',NULL); -SELECT Json_Object(56 qty,3.1416 price,'foo' truc, NULL garanty); SELECT Json_Array(56,3.1416,'My name is "Foo"',NULL); +SELECT Json_Array(Json_Array(56,3.1416,'foo'),NULL); --error ER_CANT_INITIALIZE_UDF SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL)) Array; SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL),'One more') Array; -SELECT Json_Array_Add(Json_Value('one value'),'One more'); +--error ER_CANT_INITIALIZE_UDF +SELECT Json_Array_Add(JsonValue('one value'),'One more'); --error ER_CANT_INITIALIZE_UDF SELECT Json_Array_Add('one value','One more'); SELECT Json_Array_Add('one value' json_,'One more'); ---error ER_CANT_INITIALIZE_UDF -SELECT Json_Value(56,3.1416,'foo',NULL); -SELECT Json_Value(3.1416); -SELECT Json_Value('foo'); -SELECT Json_Value(NULL); -SELECT Json_Value(); +SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 0); +SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 2) Array; +SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 9); +SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin', NULL), 'One more', 'Two more') Array; +SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), 'One more', 'Two more') Array FROM t1; +SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), n) Array FROM t1; +SELECT Json_Array_Add_Values(Json_Array(n, 3.1416, 'machin'), n) Array FROM t1; +SELECT Json_Array_Add_Values('[56]', 3.1416, 'machin') Array; +# +SELECT Json_Array_Delete(Json_Array(56,3.1416,'My name is "Foo"',NULL),0); +SELECT Json_Array_Delete(Json_Object(56,3.1416,'My name is Foo',NULL),2); +SELECT Json_Array_Delete(Json_Array(56,3.1416,'My name is "Foo"',NULL),'2'); +# +SELECT Json_Object(56, 3.1416, 'foo', NULL); +SELECT Json_Object(56 qty, 3.1416 price, 'foo' truc, NULL garanty); SELECT Json_Object(); -SELECT Json_Object(Json_Array(56,3.1416,'foo'),NULL); -SELECT Json_Array(Json_Array(56,3.1416,'foo'),NULL); -SELECT Json_Array(Json_Object(56 "qty",3.1416 "price",'foo'),NULL); +SELECT Json_Object(Json_Array(56, 3.1416, 'foo'), NULL); +SELECT Json_Array(Json_Object(56 "qty", 3.1416 "price", 'foo') ,NULL); +SELECT Json_Object_Add(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'blue' color); +SELECT Json_Object_Add(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 45.99 price); +SELECT Json_Object_Delete(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'truc'); +SELECT Json_Object_Delete(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty), 'chose'); +SELECT Json_Object_List(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty)) "Key List"; +SELECT Json_Object_List('{"qty":56, "price":3.1416, "truc":"machin", "garanty":null}') "Key List"; --echo # --echo # Test UDF's with column arguments --echo # -CREATE TABLE t1 +CREATE TABLE t2 ( ISBN CHAR(15), LANG CHAR(2), @@ -46,14 +73,13 @@ CREATE TABLE t1 DATEPUB int(4) ) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.json'; -SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t1; -SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t1; +SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t2; +SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t2; --error ER_CANT_INITIALIZE_UDF -SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t1; -SELECT Json_Array_Grp(TITLE) FROM t1; -DROP TABLE t1; +SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t2; +SELECT Json_Array_Grp(TITLE) FROM t2; -CREATE TABLE t1 ( +CREATE TABLE t3 ( SERIALNO CHAR(5) NOT NULL, NAME VARCHAR(12) NOT NULL FLAG=6, SEX SMALLINT(1) NOT NULL, @@ -64,30 +90,156 @@ CREATE TABLE t1 ( SALARY DOUBLE(8,2) NOT NULL FLAG=52 ) ENGINE=CONNECT TABLE_TYPE=FIX BLOCK_SIZE=8 FILE_NAME='employee.dat' ENDING=1; -SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t1 WHERE NAME = 'MERCHANT'; -SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t1 GROUP BY DEPARTMENT; -set connect_json_grp_size=30; -SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t1 GROUP BY DEPARTMENT; -SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t1 GROUP BY DEPARTMENT; -SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT; -SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT, TITLE; +SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t3 WHERE NAME = 'MERCHANT'; +SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t3 GROUP BY DEPARTMENT; +SET connect_json_grp_size=30; +SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t3 GROUP BY DEPARTMENT; +SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t3 GROUP BY DEPARTMENT; +SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT; +SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT, TITLE; --error ER_CANT_INITIALIZE_UDF -SELECT Json_Object_Grp(SALARY) FROM t1; -SELECT Json_Object_Grp(SALARY, NAME) FROM t1; -SELECT Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") FROM t1 GROUP BY DEPARTMENT; -SELECT Json_Array_Grp(NAME) from t1; -DROP TABLE t1; +SELECT Json_Object_Grp(SALARY) FROM t3; +SELECT Json_Object_Grp(SALARY, NAME) FROM t3; +SELECT Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") FROM t3 GROUP BY DEPARTMENT; +SELECT Json_Array_Grp(NAME) FROM t3; -DROP FUNCTION Json_Array; -DROP FUNCTION Json_Array_Add; -DROP FUNCTION Json_Object; -DROP FUNCTION Json_Object_Nonull; -DROP FUNCTION Json_Value; -DROP FUNCTION Json_Array_Grp; -DROP FUNCTION Json_Object_Grp; +--echo # +--echo # Test value getting UDF's +--echo # +SELECT JsonGet_String(Json_Array_Grp(name),'[#]') FROM t3; +SELECT JsonGet_String(Json_Array_Grp(name),'[","]') FROM t3; +SELECT JsonGet_String(Json_Array_Grp(name),'[>]') FROM t3; +SET @j1 = '[45,28,36,45,89]'; +SELECT JsonGet_String(@j1,'[1]'); +SELECT JsonGet_String(@j1 json_,'[3]'); +SELECT JsonGet_String(Json_Array(45,28,36,45,89),'[3]'); +SELECT JsonGet_String(Json_Array(45,28,36,45,89),'["+"]') "list",'=' as "egal",JsonGet_String(Json_Array(45,28,36,45,89),'[+]') "sum"; +SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]'); +SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:*'); +SELECT JsonGet_String(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'truc'); +SET @j2 = '{"qty":56,"price":3.141600,"truc":"machin","garanty":null}'; +SELECT JsonGet_String(@j2 json_,'truc'); +SELECT JsonGet_String(@j2,'truc'); +SELECT JsonGet_String(@j2,'chose'); +SELECT JsonGet_String(NULL json_, NULL); +SELECT department, JsonGet_String(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department; +# +SELECT JsonGet_Int(@j1, '[4]'); +SELECT JsonGet_Int(@j1, '[#]'); +SELECT JsonGet_Int(@j1, '[+]'); +SELECT JsonGet_Int(@j1 json_,'[3]'); +SELECT JsonGet_Int(Json_Array(45,28,36,45,89),'[3]'); +SELECT JsonGet_Int(Json_Array(45,28,36,45,89),'["+"]'); +SELECT JsonGet_Int(Json_Array(45,28,36,45,89),'[+]'); +SELECT JsonGet_Int(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]'); +SELECT JsonGet_Int(Json_Array(json_array(45,28),json_array(36,45,89)),'[0]:[1]'); +SELECT JsonGet_Int(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'qty'); +SELECT JsonGet_Int(@j2 json_,'price'); +SELECT JsonGet_Int(@j2,'qty'); +SELECT JsonGet_Int('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','chose'); +SELECT JsonGet_Int(JsonGet_String(Json_Array(Json_Array(45,28),Json_Array(36,45,89)),'[1]:*'),'[+]') sum; +SELECT department, JsonGet_Int(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department; +# +SELECT JsonGet_Real(@j1, '[2]'); +SELECT JsonGet_Real(@j1 json_,'[3]',2); +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'[3]'); +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'["+"]'); +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'[+]'); +SELECT JsonGet_Real(Json_Array(45,28,36,45,89),'[!]'); +SELECT JsonGet_Real(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]'); +SELECT JsonGet_Real(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'price'); +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}' json_,'qty'); +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','price'); +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','price', 4); +SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}','chose'); +SELECT department, JsonGet_Real(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department; + +--echo # +--echo # Documentation examples +--echo # +SELECT + JsonGet_Int(Json_Array(45,28,36,45,89), '[4]') "Rank", + JsonGet_Int(Json_Array(45,28,36,45,89), '[#]') "Number", + JsonGet_String(Json_Array(45,28,36,45,89), '[","]') "Concat", + JsonGet_Int(Json_Array(45,28,36,45,89), '[+]') "Sum", + JsonGet_Real(Json_Array(45,28,36,45,89), '[!]', 2) "Avg"; +SELECT + JsonGet_String('{"qty":7,"price":29.50,"garanty":null}','price') "String", + JsonGet_Int('{"qty":7,"price":29.50,"garanty":null}','price') "Int", + JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}','price') "Real"; +SELECT JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}','price',3) "Real"; + +--echo # +--echo # Testing Locate +--echo # +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'machin'); +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),56); +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),3.1416); +SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'chose'); +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'Jack') Path; +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'jack' ci) Path; +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"Jack", "LN":"London"}' json_) Path; +SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"jack", "LN":"London"}' json_) Path; +SELECT JsonLocate('[45,28,36,45,89]',36); +SELECT JsonLocate('[45,28,36,45,89]' json_,28.0); +SELECT Json_Locate_All('[45,28,36,45,89]',10); +SELECT Json_Locate_All('[45,28,36,45,89]',45); +SELECT Json_Locate_All('[[45,28],36,45,89]',45); +SELECT Json_Locate_All('[[45,28,45],36,45,89]',45); +SELECT Json_Locate_All('[[45,28,45],36,45,89]',JsonGet_Int('[3,45]','[1]')); +SELECT JsonLocate('[[45,28,45],36,45,89]',45,n) from t1; +SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) FROM t1; +SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) AS `Path` FROM t1 GROUP BY n HAVING `Path` IS NOT NULL; +SELECT Json_Locate_All('[45,28,[36,45,89]]',45); +SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',JsonValue(45.0)); +SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',45.0); +SELECT JsonLocate('[[45,28],[36,45,89]]','[36,45,89]' json_); +SELECT JsonLocate('[[45,28],[36,45,89]]','[45,28]' json_); +SELECT Json_Locate_All('[[45,28],[[36,45],89]]','45') "All paths"; +SELECT Json_Locate_All('[[45,28],[[36,45],89]]','[36,45]' json_); +SELECT JsonGet_Int(Json_Locate_All('[[45,28],[[36,45],89]]',45), '[#]') "Nb of occurs"; +SELECT Json_Locate_All('[[45,28],[[36,45],89]]',45,2); +SELECT JsonGet_String(Json_Locate_All('[45,28,36,45,89]',45),'[0]'); +SELECT JsonLocate(Json_File('test/biblio.json'), 'Knab'); +SELECT Json_Locate_All('test/biblio.json' jfile_, 'Knab'); + +--echo # +--echo # Testing json files +--echo # +select Jfile_Make('[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]}, +{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]}, +{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]}, +{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]', 'test/fx.json', 0) AS NewFile; +SELECT Jfile_Make('test/fx.json', 1); +SELECT Jfile_Make('test/fx.json' jfile_); +SELECT Jfile_Make(Jbin_File('test/fx.json'), 0); +SELECT Json_File('test/fx.json', 1); +SELECT Json_File('test/fx.json', 2); +SELECT Json_File('test/fx.json', 0); +SELECT Json_File('test/fx.json', '[0]'); +SELECT Json_File('test/fx.json', '[?]'); +SELECT JsonGet_String(Json_File('test/fx.json'), '[1]:*'); +SELECT JsonGet_String(Json_File('test/fx.json'), '[1]'); +SELECT JsonGet_Int(Json_File('test/fx.json'), '[1]:mileage') AS Mileage; +SELECT JsonGet_Real(Json_File('test/fx.json'), '[0]:price', 2) AS Price; +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings'); +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 1, 'ratings'); +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings', 1); +SELECT Json_Array_Add(Json_File('test/fx.json', '[2]:ratings'), 6, 0); +SELECT Json_Array_Delete(Json_File('test/fx.json', '[2]'), 'ratings', 1); +SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 'france' origin); +SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 70 H, 'size'); +SELECT Json_Object_Add(Json_File('test/fx.json', '[3]'), 70 H, 'size'); +SELECT Json_Object_List(Json_File('test/fx.json', '[3]:size')); + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; # # Clean up # +--source json_udf2.inc --remove_file $MYSQLD_DATADIR/test/biblio.json --remove_file $MYSQLD_DATADIR/test/employee.dat +--remove_file $MYSQLD_DATADIR/test/fx.json diff --git a/storage/connect/mysql-test/connect/t/json_udf2.inc b/storage/connect/mysql-test/connect/t/json_udf2.inc new file mode 100644 index 00000000000..e5609bfd1f8 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/json_udf2.inc @@ -0,0 +1,39 @@ +--disable_query_log + +DROP FUNCTION jsonvalue; +DROP FUNCTION json_array; +DROP FUNCTION json_array_add; +DROP FUNCTION json_array_add_values; +DROP FUNCTION json_array_delete; +DROP FUNCTION json_object; +DROP FUNCTION json_object_nonull; +DROP FUNCTION json_object_add; +DROP FUNCTION json_object_delete; +DROP FUNCTION json_object_list; +DROP FUNCTION json_array_grp; +DROP FUNCTION json_object_grp; +DROP FUNCTION json_item_merge; +DROP FUNCTION json_get_item; +DROP FUNCTION JsonGet_string; +DROP FUNCTION JsonGet_int; +DROP FUNCTION JsonGet_real; +DROP FUNCTION jsonlocate; +DROP FUNCTION json_locate_all; +DROP FUNCTION json_file; +DROP FUNCTION json_serialize; +DROP FUNCTION jfile_make; +DROP FUNCTION jbin_array; +DROP FUNCTION jbin_array_add_values; +DROP FUNCTION jbin_array_add; +DROP FUNCTION jbin_array_delete; +DROP FUNCTION jbin_object; +DROP FUNCTION jbin_object_nonull; +DROP FUNCTION jbin_object_add; +DROP FUNCTION jbin_object_delete; +DROP FUNCTION jbin_object_list; +DROP FUNCTION jbin_get_item; +DROP FUNCTION jbin_item_merge; +DROP FUNCTION jbin_file; + +--enable_query_log + diff --git a/storage/connect/mysql-test/connect/t/odbc_firebird.test b/storage/connect/mysql-test/connect/t/odbc_firebird.test new file mode 100644 index 00000000000..f354512b1c2 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/odbc_firebird.test @@ -0,0 +1,31 @@ +--source have_odbc.inc + +SET NAMES utf8; + +# MS ODBC and unixODBC return different error message text, +# so disable displaying error messages +#--disable_result_log ONCE +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='Bad connection string'; + +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Sources; +SHOW CREATE TABLE t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Drivers; +SHOW CREATE TABLE t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Tables CONNECTION='Not important'; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Columns CONNECTION='Not important'; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC TABNAME='EMPLOYEE' CONNECTION='DSN=Firebird;UID=SYSDBA;PWD=manager'; +SELECT * FROM t1; +DROP TABLE t1; diff --git a/storage/connect/noconst.c b/storage/connect/noconst.c new file mode 100644 index 00000000000..043eae7c54e --- /dev/null +++ b/storage/connect/noconst.c @@ -0,0 +1,38 @@ +/***********************************************************************/ +/* (C) Copyright to the author Olivier BERTRAND 2015 */ +/***********************************************************************/ +#include +#include +#include + +#if defined(__WIN__) +#define DllExport __declspec( dllexport ) +#else // !__WIN__ +#define DllExport +#endif // !__WIN__ + +extern "C" { + DllExport my_bool noconst_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport char *noconst(UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char*); +} // extern "C" + +/***********************************************************************/ +/* Returns its argument saying it is not a constant. */ +/***********************************************************************/ +my_bool noconst_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) { + strcpy(message, "noconst unique argument must be a string"); + return true; + } // endif arg + + initid->const_item = false; // The trick! + return false; +} // end of noconst_init + +char *noconst(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *, char *) +{ + return args->args[0]; +} // end of noconst + diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c index c0e249adf12..38e28a171b2 100644 --- a/storage/connect/plugutil.c +++ b/storage/connect/plugutil.c @@ -143,7 +143,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize) fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL)); return NULL; } else { - g->Sarea_Size = worksize; + g->Sarea = NULL; g->Createas = 0; g->Alchecked = 0; g->Mrr = 0; @@ -155,7 +155,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize) /*******************************************************************/ /* Allocate the main work segment. */ /*******************************************************************/ - if (!(g->Sarea = PlugAllocMem(g, worksize))) { + if (worksize && !(g->Sarea = PlugAllocMem(g, worksize))) { char errmsg[256]; sprintf(errmsg, MSG(WORK_AREA), g->Message); strcpy(g->Message, errmsg); @@ -163,7 +163,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize) } else g->Sarea_Size = worksize; - } /* endif g */ + } /* endif g */ g->jump_level = -1; /* New setting to allow recursive call of Plug */ return(g); diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp index 065790b523c..8f9328c0b2f 100644 --- a/storage/connect/reldef.cpp +++ b/storage/connect/reldef.cpp @@ -57,6 +57,7 @@ extern handlerton *connect_hton; /* External function. */ /***********************************************************************/ USETEMP UseTemp(void); +char *GetPluginDir(void); /* --------------------------- Class RELDEF -------------------------- */ diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index 527fe55dd89..7906f6c9219 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -2019,7 +2019,7 @@ int TDBDOS::EstimatedLength(void) dep = 1 + cdp->GetLong() / 20; // Why 20 ????? } else for (; cdp; cdp = cdp->GetNext()) if (!(cdp->Flags & (U_VIRTUAL|U_SPECIAL))) - dep = MY_MAX(dep, cdp->GetOffset()); + dep = MY_MAX(dep, cdp->GetOffset()); return (int)dep; } // end of Estimated Length @@ -2204,7 +2204,7 @@ bool TDBDOS::PrepareWriting(PGLOBAL) } // endif Mode return false; - } // end of WriteDB + } // end of PrepareWriting /***********************************************************************/ /* WriteDB: Data Base write routine for DOS access method. */ @@ -2216,7 +2216,7 @@ int TDBDOS::WriteDB(PGLOBAL g) // Make the line to write if (PrepareWriting(g)) - return true; + return RC_FX; if (trace > 1) htrc("Write: line is='%s'\n", To_Line); diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index 211a58f0344..8f7b9508e60 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -659,7 +659,7 @@ int TDBJSN::ReadDB(PGLOBAL g) if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK)) { // Deferred reading failed } else if (!(Row = ParseJson(g, To_Line, - strlen(To_Line), Pretty, &Comma))) { + strlen(To_Line), &Pretty, &Comma))) { rc = (Pretty == 1 && !strcmp(To_Line, "]")) ? RC_EF : RC_FX; } else { Row = FindRow(g); @@ -755,7 +755,6 @@ int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp) } else strcpy(To_Line, s); -// Row->Clear(); return false; } else return true; @@ -980,7 +979,7 @@ bool JSONCOL::ParseJpath(PGLOBAL g) Nod = colp->Nod; Nodes = colp->Nodes; Xpd = colp->Xpd; - goto fin; + goto fin; } // endif Name sprintf(g->Message, "Cannot parse updated column %s", Name); @@ -1046,7 +1045,8 @@ void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n) switch (val->GetValType()) { case TYPE_STRG: case TYPE_INTG: - case TYPE_DBL: + case TYPE_BINT: + case TYPE_DBL: vp->SetValue_pval(val->GetValue()); break; case TYPE_BOOL: @@ -1353,7 +1353,7 @@ void JSONCOL::WriteColumn(PGLOBAL g) longjmp(g->jumper[g->jump_level], 666); } // endif Xpd - /*********************************************************************/ + /*********************************************************************/ /* Check whether this node must be written. */ /*********************************************************************/ if (Value != To_Val) @@ -1384,7 +1384,7 @@ void JSONCOL::WriteColumn(PGLOBAL g) if (Nodes[Nod-1].Op == OP_XX) { s = Value->GetCharValue(); - if (!(jsp = ParseJson(g, s, (int)strlen(s), 0))) { + if (!(jsp = ParseJson(g, s, (int)strlen(s)))) { strcpy(g->Message, s); longjmp(g->jumper[g->jump_level], 666); } // endif jsp @@ -1522,7 +1522,7 @@ int TDBJSON::MakeDocument(PGLOBAL g) /* Parse the json file and allocate its tree structure. */ /*********************************************************************/ g->Message[0] = 0; - jsp = Top = ParseJson(g, memory, len, Pretty); + jsp = Top = ParseJson(g, memory, len, &Pretty); Txfp->CloseTableFile(g, false); Mode = mode; // Restore saved Mode @@ -1540,7 +1540,7 @@ int TDBJSON::MakeDocument(PGLOBAL g) if (*objpath != '[') { // objpass is a key if (jsp->GetType() != TYPE_JOB) { - strcpy(g->Message, "Table path does no match json file"); + strcpy(g->Message, "Table path does not match the json file"); return RC_FX; } // endif Type @@ -1556,7 +1556,7 @@ int TDBJSON::MakeDocument(PGLOBAL g) } else if (objpath[strlen(objpath)-1] == ']') { if (jsp->GetType() != TYPE_JAR) { - strcpy(g->Message, "Table path does no match json file"); + strcpy(g->Message, "Table path does not match the json file"); return RC_FX; } // endif Type @@ -1837,7 +1837,6 @@ void TDBJSON::CloseDB(PGLOBAL g) // Save the modified document char filename[_MAX_PATH]; PSZ msg; - FILE *fop; Doc->InitArray(g); @@ -1845,11 +1844,7 @@ void TDBJSON::CloseDB(PGLOBAL g) PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath()); // Serialize the modified table - if (!(fop = fopen(filename, "wb"))) { - sprintf(g->Message, MSG(OPEN_MODE_ERROR), - "w", (int)errno, filename); - strcat(strcat(g->Message, ": "), strerror(errno)); - } else if ((msg = Serialize(g, Top, fop, Pretty))) + if ((msg = Serialize(g, Top, filename, Pretty))) puts(msg); } // end of CloseDB diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index f82cca3b514..658f3513b07 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -1054,32 +1054,14 @@ int TDBMYSQL::SendCommand(PGLOBAL g) /***********************************************************************/ /* Data Base indexed read routine for MYSQL access method. */ /***********************************************************************/ -bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len) +bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr) { - bool oom; int oldlen = Query->GetLength(); PHC hc = To_Def->GetHandler(); - if (op == OP_FIRST && hc->end_range) { -#ifdef _DEBUG - assert(!key); -#endif - key_range *end_key = &hc->save_end_range; - - key = end_key->key; - len = end_key->length; - - switch (end_key->flag) { - case HA_READ_BEFORE_KEY: op = OP_LT; break; - case HA_READ_AFTER_KEY: op = OP_LE; break; - default: key = NULL; - } // endswitch flag - - } // endif OP_FIRST - - if (!key || op == OP_NEXT || - Mode == MODE_UPDATE || Mode == MODE_DELETE) { - if (!key && Mode == MODE_READX) { + if (!(kr || hc->end_range) || op == OP_NEXT || + Mode == MODE_UPDATE || Mode == MODE_DELETE) { + if (!kr && Mode == MODE_READX) { // This is a false indexed read m_Rc = Myc.ExecSQL(g, Query->GetStr()); Mode = MODE_READ; @@ -1091,23 +1073,35 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len) if (Myc.m_Res) Myc.FreeResult(); - if (hc->MakeKeyWhere(g, Query, op, '`', key, len)) + if (hc->MakeKeyWhere(g, Query, op, '`', kr)) return true; if (To_CondFil) { - oom = Query->Append(" AND ("); - oom |= Query->Append(To_CondFil->Body); + if (To_CondFil->Idx != hc->active_index) { + To_CondFil->Idx = hc->active_index; + To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0); + *To_CondFil->Body= 0; - if ((oom |= Query->Append(')'))) { - strcpy(g->Message, "Readkey: Out of memory"); - return true; - } // endif oom + if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond))) + PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1); - } // endif To_Condfil + } // endif active_index - } // endif's op + if (To_CondFil) + if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) { + strcpy(g->Message, "Readkey: Out of memory"); + return true; + } // endif Append - m_Rc = Myc.ExecSQL(g, Query->GetStr()); + } // endif To_Condfil + + Mode = MODE_READ; + } // endif's op + + if (trace) + htrc("MYSQL ReadKey: Query=%s\n", Query->GetStr()); + + m_Rc = Myc.ExecSQL(g, Query->GetStr()); Query->Truncate(oldlen); return (m_Rc == RC_FX) ? true : false; } // end of ReadKey diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h index 17d7b190340..edb15b5cca6 100644 --- a/storage/connect/tabmysql.h +++ b/storage/connect/tabmysql.h @@ -99,7 +99,7 @@ class TDBMYSQL : public TDBASE { virtual int WriteDB(PGLOBAL g); virtual int DeleteDB(PGLOBAL g, int irc); virtual void CloseDB(PGLOBAL g); - virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len); + virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr); // Specific routines bool SetColumnRanks(PGLOBAL g); diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index 31854870ed2..fd9a049a05a 100644 --- a/storage/connect/tabodbc.cpp +++ b/storage/connect/tabodbc.cpp @@ -1,7 +1,7 @@ /************* Tabodbc C++ Program Source Code File (.CPP) *************/ /* PROGRAM NAME: TABODBC */ /* ------------- */ -/* Version 2.9 */ +/* Version 3.0 */ /* */ /* COPYRIGHT: */ /* ---------- */ @@ -35,6 +35,7 @@ /* Include relevant MariaDB header file. */ /***********************************************************************/ #include "my_global.h" +#include "sql_class.h" #if defined(__WIN__) #include #include @@ -72,6 +73,7 @@ #include "reldef.h" #include "tabcol.h" #include "valblk.h" +#include "ha_connect.h" #include "sql_string.h" @@ -322,7 +324,7 @@ PSZ TDBODBC::GetFile(PGLOBAL g) if (Connect) { char *p1, *p2; int i; - size_t n; + size_t n; if (!(p1 = strstr(Connect, "DBQ="))) { char *p, *lc = strlwr(PlugDup(g, Connect)); @@ -334,8 +336,8 @@ PSZ TDBODBC::GetFile(PGLOBAL g) } else i = 4; - if (p1) { - p1 += i; // Beginning of file name + if (p1) { + p1 += i; // Beginning of file name p2 = strchr(p1, ';'); // End of file path/name // Make the File path/name from the connect string @@ -397,176 +399,209 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n) /* Note: when implementing EOM filtering, column only used in local */ /* filter should be removed from column list. */ /***********************************************************************/ -char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt) +bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt) { - char *colist, *tabname, *sql, buf[NAM_LEN * 3]; - LPCSTR schmp = NULL, catp = NULL; - int len, ncol = 0; - bool first = true; - PTABLE tablep = To_Table; - PCOL colp; + char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3]; + int len; + bool oom = false, first = true; + PTABLE tablep = To_Table; + PCOL colp; - if (Srcdef) - return Srcdef; + if (Srcdef) { + Query = new(g)STRING(g, 0, Srcdef); + return false; + } // endif Srcdef - if (!cnt) { - // Normal SQL statement to retrieve results - for (colp = Columns; colp; colp = colp->GetNext()) - if (!colp->IsSpecial()) - ncol++; + // Allocate the string used to contain the Query + Query = new(g)STRING(g, 1023, "SELECT "); - if (ncol) { - colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol); + if (!cnt) { + if (Columns) { + // Normal SQL statement to retrieve results + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) { + if (!first) + oom |= Query->Append(", "); + else + first = false; - for (colp = Columns; colp; colp = colp->GetNext()) - if (!colp->IsSpecial()) { - // Column name can be in UTF-8 encoding - /*rc=*/ Decode(colp->GetName(), buf, sizeof(buf)); + // Column name can be encoded in UTF-8 + Decode(colp->GetName(), buf, sizeof(buf)); - if (Quote) { - if (first) { - strcat(strcat(strcpy(colist, Quote), buf), Quote); - first = false; - } else - strcat(strcat(strcat(strcat(colist, ", "), - Quote), buf), Quote); + if (Quote) { + // Put column name between identifier quotes in case in contains blanks + oom |= Query->Append(Quote); + oom |= Query->Append(buf); + oom |= Query->Append(Quote); + } else + oom |= Query->Append(buf); - } else { - if (first) { - strcpy(colist, buf); - first = false; - } else - strcat(strcat(colist, ", "), buf); + } // endif colp - } // endif Quote - - } // endif !Special - - } else { - // ncol == 0 can occur for queries such that sql count(*) from... + } else + // !Columns can occur for queries such that sql count(*) from... // for which we will count the rows from sql * from... - colist = (char*)PlugSubAlloc(g, NULL, 2); - strcpy(colist, "*"); - } // endif ncol + oom |= Query->Append('*'); - } else { + } else // SQL statement used to retrieve the size of the result - colist = (char*)PlugSubAlloc(g, NULL, 9); - strcpy(colist, "count(*)"); - } // endif cnt + oom |= Query->Append("count(*)"); - // Table name can be encoded in UTF-8 - /*rc = */Decode(TableName, buf, sizeof(buf)); - - // Put table name between identifier quotes in case in contains blanks - tabname = (char*)PlugSubAlloc(g, NULL, strlen(buf) + 3); - - if (Quote) - strcat(strcat(strcpy(tabname, Quote), buf), Quote); - else - strcpy(tabname, buf); - - // Below 14 is length of 'select ' + length of ' from ' + 1 - len = (strlen(colist) + strlen(buf) + 14); - len += (To_CondFil ? strlen(To_CondFil->Body) + 7 : 0); + oom |= Query->Append(" FROM "); if (Catalog && *Catalog) catp = Catalog; - if (catp) - len += (strlen(catp) + 2); - if (tablep->GetSchema()) - schmp = tablep->GetSchema(); + schmp = (char*)tablep->GetSchema(); else if (Schema && *Schema) schmp = Schema; - if (schmp) - len += (strlen(schmp) + 1); - - sql = (char*)PlugSubAlloc(g, NULL, len); - strcat(strcat(strcpy(sql, "SELECT "), colist), " FROM "); - if (catp) { - strcat(sql, catp); + oom |= Query->Append(catp); - if (schmp) - strcat(strcat(sql, "."), schmp); - else - strcat(sql, "."); + if (schmp) { + oom |= Query->Append('.'); + oom |= Query->Append(schmp); + } // endif schmp - strcat(sql, "."); - } else if (schmp) - strcat(strcat(sql, schmp), "."); + oom |= Query->Append('.'); + } else if (schmp) { + oom |= Query->Append(schmp); + oom |= Query->Append('.'); + } // endif schmp - strcat(sql, tabname); + // Table name can be encoded in UTF-8 + Decode(TableName, buf, sizeof(buf)); - if (To_CondFil) - strcat(strcat(sql, " WHERE "), To_CondFil->Body); - - if (trace) - htrc("sql: '%s'\n", sql); + if (Quote) { + // Put table name between identifier quotes in case in contains blanks + oom |= Query->Append(Quote); + oom |= Query->Append(buf); + oom |= Query->Append(Quote); + } else + oom |= Query->Append(buf); - return sql; + len = Query->GetLength(); + + if (To_CondFil) { + if (Mode == MODE_READ) { + oom |= Query->Append(" WHERE "); + oom |= Query->Append(To_CondFil->Body); + len = Query->GetLength() + 1; + } else + len += (strlen(To_CondFil->Body) + 256); + + } else + len += ((Mode == MODE_READX) ? 256 : 1); + + if (oom || Query->Resize(len)) { + strcpy(g->Message, "MakeSQL: Out of memory"); + return true; + } // endif oom + + if (trace) + htrc("Query=%s\n", Query->GetStr()); + + return false; } // end of MakeSQL /***********************************************************************/ /* MakeInsert: make the Insert statement used with ODBC connection. */ /***********************************************************************/ -char *TDBODBC::MakeInsert(PGLOBAL g) +bool TDBODBC::MakeInsert(PGLOBAL g) { - char *stmt, *colist, *valist, buf[NAM_LEN * 3]; -// char *tk = "`"; - int len = 0; - bool b = FALSE; - PCOL colp; + char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3]; + int len = 0; + bool b = false, oom = false; + PTABLE tablep = To_Table; + PCOL colp; for (colp = Columns; colp; colp = colp->GetNext()) if (colp->IsSpecial()) { strcpy(g->Message, MSG(NO_ODBC_SPECOL)); - return NULL; + return true; } else { - len += (strlen(colp->GetName()) + 4); + // Column name can be encoded in UTF-8 + Decode(colp->GetName(), buf, sizeof(buf)); + len += (strlen(buf) + 6); // comma + quotes + valist ((PODBCCOL)colp)->Rank = ++Nparm; } // endif colp - colist = (char*)PlugSubAlloc(g, NULL, len); - *colist = '\0'; - valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm); - *valist = '\0'; + // Below 32 is enough to contain the fixed part of the query + if (Catalog && *Catalog) + catp = Catalog; - for (colp = Columns; colp; colp = colp->GetNext()) { - if (b) { - strcat(colist, ", "); - strcat(valist, ","); - } else - b = true; + if (catp) + len += strlen(catp) + 1; - // Column name can be in UTF-8 encoding - Decode(colp->GetName(), buf, sizeof(buf)); + if (tablep->GetSchema()) + schmp = (char*)tablep->GetSchema(); + else if (Schema && *Schema) + schmp = Schema; - if (Quote) - strcat(strcat(strcat(colist, Quote), buf), Quote); - else - strcat(colist, buf); + if (schmp) + len += strlen(schmp) + 1; - strcat(valist, "?"); // Parameter marker - } // endfor colp + // Column name can be encoded in UTF-8 + Decode(TableName, buf, sizeof(buf)); + len += (strlen(buf) + 32); + Query = new(g) STRING(g, len, "INSERT INTO "); - // Below 32 is enough to contain the fixed part of the query - len = (strlen(TableName) + strlen(colist) + strlen(valist) + 32); - stmt = (char*)PlugSubAlloc(g, NULL, len); - strcpy(stmt, "INSERT INTO "); + if (catp) { + oom |= Query->Append(catp); - if (Quote) - strcat(strcat(strcat(stmt, Quote), TableName), Quote); - else - strcat(stmt, TableName); + if (schmp) { + oom |= Query->Append('.'); + oom |= Query->Append(schmp); + } // endif schmp - strcat(strcat(strcat(stmt, " ("), colist), ") VALUES ("); - strcat(strcat(stmt, valist), ")"); + oom |= Query->Append('.'); + } else if (schmp) { + oom |= Query->Append(schmp); + oom |= Query->Append('.'); + } // endif schmp - return stmt; + if (Quote) { + // Put table name between identifier quotes in case in contains blanks + oom |= Query->Append(Quote); + oom |= Query->Append(buf); + oom |= Query->Append(Quote); + } else + oom |= Query->Append(buf); + + oom |= Query->Append('('); + + for (colp = Columns; colp; colp = colp->GetNext()) { + if (b) + oom |= Query->Append(", "); + else + b = true; + + // Column name can be in UTF-8 encoding + Decode(colp->GetName(), buf, sizeof(buf)); + + if (Quote) { + // Put column name between identifier quotes in case in contains blanks + oom |= Query->Append(Quote); + oom |= Query->Append(buf); + oom |= Query->Append(Quote); + } else + oom |= Query->Append(buf); + + } // endfor colp + + oom |= Query->Append(") VALUES ("); + + for (int i = 0; i < Nparm; i++) + oom |= Query->Append("?,"); + + if (oom) + strcpy(g->Message, "MakeInsert: Out of memory"); + else + Query->RepLast(')'); + + return oom; } // end of MakeInsert /***********************************************************************/ @@ -591,7 +626,7 @@ bool TDBODBC::BindParameters(PGLOBAL g) /* MakeCommand: make the Update or Delete statement to send to the */ /* MySQL server. Limited to remote values and filtering. */ /***********************************************************************/ -char *TDBODBC::MakeCommand(PGLOBAL g) +bool TDBODBC::MakeCommand(PGLOBAL g) { char *p, *stmt, name[68], *body = NULL, *qc = Ocp->GetQuoteChar(); char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1); @@ -649,7 +684,8 @@ char *TDBODBC::MakeCommand(PGLOBAL g) return NULL; } // endif p - return stmt; + Query = new(g) STRING(g, 0, stmt); + return (!Query->GetSize()); } // end of MakeCommand #if 0 @@ -826,10 +862,12 @@ bool TDBODBC::OpenDB(PGLOBAL g) if (Memory < 3) { // Method will depend on cursor type - if ((Rbuf = Ocp->Rewind(Query, (PODBCCOL)Columns)) < 0) { - Ocp->Close(); - return true; - } // endif Rewind + if ((Rbuf = Ocp->Rewind(Query->GetStr(), (PODBCCOL)Columns)) < 0) + if (Mode != MODE_READX) { + Ocp->Close(); + return true; + } else + Rbuf = 0; } else Rbuf = Qrp->Nblin; @@ -864,15 +902,14 @@ bool TDBODBC::OpenDB(PGLOBAL g) /*********************************************************************/ if (Mode == MODE_READ || Mode == MODE_READX) { if (Memory > 1 && !Srcdef) { - char *Sql; - int n; + int n; - if ((Sql = MakeSQL(g, true))) { + if (!MakeSQL(g, true)) { // Allocate a Count(*) column Cnp = new(g) ODBCCOL; Cnp->InitValue(g); - if ((n = Ocp->GetResultSize(Sql, Cnp)) < 0) { + if ((n = Ocp->GetResultSize(Query->GetStr(), Cnp)) < 0) { strcpy(g->Message, "Cannot get result size"); return true; } // endif n @@ -882,36 +919,36 @@ bool TDBODBC::OpenDB(PGLOBAL g) if ((Qrp = Ocp->AllocateResult(g))) Memory = 2; // Must be filled else { - strcpy(g->Message, "Memory allocation failed"); + strcpy(g->Message, "Result set memory allocation failed"); return true; } // endif n Ocp->m_Rows = 0; - } else { - strcpy(g->Message, "MakeSQL failed"); + } else return true; - } // endif Sql } // endif Memory - if ((Query = MakeSQL(g, false))) { + if (!(rc = MakeSQL(g, false))) { for (PODBCCOL colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->GetNext()) if (!colp->IsSpecial()) colp->AllocateBuffers(g, Rows); - rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0); - } // endif Query + rc = (Mode == MODE_READ) + ? ((Rows = Ocp->ExecDirectSQL(Query->GetStr(), (PODBCCOL)Columns)) < 0) + : false; + } // endif rc } else if (Mode == MODE_INSERT) { - if ((Query = MakeInsert(g))) { - if (Nparm != Ocp->PrepareSQL(Query)) { + if (!(rc = MakeInsert(g))) { + if (Nparm != Ocp->PrepareSQL(Query->GetStr())) { strcpy(g->Message, MSG(PARM_CNT_MISS)); rc = true; } else rc = BindParameters(g); - } // endif Query + } // endif rc } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) { rc = false; // wait for CheckCond before calling MakeCommand(g); @@ -969,6 +1006,59 @@ bool TDBODBC::SetRecpos(PGLOBAL g, int recpos) return false; } // end of SetRecpos +/***********************************************************************/ +/* Data Base indexed read routine for MYSQL access method. */ +/***********************************************************************/ +bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr) +{ + char c = Quote ? *Quote : 0; + int oldlen = Query->GetLength(); + PHC hc = To_Def->GetHandler(); + + if (!(kr || hc->end_range) || op == OP_NEXT || + Mode == MODE_UPDATE || Mode == MODE_DELETE) { + if (!kr && Mode == MODE_READX) { + // This is a false indexed read + Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns); + Mode = MODE_READ; + return (Rows < 0); + } // endif key + + return false; + } else { + if (To_Def->GetHandler()->MakeKeyWhere(g, Query, op, c, kr)) + return true; + + if (To_CondFil) { + if (To_CondFil->Idx != hc->active_index) { + To_CondFil->Idx = hc->active_index; + To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0); + *To_CondFil->Body= 0; + + if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond))) + PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1); + + } // endif active_index + + if (To_CondFil) + if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) { + strcpy(g->Message, "Readkey: Out of memory"); + return true; + } // endif Append + + } // endif To_Condfil + + Mode = MODE_READ; + } // endif's op + + if (trace) + htrc("ODBC ReadKey: Query=%s\n", Query->GetStr()); + + Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns); + Query->Truncate(oldlen); + return (Rows < 0); +} // end of ReadKey + /***********************************************************************/ /* VRDNDOS: Data Base read routine for odbc access method. */ /***********************************************************************/ @@ -981,11 +1071,11 @@ int TDBODBC::ReadDB(PGLOBAL g) GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) { - if (!Query && !(Query = MakeCommand(g))) + if (!Query && MakeCommand(g)) return RC_FX; // Send the UPDATE/DELETE command to the remote table - if (!Ocp->ExecSQLcommand(Query)) { + if (!Ocp->ExecSQLcommand(Query->GetStr())) { sprintf(g->Message, "%s: %d affected rows", TableName, AftRows); if (trace) @@ -1063,11 +1153,11 @@ int TDBODBC::WriteDB(PGLOBAL g) int TDBODBC::DeleteDB(PGLOBAL g, int irc) { if (irc == RC_FX) { - if (!Query && !(Query = MakeCommand(g))) + if (!Query && MakeCommand(g)) return RC_FX; // Send the DELETE (all) command to the remote table - if (!Ocp->ExecSQLcommand(Query)) { + if (!Ocp->ExecSQLcommand(Query->GetStr())) { sprintf(g->Message, "%s: %d affected rows", TableName, AftRows); if (trace) @@ -1279,12 +1369,7 @@ void ODBCCOL::ReadColumn(PGLOBAL g) } // endif Buf_Type - // Nulls are handled by StrLen[n] == SQL_NULL_DATA - // MDEV-8561 -//if (Value->IsZero()) -// Value->SetNull(Nullable); - - if (trace) { + if (trace > 1) { char buf[64]; htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n", @@ -1572,9 +1657,12 @@ bool TDBXDBC::OpenDB(PGLOBAL g) int TDBXDBC::ReadDB(PGLOBAL g) { if (Cmdlist) { - Query = Cmdlist->Cmd; + if (!Query) + Query = new(g)STRING(g, 0, Cmdlist->Cmd); + else + Query->Set(Cmdlist->Cmd); - if (Ocp->ExecSQLcommand(Query)) + if (Ocp->ExecSQLcommand(Query->GetStr())) Nerr++; Fpos++; // Used for progress info @@ -1632,10 +1720,10 @@ void XSRCCOL::ReadColumn(PGLOBAL g) PTDBXDBC tdbp = (PTDBXDBC)To_Tdb; switch (Flag) { - case 0: Value->SetValue_psz(tdbp->Query); break; - case 1: Value->SetValue(tdbp->AftRows); break; - case 2: Value->SetValue_psz(g->Message); break; - default: Value->SetValue_psz("Invalid Flag"); break; + case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break; + case 1: Value->SetValue(tdbp->AftRows); break; + case 2: Value->SetValue_psz(g->Message); break; + default: Value->SetValue_psz("Invalid Flag"); break; } // endswitch Flag } // end of ReadColumn diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h index b8c9d85aae5..6440dee830d 100644 --- a/storage/connect/tabodbc.h +++ b/storage/connect/tabodbc.h @@ -42,7 +42,8 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */ int GetOptions(void) {return Options;} // Methods - virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual int Indexable(void) {return 2;} + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE m); protected: @@ -111,15 +112,14 @@ class TDBODBC : public TDBASE { virtual int WriteDB(PGLOBAL g); virtual int DeleteDB(PGLOBAL g, int irc); virtual void CloseDB(PGLOBAL g); - virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len) - {return true;} + virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr); protected: // Internal functions int Decode(char *utf, char *buf, size_t n); - char *MakeSQL(PGLOBAL g, bool cnt); - char *MakeInsert(PGLOBAL g); - char *MakeCommand(PGLOBAL g); + bool MakeSQL(PGLOBAL g, bool cnt); + bool MakeInsert(PGLOBAL g); + bool MakeCommand(PGLOBAL g); //bool MakeFilter(PGLOBAL g, bool c); bool BindParameters(PGLOBAL g); //char *MakeUpdate(PGLOBAL g); @@ -129,14 +129,14 @@ class TDBODBC : public TDBASE { ODBConn *Ocp; // Points to an ODBC connection class ODBCCOL *Cnp; // Points to count(*) column ODBCPARM Ops; // Additional parameters - char *Connect; // Points to connection string + PSTRG Query; // Constructed SQL query + char *Connect; // Points to connection string char *TableName; // Points to ODBC table name char *Schema; // Points to ODBC table Schema char *User; // User connect info char *Pwd; // Password connect info char *Catalog; // Points to ODBC table Catalog char *Srcdef; // The source table SQL definition - char *Query; // Points to SQL statement char *Count; // Points to count(*) SQL statement //char *Where; // Points to local where clause char *Quote; // The identifier quoting character diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index 49fa9a1c554..9c47829d5bc 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -60,6 +60,7 @@ extern "C" char version[]; #endif // !__WIN__ #define TYPE_UNKNOWN 12 /* Must be greater than other types */ +#define XSTR(M) sizeof(M) - strlen(M) - 1 /* To avoid overflow*/ /***********************************************************************/ /* Class and structure used by XMLColumns. */ @@ -225,30 +226,30 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) more: if (vp->atp) { strncpy(colname, vp->atp->GetName(g), sizeof(colname)); - strncat(xcol->Name, colname, 64); + strncat(xcol->Name, colname, XSTR(xcol->Name)); switch (vp->atp->GetText(g, buf, sizeof(buf))) { case RC_INFO: PushWarning(g, txmp); case RC_OK: - strncat(fmt, "@", sizeof(fmt)); + strncat(fmt, "@", XSTR(fmt)); break; default: goto err; } // enswitch rc if (j) - strncat(fmt, colname, sizeof(fmt)); + strncat(fmt, colname, XSTR(fmt)); } else { if (tdp->Usedom && node->GetType() != 1) continue; strncpy(colname, node->GetName(g), sizeof(colname)); - strncat(xcol->Name, colname, 64); + strncat(xcol->Name, colname, XSTR(xcol->Name)); if (j) - strncat(fmt, colname, sizeof(fmt)); + strncat(fmt, colname, XSTR(fmt)); if (j < lvl && ok) { vp = lvlp[j+1]; @@ -266,9 +267,10 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) if (!vp->atp) node = vp->nl->GetItem(g, vp->k++, node); - strncat(strncat(fmt, colname, 125), "/", 125); - strncat(xcol->Name, "_", 64); - j++; + strncat(fmt, colname, XSTR(fmt)); + strncat(fmt, "/", XSTR(fmt)); + strncat(xcol->Name, "_", XSTR(xcol->Name)); + j++; vp->n = (int)strlen(xcol->Name); vp->m = (int)strlen(fmt); goto more; diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp index 884ce976a52..ab4ed5f32a3 100644 --- a/storage/connect/value.cpp +++ b/storage/connect/value.cpp @@ -340,7 +340,7 @@ PVAL AllocateValue(PGLOBAL g, void *value, short type, short prec) switch (type) { case TYPE_STRING: - valp = new(g) TYPVAL((PSZ)value); + valp = new(g) TYPVAL((PSZ)value, prec); break; case TYPE_SHORT: valp = new(g) TYPVAL(*(short*)value, TYPE_SHORT); @@ -1209,12 +1209,12 @@ void TYPVAL::Print(PGLOBAL g, char *ps, uint z) /***********************************************************************/ /* STRING public constructor from a constant string. */ /***********************************************************************/ -TYPVAL::TYPVAL(PSZ s) : VALUE(TYPE_STRING) +TYPVAL::TYPVAL(PSZ s, short c) : VALUE(TYPE_STRING) { Strp = s; Len = strlen(s); Clen = Len; - Ci = false; + Ci = (c == 1); } // end of STRING constructor /***********************************************************************/ @@ -2440,6 +2440,7 @@ void DTVAL::SetTimeShift(void) } // end of SetTimeShift +#if defined(connect_EXPORTS) // Added by Alexander Barkov static void TIME_to_localtime(struct tm *tm, const MYSQL_TIME *ltime) { @@ -2461,6 +2462,9 @@ static struct tm *gmtime_mysql(const time_t *timep, struct tm *tm) TIME_to_localtime(tm, <ime); return tm; } // end of gmtime_mysql +#else +#define gmtime_mysql(T,B) gmtime((const time_t *)T) +#endif /***********************************************************************/ /* GetGmTime: returns a pointer to a static tm structure obtained */ @@ -2489,6 +2493,7 @@ struct tm *DTVAL::GetGmTime(struct tm *tm_buffer) return datm; } // end of GetGmTime +#if defined(connect_EXPORTS) // Added by Alexander Barkov static time_t mktime_mysql(struct tm *ptm) { @@ -2499,6 +2504,9 @@ static time_t mktime_mysql(struct tm *ptm) time_t t= TIME_to_timestamp(current_thd, <ime, &error_code); return error_code ? (time_t) -1 : t; } +#else +#define mktime_mysql mktime +#endif /***********************************************************************/ /* MakeTime: calculates a date value from a tm structures using the */ diff --git a/storage/connect/value.h b/storage/connect/value.h index 471da851423..c5a381e89da 100644 --- a/storage/connect/value.h +++ b/storage/connect/value.h @@ -236,7 +236,7 @@ template <> class DllExport TYPVAL: public VALUE { public: // Constructors - TYPVAL(PSZ s); + TYPVAL(PSZ s, short c = 0); TYPVAL(PGLOBAL g, PSZ s, int n, int c); // Implementation diff --git a/storage/connect/xobject.cpp b/storage/connect/xobject.cpp index a6faebf3c2b..a0b7849543d 100644 --- a/storage/connect/xobject.cpp +++ b/storage/connect/xobject.cpp @@ -291,14 +291,14 @@ bool STRING::Set(char *s, uint n) } // end of Set /***********************************************************************/ -/* Append a char* to a STRING. */ +/* Append a char* to a STRING. */ /***********************************************************************/ -bool STRING::Append(const char *s, uint ln) +bool STRING::Append(const char *s, uint ln, bool nq) { if (!s) return false; - uint len = Length + ln + 1; + uint i, len = Length + ln + 1; if (len > Size) { char *p = Realloc(len); @@ -312,8 +312,22 @@ bool STRING::Append(const char *s, uint ln) } // endif n - strncpy(Strp + Length, s, ln); - Length = len - 1; + if (nq) { + for (i = 0; i < ln; i++) + switch (s[i]) { + case '\\': Strp[Length++] = '\\'; Strp[Length++] = '\\'; break; + case '\0': Strp[Length++] = '\\'; Strp[Length++] = '0'; break; + case '\'': Strp[Length++] = '\\'; Strp[Length++] = '\''; break; + case '\n': Strp[Length++] = '\\'; Strp[Length++] = 'n'; break; + case '\r': Strp[Length++] = '\\'; Strp[Length++] = 'r'; break; + case '\032': Strp[Length++] = '\\'; Strp[Length++] = 'Z'; break; + default: Strp[Length++] = s[i]; + } // endswitch s[i] + + } else + for (i = 0; i < ln && s[i]; i++) + Strp[Length++] = s[i]; + Strp[Length] = 0; return false; } // end of Append diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h index 82ff9e21225..d78cd09f9a4 100644 --- a/storage/connect/xobject.h +++ b/storage/connect/xobject.h @@ -134,7 +134,7 @@ class DllExport STRING : public BLOCK { inline void Reset(void) {*Strp = 0;} bool Set(PSZ s); bool Set(char *s, uint n); - bool Append(const char *s, uint ln); + bool Append(const char *s, uint ln, bool nq = false); bool Append(PSZ s); bool Append(STRING &str); bool Append(char c); diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h index 1a75d97bafa..e18a08a54b8 100644 --- a/storage/connect/xtable.h +++ b/storage/connect/xtable.h @@ -19,6 +19,7 @@ #include "m_ctype.h" typedef class CMD *PCMD; +typedef struct st_key_range key_range; // Commands executed by XDBC and MYX tables class CMD : public BLOCK { @@ -32,12 +33,24 @@ class CMD : public BLOCK { }; // end of class CMD // Condition filter structure -typedef struct _cond_filter { - char *Body; - OPVAL Op; - PCMD Cmds; -} CONDFIL, *PCFIL; +class CONDFIL : public BLOCK { + public: + // Constructor + CONDFIL(const Item *cond, uint idx, AMT type) + { + Cond = cond; Idx = idx; Type = type; Body = NULL; Op = OP_XX; Cmds = NULL; + } + // Members + const Item *Cond; + AMT Type; + uint Idx; + char *Body; + OPVAL Op; + PCMD Cmds; +}; // end of class CONDFIL + +typedef class CONDFIL *PCFIL; typedef class TDBCAT *PTDBCAT; typedef class CATCOL *PCATCOL; @@ -109,7 +122,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block. virtual int DeleteDB(PGLOBAL, int) = 0; virtual void CloseDB(PGLOBAL) = 0; virtual int CheckWrite(PGLOBAL) {return 0;} - virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int) = 0; + virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *) = 0; protected: // Members @@ -188,7 +201,7 @@ class DllExport TDBASE : public TDB { virtual void MarkDB(PGLOBAL g, PTDB tdb2); virtual int MakeIndex(PGLOBAL g, PIXDEF, bool) {strcpy(g->Message, "Remote index"); return RC_INFO;} - virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int) + virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *) {assert(false); return true;} protected: diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 03beeca6591..5f9443e6bc4 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -112,6 +112,7 @@ UNIV_INTERN ulint os_innodb_umask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; #else /** Umask for creating files */ UNIV_INTERN ulint os_innodb_umask = 0; +#define ECANCELED 125 #endif /* __WIN__ */ #ifndef UNIV_HOTBACKUP diff --git a/storage/xtradb/os/os0file.cc b/storage/xtradb/os/os0file.cc index f72d8169da4..0db50d3e9de 100644 --- a/storage/xtradb/os/os0file.cc +++ b/storage/xtradb/os/os0file.cc @@ -117,6 +117,7 @@ UNIV_INTERN ulint os_innodb_umask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; #else /** Umask for creating files */ UNIV_INTERN ulint os_innodb_umask = 0; +#define ECANCELED 125 #endif /* __WIN__ */ #ifndef UNIV_HOTBACKUP diff --git a/win/packaging/heidisql.cmake b/win/packaging/heidisql.cmake index 5b0cd07cea4..dfde0cc53b3 100644 --- a/win/packaging/heidisql.cmake +++ b/win/packaging/heidisql.cmake @@ -4,7 +4,7 @@ SET(HEIDISQL_URL "http://www.heidisql.com/downloads/releases/${HEIDISQL_ZIP}") SET(HEIDISQL_DOWNLOAD_DIR ${THIRD_PARTY_DOWNLOAD_LOCATION}/${HEIDISQL_BASE_NAME}) IF(NOT EXISTS ${HEIDISQL_DOWNLOAD_DIR}/${HEIDISQL_ZIP}) - MAKE_DIRECTORY(${HEIDISQL_DOWNLOAD_DIR}) + MAKE_DIRECTORY(${HEIDISQL_DOWNLOAD_DIR}) MESSAGE(STATUS "Downloading ${HEIDISQL_URL} to ${HEIDISQL_DOWNLOAD_DIR}/${HEIDISQL_ZIP}") FILE(DOWNLOAD ${HEIDISQL_URL} ${HEIDISQL_DOWNLOAD_DIR}/${HEIDISQL_ZIP} TIMEOUT 60) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E chdir ${HEIDISQL_DOWNLOAD_DIR} diff --git a/win/packaging/heidisql.wxi.in b/win/packaging/heidisql.wxi.in index 4f07a07627c..ce9f668e291 100644 --- a/win/packaging/heidisql.wxi.in +++ b/win/packaging/heidisql.wxi.in @@ -10,16 +10,16 @@ - + -