From 4d17053699420837099f241440a38fe2cdd96b14 Mon Sep 17 00:00:00 2001 From: Ondrej Musil Date: Wed, 27 Sep 2017 15:40:23 +0200 Subject: [PATCH 1/3] Update .jenkins.prop --- .jenkins.prop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins.prop b/.jenkins.prop index 0735c04..0c77223 100644 --- a/.jenkins.prop +++ b/.jenkins.prop @@ -1 +1 @@ -QORE_BRANCH_NAME=0.8.13 +QORE_BRANCH_NAME=develop From 046d111015c3692a64f27b123a968312dfa730b6 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Sat, 21 Oct 2017 17:55:11 +0200 Subject: [PATCH 2/3] refs qorelanguage/qore#2322 added the EmitSqlNull constant and support for SQL NULL serialization and deserialization + docs & tests --- CMakeLists.txt | 2 +- configure.ac | 2 +- docs/mainpage.doxygen.tmpl | 6 +- qore-yaml-module.spec | 5 +- src/QoreYamlEmitter.cpp | 121 ++++++++-------- src/QoreYamlParser.cpp | 288 +++++++++++++++++++------------------ src/ql_yaml.qpp | 5 +- src/yaml-module.cpp | 4 +- src/yaml-module.h | 96 +++++++------ test/yaml.qtest | 19 +++ 10 files changed, 298 insertions(+), 250 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 09621e1..444b332 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8.3) project(qore-yaml-module) set (VERSION_MAJOR 0) -set (VERSION_MINOR 5) +set (VERSION_MINOR 7) set (VERSION_PATCH 0) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked diff --git a/configure.ac b/configure.ac index 202e157..4d8dba8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script. # AC_PREREQ(2.59) -AC_INIT([qore-yaml-module], [0.6], +AC_INIT([qore-yaml-module], [0.7], [David Nichols ], [qore-yaml-module]) AM_INIT_AUTOMAKE([no-dist-gzip dist-bzip2 tar-ustar]) diff --git a/docs/mainpage.doxygen.tmpl b/docs/mainpage.doxygen.tmpl index 59e2af7..1e11bc5 100644 --- a/docs/mainpage.doxygen.tmpl +++ b/docs/mainpage.doxygen.tmpl @@ -80,12 +80,16 @@ any data = parse_yaml(yaml_str); |date (relative)|\c !duration|\c P2M3DT10H14u|\c P2M3DT10H14u|Relative date/time values (durations) are serialized with Qore's ISO-8601-based format.

This tag is a custom tag used only by Qore to serialize Qore relative date/time values with YAML |date (absolute)|\c !!timestamp|\c 2010-05-05T15:35:02.100|\c 2010-05-05T15:35:02.1+02:00|Absolute date/time values are serialized with YAML's timestamp format.

Note that qore date/time values without an explicit time zone are assumed to be in the local time zone.

When converting a YAML timestamp to a Qore date, because Qore supports only up to microsecond resolution in date/time values, any digits after microseconds are lost. |NOTHING|\c !!null|\c NOTHING|\c null|direct serialization - |NULL|\c !!null|\c NULL|\c null|serialization to YAML null, just like \c NOTHING; will be deserialized as \c NOTHING. + |NULL|\c !!null or \c !sqlnull|\c NULL|\c null or \c !sqlnull|without @ref @ref Qore::YAML::EmitSqlNull "EmitSqlNull", serialization to YAML null, just like \c NOTHING; will be deserialized as \c NOTHING, with @ref Qore::YAML::EmitSqlNull "EmitSqlNull" will be deserialized to \c NULL. |list|\c !!seq|\c (1, 2, "three")|\c [1, 2, "three"]|direct serialization |hash|\c !!map|\c ("key" : 1, "other" : 2.0, "data" : "three")|\c {key: 1, other: 2.0, data: "three"}|direct serialization, although qore will maintain key order as well even though this property is only defined for an ordered map @section yamlreleasenotes Release Notes + @subsection yaml07 yaml Module Version 0.7 + + - added optional support for serializing SQL NULL values with @ref Qore::YAML::EmitSqlNull "EmitSqlNull" + @subsection yaml06 yaml Module Version 0.6 - improved the description for the \c DESERIALIZATION-ERROR exception for non-deserializable message bodies from HTTP servers with error responses (issue 1033) diff --git a/qore-yaml-module.spec b/qore-yaml-module.spec index 92809ae..ef762f7 100644 --- a/qore-yaml-module.spec +++ b/qore-yaml-module.spec @@ -1,4 +1,4 @@ -%global mod_ver 0.6 +%global mod_ver 0.7 %{?_datarootdir: %global mydatarootdir %_datarootdir} %{!?_datarootdir: %global mydatarootdir /usr/share} @@ -100,6 +100,9 @@ rm -rf $RPM_BUILD_ROOT %doc COPYING.LGPL COPYING.MIT README RELEASE-NOTES AUTHORS %changelog +* Sat Oct 21 2017 David Nichols 0.7 +- updated to version 0.7 + * Wed Nov 23 2016 David Nichols 0.6 - updated to version 0.6 diff --git a/src/QoreYamlEmitter.cpp b/src/QoreYamlEmitter.cpp index d0c9454..8e26cc7 100644 --- a/src/QoreYamlEmitter.cpp +++ b/src/QoreYamlEmitter.cpp @@ -2,7 +2,7 @@ /* yaml Qore module - Copyright (C) 2010 - 2013 David Nichols, all rights reserved + Copyright (C) 2010 - 2017 Qore Technologies, s.r.o. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -31,6 +31,7 @@ QoreYamlEmitter::QoreYamlEmitter(QoreYamlWriteHandler &n_wh, int flags, int widt : QoreYamlBase(n_xsink), wh(n_wh), block(flags & QYE_BLOCK_STYLE), implicit_start_doc(!(flags & QYE_EXPLICIT_START_DOC)), implicit_end_doc(!(flags & QYE_EXPLICIT_END_DOC)), + emit_sqlnull(flags & QYE_EMIT_SQLNULL), yaml_ver(0) { if (!yaml_emitter_initialize(&emitter)) { err("unknown error initializing yaml emitter"); @@ -68,39 +69,43 @@ QoreYamlEmitter::QoreYamlEmitter(QoreYamlWriteHandler &n_wh, int flags, int widt int QoreYamlEmitter::emit(const QoreValue& v) { switch (v.getType()) { case NT_STRING: - return emitValue(*v.get()); + return emitValue(*v.get()); case NT_INT: - return emitValue(v.getAsBigInt()); + return emitValue(v.getAsBigInt()); case NT_FLOAT: - return emitValue(v.getAsFloat()); + return emitValue(v.getAsFloat()); case NT_NUMBER: - return emitValue(*v.get()); + return emitValue(*v.get()); case NT_BOOLEAN: - return emitValue(v.getAsBool()); + return emitValue(v.getAsBool()); case NT_LIST: - return emitValue(*v.get()); + return emitValue(*v.get()); case NT_HASH: - return emitValue(*v.get()); + return emitValue(*v.get()); case NT_DATE: - return emitValue(*v.get()); + return emitValue(*v.get()); case NT_BINARY: - return emitValue(*v.get()); + return emitValue(*v.get()); case NT_NULL: + if (emit_sqlnull) + return emitSqlNull(); + // fall down to nothing + case NT_NOTHING: - return emitNull(); + return emitNull(); default: - err("cannot convert Qore type '%s' to YAML", v.getTypeName()); - return -1; + err("cannot convert Qore type '%s' to YAML", v.getTypeName()); + return -1; } return 0; @@ -115,43 +120,43 @@ int QoreYamlEmitter::emitValue(const DateTime &d) { if (d.isRelative()) { str.concat('P'); if (d.hasValue()) { - if (info.year) - str.sprintf("%dY", info.year); - if (info.month) - str.sprintf("%dM", info.month); - if (info.day) - str.sprintf("%dD", info.day); - - bool has_t = false; - - if (info.hour) { - str.sprintf("T%dH", info.hour); - has_t = true; - } - if (info.minute) { - if (!has_t) { - str.concat('T'); - has_t = true; - } - str.sprintf("%dM", info.minute); - } - if (info.second) { - if (!has_t) { - str.concat('T'); - has_t = true; - } - str.sprintf("%dS", info.second); - } - if (info.us) { - if (!has_t) { - str.concat('T'); - has_t = true; - } - str.sprintf("%du", info.us); - } + if (info.year) + str.sprintf("%dY", info.year); + if (info.month) + str.sprintf("%dM", info.month); + if (info.day) + str.sprintf("%dD", info.day); + + bool has_t = false; + + if (info.hour) { + str.sprintf("T%dH", info.hour); + has_t = true; + } + if (info.minute) { + if (!has_t) { + str.concat('T'); + has_t = true; + } + str.sprintf("%dM", info.minute); + } + if (info.second) { + if (!has_t) { + str.concat('T'); + has_t = true; + } + str.sprintf("%dS", info.second); + } + if (info.us) { + if (!has_t) { + str.concat('T'); + has_t = true; + } + str.sprintf("%du", info.us); + } } else - str.concat("0D"); + str.concat("0D"); return emitScalar(str, QORE_YAML_DURATION_TAG); } @@ -163,13 +168,13 @@ int QoreYamlEmitter::emitValue(const DateTime &d) { else { d.format(str, "YYYY-MM-DD"); if (!info.isTimeNull() || info.secsEast()) { - // use spaces for enhanced readability - if (!info.us) - d.format(str, " HH:mm:SS."); - else if (!(info.us % 1000)) - d.format(str, " HH:mm:SS.ms"); - else - d.format(str, " HH:mm:SS.xx"); + // use spaces for enhanced readability + if (!info.us) + d.format(str, " HH:mm:SS."); + else if (!(info.us % 1000)) + d.format(str, " HH:mm:SS.ms"); + else + d.format(str, " HH:mm:SS.xx"); } } @@ -182,9 +187,9 @@ int QoreYamlEmitter::emitValue(const DateTime &d) { // then add a space if (!emitter.canonical) { if (!info.isTimeNull() || info.secsEast()) { - str.concat(' '); - // add time zone offset (or "Z") - d.format(str, "Z"); + str.concat(' '); + // add time zone offset (or "Z") + d.format(str, "Z"); } } else diff --git a/src/QoreYamlParser.cpp b/src/QoreYamlParser.cpp index 3bcec93..7401e89 100644 --- a/src/QoreYamlParser.cpp +++ b/src/QoreYamlParser.cpp @@ -2,7 +2,7 @@ /* yaml Qore module - Copyright (C) 2010 - 2016 David Nichols, all rights reserved + Copyright (C) 2010 - 2017 Qore Technologies, s.r.o. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -43,18 +43,18 @@ AbstractQoreNode* QoreYamlParser::parse() { if (event.type == YAML_DOCUMENT_START_EVENT) { if (getEvent()) - return 0; + return 0; if (event.type != YAML_DOCUMENT_END_EVENT) { - rv = parseNode(); - if (*xsink) - return 0; + rv = parseNode(); + if (*xsink) + return 0; - if (getCheckEvent(YAML_DOCUMENT_END_EVENT)) - return 0; + if (getCheckEvent(YAML_DOCUMENT_END_EVENT)) + return 0; - if (getEvent()) - return 0; + if (getEvent()) + return 0; } } @@ -67,16 +67,16 @@ AbstractQoreNode* QoreYamlParser::parse() { AbstractQoreNode* QoreYamlParser::parseNode(bool favor_string) { switch (event.type) { case YAML_SCALAR_EVENT: - return parseScalar(favor_string); + return parseScalar(favor_string); case YAML_SEQUENCE_START_EVENT: - return parseSeq(); + return parseSeq(); case YAML_MAPPING_START_EVENT: - return parseMap(); + return parseMap(); default: - xsink->raiseException(QY_PARSE_ERR, "unexpected event '%s' when parsing YAML document", get_event_name(event.type)); + xsink->raiseException(QY_PARSE_ERR, "unexpected event '%s' when parsing YAML document", get_event_name(event.type)); } return 0; @@ -87,14 +87,14 @@ QoreListNode* QoreYamlParser::parseSeq() { while (true) { if (getEvent()) - return 0; + return 0; if (event.type == YAML_SEQUENCE_END_EVENT) - break; + break; AbstractQoreNode* rv = parseNode(); if (*xsink) - return 0; + return 0; l->push(rv); } @@ -106,34 +106,34 @@ QoreHashNode* QoreYamlParser::parseMap() { while (true) { if (getEvent()) - return 0; + return 0; if (event.type == YAML_MAPPING_END_EVENT) - break; + break; // get key node and convert to string ReferenceHolder key(parseNode(true), xsink); if (*xsink) - return 0; + return 0; //printd(5, "key=%p type=%s\n", *key, key->getTypeName()); // convert to string in default encoding QoreStringValueHelper str(*key, QCS_DEFAULT, xsink); if (*xsink) - return 0; + return 0; // get value if (getEvent()) - return 0; + return 0; AbstractQoreNode* value = parseNode(); if (*xsink) - return 0; + return 0; h->setKeyValue(str->getBuffer(), value, xsink); if (*xsink) - return 0; + return 0; } return h.release(); @@ -174,10 +174,10 @@ static double parseFloat(const char* val, size_t len) { bool sign = (*val == '-' || *val == '+'); if ((len == static_cast(5 + sign)) && (!strcasecmp(val + sign, "@nan@") || !strcasecmp(val + sign, "@inf@"))) { if (val[1 + sign] == 'n' || val[1 + sign] == 'N') - return strtod("nan", 0); + return strtod("nan", 0); double d = strtod("inf", 0); if (*val == '-') - d = -d; + d = -d; return d; } return strtod(val, 0); @@ -190,13 +190,13 @@ static QoreNumberNode* parseNumber(const char* val, size_t len) { // check for @inf@ and @nan@ if (!strncasecmp(val + sign, "@nan@", 5) || !strncasecmp(val + sign, "@inf@", 5)) { if (val[5 + sign] == 'n') { - if (len == static_cast(6 + sign)) - return new QoreNumberNode(val); - else { - unsigned prec = is_prec(val + sign + 6, len - sign - 6); - if (prec) - return new QoreNumberNode(val, prec); - } + if (len == static_cast(6 + sign)) + return new QoreNumberNode(val); + else { + unsigned prec = is_prec(val + sign + 6, len - sign - 6); + if (prec) + return new QoreNumberNode(val, prec); + } } return 0; } @@ -206,7 +206,7 @@ static QoreNumberNode* parseNumber(const char* val, size_t len) { unsigned prec = is_prec(p, len - (p - val)); //printd(5, "%s prec %d\n", val, prec); if (prec) - return new QoreNumberNode(val, prec); + return new QoreNumberNode(val, prec); } return new QoreNumberNode(val); @@ -219,21 +219,21 @@ static AbstractQoreNode* try_parse_number(const char* val, size_t len) { // check for @inf@ and @nan@ if (!strncasecmp(val + sign, "@nan@", 5) || !strncasecmp(val + sign, "@inf@", 5)) { if (len == static_cast(5 + sign)) { - if (val[1 + sign] == 'n' || val[1 + sign] == 'N') - return new QoreFloatNode(strtod("nan", 0)); - double d = strtod("inf", 0); - if (*val == '-') - d = -d; - return new QoreFloatNode(d); + if (val[1 + sign] == 'n' || val[1 + sign] == 'N') + return new QoreFloatNode(strtod("nan", 0)); + double d = strtod("inf", 0); + if (*val == '-') + d = -d; + return new QoreFloatNode(d); } if (val[5 + sign] == 'n') { - if (len == static_cast(6 + sign)) - return new QoreNumberNode(val); - else { - unsigned prec = is_prec(val + sign + 6, len - sign - 6); - if (prec) - return new QoreNumberNode(val, prec); - } + if (len == static_cast(6 + sign)) + return new QoreNumberNode(val); + else { + unsigned prec = is_prec(val + sign + 6, len - sign - 6); + if (prec) + return new QoreNumberNode(val, prec); + } } return 0; } @@ -249,38 +249,38 @@ static AbstractQoreNode* try_parse_number(const char* val, size_t len) { bool pm = false; while (*str) { if (isdigit(*str)) { - ++str; - continue; + ++str; + continue; } if (*str == 'n') { - if ((size_t)(str - val + 1) == len) - return new QoreNumberNode(val); - else { - unsigned prec = is_prec(str + 1, len - (str - val) - 1); - //printd(5, "%s prec %d\n", val, prec); - if (prec) - return new QoreNumberNode(val, prec); - } + if ((size_t)(str - val + 1) == len) + return new QoreNumberNode(val); + else { + unsigned prec = is_prec(str + 1, len - (str - val) - 1); + //printd(5, "%s prec %d\n", val, prec); + if (prec) + return new QoreNumberNode(val, prec); + } } if (od) - od = false; + od = false; if (*str == '.') { - if (dp || e || pm) - return 0; - dp = true; + if (dp || e || pm) + return 0; + dp = true; } else if (*str == 'e' || *str == 'E') { - if (e || pm) - return 0; - e = true; + if (e || pm) + return 0; + e = true; } else if (*str == '+' || *str == '-') { - if (pm || !e) - return 0; - pm = true; + if (pm || !e) + return 0; + pm = true; } else - return 0; + return 0; ++str; } @@ -288,16 +288,16 @@ static AbstractQoreNode* try_parse_number(const char* val, size_t len) { if (od) { if ((len < 19 - || (len == 19 && !sign - && ((strcmp(val, "9223372036854775807") <= 0))) - || (len == 20 && sign - && ((*val == '+' && strcmp(val, "+9223372036854775807") <= 0) - || (*val == '-' && strcmp(val, "-9223372036854775808") <= 0))))) { - int64 iv = strtoll(val, 0, 10); - errno = 0; - assert(errno != ERANGE); - //printd(5, "try_parse_number() returning INT\n"); - return new QoreBigIntNode(iv); + || (len == 19 && !sign + && ((strcmp(val, "9223372036854775807") <= 0))) + || (len == 20 && sign + && ((*val == '+' && strcmp(val, "+9223372036854775807") <= 0) + || (*val == '-' && strcmp(val, "-9223372036854775808") <= 0))))) { + int64 iv = strtoll(val, 0, 10); + errno = 0; + assert(errno != ERANGE); + //printd(5, "try_parse_number() returning INT\n"); + return new QoreBigIntNode(iv); } //printd(5, "try_parse_number() returning NUMBER\n"); // if it is an integer requiring > 64bits, use "number" if possible @@ -317,43 +317,47 @@ AbstractQoreNode* QoreYamlParser::parseScalar(bool favor_string) { if (!event.data.scalar.tag) { if (favor_string || (event.data.scalar.quoted_implicit && event.data.scalar.style == YAML_DOUBLE_QUOTED_SCALAR_STYLE)) { - // assume it's a string - return new QoreStringNode(val, len, QCS_UTF8); + // assume it's a string + return new QoreStringNode(val, len, QCS_UTF8); } // check for boolean values if (!strcmp(val, "true")) - return &True; + return &True; if (!strcmp(val, "false")) - return &False; + return &False; // check for null if (!strcmp(val, "null") || !strcmp(val, "~") || !len) - return 0; + return 0; + + // check for sqlnull + if (!strcmp(val, "sqlnull")) + return &Null; // check for absolute date/time values if (isdigit(val[0]) && isdigit(val[1]) && isdigit(val[2]) && isdigit(val[3]) && val[4] == '-') - return parseAbsoluteDate(); + return parseAbsoluteDate(); // check for relative date/time values (durations) if (*val == 'P') { - const char* tv = val + 1; - if (*tv == 'T') { - ++tv; - if (is_number(tv)) { - if (*tv == 'H' || *tv == 'M' || *tv == 'S' || *tv == 'u') - return new DateTimeNode(val); - } - } - else if (is_number(tv)) { - if (*tv == 'Y' || *tv == 'M' || *tv == 'D') - return new DateTimeNode(val); - } + const char* tv = val + 1; + if (*tv == 'T') { + ++tv; + if (is_number(tv)) { + if (*tv == 'H' || *tv == 'M' || *tv == 'S' || *tv == 'u') + return new DateTimeNode(val); + } + } + else if (is_number(tv)) { + if (*tv == 'Y' || *tv == 'M' || *tv == 'D') + return new DateTimeNode(val); + } } AbstractQoreNode* n = try_parse_number(val, len); if (n) - return n; + return n; return new QoreStringNode(val, len, QCS_UTF8); } @@ -497,25 +501,25 @@ DateTimeNode* QoreYamlParser::parseAbsoluteDate() { if (*p == '.') { ++p; if (!isdigit(*p)) - return dt_err(xsink, val, truncated_date); + return dt_err(xsink, val, truncated_date); // read all digits int len = 0; while (isdigit(*p)) { - us *= 10; - us += *p - '0'; - ++len; - ++p; + us *= 10; + us += *p - '0'; + ++len; + ++p; } // adjust to microseconds while (len < 6) { - us *= 10; - ++len; + us *= 10; + ++len; } while (len > 6) { - us /= 10; - --len; + us /= 10; + --len; } } @@ -528,7 +532,7 @@ DateTimeNode* QoreYamlParser::parseAbsoluteDate() { if (*p == ' ') { ++p; if (!*p) - return dt_err(xsink, val, truncated_date); + return dt_err(xsink, val, truncated_date); } if (*p == 'Z') { ++p; @@ -538,51 +542,51 @@ DateTimeNode* QoreYamlParser::parseAbsoluteDate() { ++p; if (!isdigit(*p)) - return dt_err(xsink, val, truncated_date); + return dt_err(xsink, val, truncated_date); int utc_h = *p - '0'; ++p; if (isdigit(*p)) { - utc_h = utc_h * 10 + (*p - '0'); - ++p; + utc_h = utc_h * 10 + (*p - '0'); + ++p; } int offset = utc_h * 3600; if (*p) { - if (*p != ':') - return dt_err(xsink, val, "invalid time zone hours/minutes separator character"); - - ++p; - if (!isdigit(*p)) - return dt_err(xsink, val, truncated_date); - - int utc_m = *p - '0'; - ++p; - if (isdigit(*p)) { - utc_m = utc_m * 10 + (*p - '0'); - ++p; - } - - offset += utc_m * 60; - - if (*p) { - if (*p != ':') - return dt_err(xsink, val, "invalid time zone hours/minutes separator character"); - - ++p; - if (!isdigit(*p)) - return dt_err(xsink, val, truncated_date); - - int utc_s = *p - '0'; - ++p; - if (isdigit(*p)) { - utc_s = utc_s * 10 + (*p - '0'); - ++p; - } - - offset += utc_s; - } + if (*p != ':') + return dt_err(xsink, val, "invalid time zone hours/minutes separator character"); + + ++p; + if (!isdigit(*p)) + return dt_err(xsink, val, truncated_date); + + int utc_m = *p - '0'; + ++p; + if (isdigit(*p)) { + utc_m = utc_m * 10 + (*p - '0'); + ++p; + } + + offset += utc_m * 60; + + if (*p) { + if (*p != ':') + return dt_err(xsink, val, "invalid time zone hours/minutes separator character"); + + ++p; + if (!isdigit(*p)) + return dt_err(xsink, val, truncated_date); + + int utc_s = *p - '0'; + ++p; + if (isdigit(*p)) { + utc_s = utc_s * 10 + (*p - '0'); + ++p; + } + + offset += utc_s; + } } zone = findCreateOffsetZone(offset * mult); diff --git a/src/ql_yaml.qpp b/src/ql_yaml.qpp index 8f3af23..997a652 100644 --- a/src/ql_yaml.qpp +++ b/src/ql_yaml.qpp @@ -4,7 +4,7 @@ yaml Qore module - Copyright (C) 2010 - 2012 David Nichols + Copyright (C) 2010 - 2017 Qore Technologies, s.r.o. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -74,6 +74,9 @@ const ExplicitEndDoc = QYE_EXPLICIT_END_DOC; //! emitter constant: emit seq and map with block style const BlockStyle = QYE_BLOCK_STYLE; +//! emitter constant: emit SQL null \c "!!sqlnull" +const EmitSqlNull = QYE_EMIT_SQLNULL; + //const Yaml1_0 = QYE_VER_1_0; //! emitter constant: emit YAML 1.1 (not necessary to use as this is the default and currently the only YAML version supported by libyaml) diff --git a/src/yaml-module.cpp b/src/yaml-module.cpp index 54e32e0..4a0b10c 100644 --- a/src/yaml-module.cpp +++ b/src/yaml-module.cpp @@ -2,7 +2,7 @@ /* yaml Qore module - Copyright (C) 2010 - 2016 David Nichols, all rights reserved + Copyright (C) 2010 - 2017 Qore Technologies, s.r.o. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -44,9 +44,11 @@ DLLEXPORT qore_license_t qore_module_license = QL_LGPL; DLLEXPORT char qore_module_license_str[] = "MIT"; QoreString NullStr("null"); +QoreString SqlNullStr("sqlnull"); const char* QORE_YAML_DURATION_TAG = "!duration"; const char* QORE_YAML_NUMBER_TAG = "!number"; +const char* QORE_YAML_SQLNULL_TAG = "!sqlnull"; yaml_version_directive_t yaml_ver_1_0 = {1, 0}, yaml_ver_1_1 = {1, 1}, yaml_ver_1_2 = {1, 2}; diff --git a/src/yaml-module.h b/src/yaml-module.h index 911462a..867826a 100644 --- a/src/yaml-module.h +++ b/src/yaml-module.h @@ -4,7 +4,7 @@ Qore Programming Language - Copyright 2003 - 2016 David Nichols + Copyright 2003 - 2017 Qore Technologies, s.r.o. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -45,6 +45,7 @@ #define QYE_VER_1_0 (1 << 5) #define QYE_VER_1_1 (1 << 6) #define QYE_VER_1_2 (1 << 7) +#define QYE_EMIT_SQLNULL (1 << 8) #define QYE_DEFAULT (QYE_NONE) @@ -58,12 +59,14 @@ DLLLOCAL extern const char* QORE_YAML_DURATION_TAG; DLLLOCAL extern const char* QORE_YAML_NUMBER_TAG; +DLLLOCAL extern const char* QORE_YAML_SQLNULL_TAG; DLLLOCAL extern const char *QY_EMIT_ERR; DLLLOCAL extern const char *QY_PARSE_ERR; DLLLOCAL extern QoreString NullStr; +DLLLOCAL extern QoreString SqlNullStr; DLLLOCAL extern yaml_version_directive_t yaml_ver_1_0, yaml_ver_1_1, yaml_ver_1_2; @@ -100,7 +103,8 @@ class QoreYamlEmitter : public QoreYamlBase { bool block, implicit_start_doc, - implicit_end_doc; + implicit_end_doc, + emit_sqlnull; yaml_version_directive_t *yaml_ver; @@ -136,14 +140,14 @@ class QoreYamlEmitter : public QoreYamlBase { DLLLOCAL int streamStart() { if (!yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING)) - return err("error initializing yaml stream start event"); + return err("error initializing yaml stream start event"); return emit("stream start"); } DLLLOCAL int streamEnd() { if (!yaml_stream_end_event_initialize(&event)) - return err("error initializing yaml stream end event"); + return err("error initializing yaml stream end event"); return emit("stream end"); } @@ -153,15 +157,15 @@ class QoreYamlEmitter : public QoreYamlBase { DLLLOCAL ~QoreYamlEmitter() { if (valid) { - docEnd(); - streamEnd(); + docEnd(); + streamEnd(); } yaml_emitter_delete(&emitter); } DLLLOCAL int docStart(yaml_tag_directive_t *start = 0, unsigned elements = 0) { if (!yaml_document_start_event_initialize(&event, yaml_ver, start, start + elements, implicit_start_doc)) - return err("unknown error initializing yaml document start event"); + return err("unknown error initializing yaml document start event"); return emit("doc start"); } @@ -169,16 +173,16 @@ class QoreYamlEmitter : public QoreYamlBase { DLLLOCAL int docEnd() { //printd(5, "QoreYamlEmitter::docEnd() ied=%d\n", implicit_end_doc); if (!yaml_document_end_event_initialize(&event, implicit_end_doc)) - return err("unknown error initializing yaml document end event"); + return err("unknown error initializing yaml document end event"); return emit("doc end"); } DLLLOCAL int seqStart(yaml_sequence_style_t style = YAML_ANY_SEQUENCE_STYLE, - const char *tag = YAML_SEQ_TAG, const char *anchor = 0, bool implicit = true) { + const char *tag = YAML_SEQ_TAG, const char *anchor = 0, bool implicit = true) { if (!yaml_sequence_start_event_initialize(&event, (yaml_char_t *)anchor, (yaml_char_t *)tag, - implicit, style)) - return err("unknown error initializing yaml sequence start event"); + implicit, style)) + return err("unknown error initializing yaml sequence start event"); //printd(5, "QoreYamlEmitter::seqStart(tag=%s, anchor=%s)\n", tag, anchor ? anchor : "(null)"); return emit("seq start"); @@ -186,49 +190,49 @@ class QoreYamlEmitter : public QoreYamlBase { DLLLOCAL int seqEnd() { if (!yaml_sequence_end_event_initialize(&event)) - return err("unknown error initializing yaml sequence end event"); + return err("unknown error initializing yaml sequence end event"); return emit("seq end"); } DLLLOCAL int mapStart(yaml_mapping_style_t style = YAML_ANY_MAPPING_STYLE, - const char *tag = YAML_MAP_TAG, const char *anchor = 0, bool implicit = true) { + const char *tag = YAML_MAP_TAG, const char *anchor = 0, bool implicit = true) { if (!yaml_mapping_start_event_initialize(&event, (yaml_char_t *)anchor, (yaml_char_t *)tag, - implicit, style)) - return err("unknown error initializing yaml mapping start event"); + implicit, style)) + return err("unknown error initializing yaml mapping start event"); return emit("map start"); } DLLLOCAL int mapEnd() { if (!yaml_mapping_end_event_initialize(&event)) - return err("unknown error initializing yaml mapping end event"); + return err("unknown error initializing yaml mapping end event"); return emit("map end"); } DLLLOCAL int emitScalar(const QoreString &value, const char *tag, const char *anchor = 0, - bool plain_implicit = true, bool quoted_implicit = true, - yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE) { + bool plain_implicit = true, bool quoted_implicit = true, + yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE) { TempEncodingHelper str(&value, QCS_UTF8, xsink); if (*xsink) - return -1; + return -1; if (!yaml_scalar_event_initialize(&event, (yaml_char_t *)anchor, (yaml_char_t *)tag, - (yaml_char_t *)str->getBuffer(), str->strlen(), - plain_implicit, quoted_implicit, style)) - return err("unknown error initializing yaml scalar output event for yaml type '%s', value '%s'", tag, value.getBuffer()); + (yaml_char_t *)str->getBuffer(), str->strlen(), + plain_implicit, quoted_implicit, style)) + return err("unknown error initializing yaml scalar output event for yaml type '%s', value '%s'", tag, value.getBuffer()); return emit("scalar", tag); } DLLLOCAL int emitScalar(const char *value, const char *tag, const char *anchor = 0, - bool plain_implicit = true, bool quoted_implicit = true, - yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE) { + bool plain_implicit = true, bool quoted_implicit = true, + yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE) { if (!yaml_scalar_event_initialize(&event, (yaml_char_t *)anchor, (yaml_char_t *)tag, - (yaml_char_t *)value, -1, - plain_implicit, quoted_implicit, style)) { - return err("unknown error initializing yaml scalar output event for yaml type '%s', value '%s'", tag, value); + (yaml_char_t *)value, -1, + plain_implicit, quoted_implicit, style)) { + return err("unknown error initializing yaml scalar output event for yaml type '%s', value '%s'", tag, value); } return emit("scalar", tag); @@ -237,7 +241,7 @@ class QoreYamlEmitter : public QoreYamlBase { DLLLOCAL int emitValue(const QoreString &str) { TempEncodingHelper tmp(&str, QCS_UTF8, xsink); if (!tmp) - return -1; + return -1; return emitScalar(**tmp, YAML_STR_TAG, 0, true, true, YAML_DOUBLE_QUOTED_SCALAR_STYLE); } @@ -290,24 +294,24 @@ class QoreYamlEmitter : public QoreYamlBase { DLLLOCAL int emitValue(const QoreListNode &l) { if (seqStart(block ? YAML_BLOCK_SEQUENCE_STYLE : YAML_FLOW_SEQUENCE_STYLE)) - return -1; + return -1; ConstListIterator li(l); while (li.next()) { - if (emit(li.getValue())) - return -1; + if (emit(li.getValue())) + return -1; } return seqEnd(); } DLLLOCAL int emitValue(const QoreHashNode &h) { if (mapStart(block ? YAML_BLOCK_MAPPING_STYLE : YAML_FLOW_MAPPING_STYLE)) - return -1; + return -1; ConstHashIterator hi(h); while (hi.next()) { - if (emitScalar(hi.getKey(), YAML_STR_TAG)) - return -1; - if (emit(hi.getValue())) - return -1; + if (emitScalar(hi.getKey(), YAML_STR_TAG)) + return -1; + if (emit(hi.getValue())) + return -1; } return mapEnd(); } @@ -326,6 +330,10 @@ class QoreYamlEmitter : public QoreYamlBase { return emitScalar(NullStr, YAML_NULL_TAG); } + DLLLOCAL int emitSqlNull() { + return emitScalar(SqlNullStr, QORE_YAML_SQLNULL_TAG); + } + DLLLOCAL void setCanonical(bool b = true) { yaml_emitter_set_canonical(&emitter, b); } @@ -348,7 +356,7 @@ class QoreYamlStringWriteHandler : public QoreYamlWriteHandler { } DLLLOCAL ~QoreYamlStringWriteHandler() { if (str) - str->deref(); + str->deref(); } DLLLOCAL QoreStringNode *take() { QoreStringNode *rv = str; @@ -369,8 +377,8 @@ class QoreYamlParser : public QoreYamlBase { DLLLOCAL void discardEvent() { if (discard) { - yaml_event_delete(&event); - discard = false; + yaml_event_delete(&event); + discard = false; } } @@ -378,9 +386,9 @@ class QoreYamlParser : public QoreYamlBase { discardEvent(); if (!yaml_parser_parse(&parser, &event)) { - valid = false; + valid = false; xsink->raiseException(QY_PARSE_ERR, "getEvent: unexpected event '%s' when parsing YAML document", get_event_name(event.type)); - return -1; + return -1; } //printd(5, "QoreYamlParser::getEvent() got %s event (%d)\n", get_event_name(event.type), event.type); @@ -390,15 +398,15 @@ class QoreYamlParser : public QoreYamlBase { DLLLOCAL int checkEvent(yaml_event_type_t type) { if (event.type != type) { - xsink->raiseException(QY_PARSE_ERR, "expecting '%s' event; got '%s' event instead", get_event_name(type), get_event_name(event.type)); - return -1; + xsink->raiseException(QY_PARSE_ERR, "expecting '%s' event; got '%s' event instead", get_event_name(type), get_event_name(event.type)); + return -1; } return 0; } DLLLOCAL int getCheckEvent(yaml_event_type_t type) { if (getEvent()) - return -1; + return -1; return checkEvent(type); } diff --git a/test/yaml.qtest b/test/yaml.qtest index 12667a4..7e6f527 100755 --- a/test/yaml.qtest +++ b/test/yaml.qtest @@ -64,6 +64,7 @@ public class Main inherits QUnit::Test { addTestCase("Len test", \testLen()); addTestCase("Date/Time test", \testDates()); addTestCase("Structure with NaN test", \testNanStructure()); + addTestCase("sql null test", \sqlNull()); # Return for compatibility with test harness that checks return value. set_return_value(main()); @@ -120,4 +121,22 @@ public class Main inherits QUnit::Test { assertFalse(DATA2[3] == l[3]); assertEq(DATA2[4], l[4]); } + + sqlNull() { + string yaml = make_yaml(NULL, EmitSqlNull); + assertRegex("^sqlnull", yaml); + assertEq(NULL, parse_yaml(yaml)); + yaml = make_yaml("sqlnull"); + assertRegex("sqlnull", yaml); + assertEq("sqlnull", parse_yaml(yaml)); + yaml = make_yaml("sqlnull", EmitSqlNull); + assertRegex("sqlnull", yaml); + assertEq("sqlnull", parse_yaml(yaml)); + yaml = make_yaml(NULL); + assertRegex("^null", yaml); + assertEq(NOTHING, parse_yaml(yaml)); + list d = DATA + "NULL"; + yaml = make_yaml(d, EmitSqlNull); + assertEq(d, parse_yaml(yaml)); + } } From f337064dd2ac1048c783d8abc4e5287b7d8aef18 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Thu, 26 Oct 2017 10:32:30 +0200 Subject: [PATCH 3/3] refs qorelanguage/qore#2343 fixed last commit - botched merge --- test/yaml.qtest | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/yaml.qtest b/test/yaml.qtest index d7f5f8f..ad07b64 100755 --- a/test/yaml.qtest +++ b/test/yaml.qtest @@ -66,11 +66,8 @@ public class Main inherits QUnit::Test { addTestCase("Len test", \testLen()); addTestCase("Date/Time test", \testDates()); addTestCase("Structure with NaN test", \testNanStructure()); -<<<<<<< HEAD addTestCase("single quoted strings", \testSingleQuotedStrings()); -======= addTestCase("sql null test", \sqlNull()); ->>>>>>> origin/develop # Return for compatibility with test harness that checks return value. set_return_value(main()); @@ -126,13 +123,13 @@ public class Main inherits QUnit::Test { assertEq(DATA2[4], l[4]); } -<<<<<<< HEAD testSingleQuotedStrings() { # issue 2343 assertEq("1234", parse_yaml("'1234'")); assertEq(500n, parse_yaml("'5e+02n{128}'")); assertEq(2017-10-26T09:37:36.119613+02:00, parse_yaml("'2017-10-26 09:37:36.119613 +02:00'")); -======= + } + sqlNull() { string yaml = make_yaml(NULL, EmitSqlNull); assertRegex("^sqlnull", yaml); @@ -149,6 +146,5 @@ public class Main inherits QUnit::Test { list d = DATA + "NULL"; yaml = make_yaml(d, EmitSqlNull); assertEq(d, parse_yaml(yaml)); ->>>>>>> origin/develop } }