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 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 2ca4441..76ed5af 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 06e7f9a..821eba1 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"); @@ -95,6 +96,10 @@ int QoreYamlEmitter::emit(const QoreValue& v) { return emitValue(*v.get()); case NT_NULL: + if (emit_sqlnull) + return emitSqlNull(); + // fall down to nothing + case NT_NOTHING: return emitNull(); diff --git a/src/QoreYamlParser.cpp b/src/QoreYamlParser.cpp index 7293f68..dfd140f 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 @@ -344,13 +344,17 @@ AbstractQoreNode* QoreYamlParser::parseScalar(bool favor_string) { // check for null if (!strcmp(val, "null") || !strcmp(val, "~") || !len) - return 0; + return nullptr; + + // check for sqlnull + if (!strcmp(val, "sqlnull")) + return &Null; // check for absolute date/time values if (len > 9 && isdigit(val[0]) && isdigit(val[1]) && isdigit(val[2]) && isdigit(val[3]) && val[4] == '-' && isdigit(val[5]) && isdigit(val[6]) && val[7] == '-' && isdigit(val[8]) && isdigit(val[9])) - return parseAbsoluteDate(); + return parseAbsoluteDate(); // check for relative date/time values (durations) if (*val == 'P') { @@ -376,6 +380,7 @@ AbstractQoreNode* QoreYamlParser::parseScalar(bool favor_string) { } const char* tag = (const char*)event.data.scalar.tag; + // FIXME: use a map here for O(ln(n)) performance if (!strcmp(tag, YAML_TIMESTAMP_TAG)) return parseAbsoluteDate(); if (!strcmp(tag, YAML_BINARY_TAG)) @@ -383,7 +388,7 @@ AbstractQoreNode* QoreYamlParser::parseScalar(bool favor_string) { if (!strcmp(tag, YAML_STR_TAG)) return new QoreStringNode(val, len, QCS_UTF8); if (!strcmp(tag, YAML_NULL_TAG)) - return 0; + return nullptr; if (!strcmp(tag, YAML_BOOL_TAG)) return parseBool(); if (!strcmp(tag, YAML_INT_TAG)) @@ -394,10 +399,12 @@ AbstractQoreNode* QoreYamlParser::parseScalar(bool favor_string) { return new DateTimeNode(val); if (!strcmp(tag, QORE_YAML_NUMBER_TAG)) return parseNumber(val, len); + if (!strcmp(tag, QORE_YAML_SQLNULL_TAG)) + return &Null; xsink->raiseException(QY_PARSE_ERR, "don't know how to parse scalar tag '%s'", tag); - return 0; + return nullptr; } QoreBoolNode* QoreYamlParser::parseBool() { 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 1e986ab..1ceb503 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; @@ -214,8 +218,8 @@ class QoreYamlEmitter : public QoreYamlBase { if (*xsink) return -1; - if (!yaml_scalar_event_initialize(&event, (yaml_char_t *)anchor, (yaml_char_t *)tag, - (yaml_char_t *)str->c_str(), str->strlen(), + if (!yaml_scalar_event_initialize(&event, (yaml_char_t*)anchor, (yaml_char_t *)tag, + (yaml_char_t*)str->c_str(), 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()); @@ -327,6 +331,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); } diff --git a/test/yaml.qtest b/test/yaml.qtest index d238ead..ad07b64 100755 --- a/test/yaml.qtest +++ b/test/yaml.qtest @@ -67,6 +67,7 @@ public class Main inherits QUnit::Test { addTestCase("Date/Time test", \testDates()); addTestCase("Structure with NaN test", \testNanStructure()); addTestCase("single quoted strings", \testSingleQuotedStrings()); + addTestCase("sql null test", \sqlNull()); # Return for compatibility with test harness that checks return value. set_return_value(main()); @@ -128,4 +129,22 @@ public class Main inherits QUnit::Test { 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); + 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)); + } }