From 3a04fb0cfd803e4fe4bb5310c706e5cbca277361 Mon Sep 17 00:00:00 2001 From: Chen Yangjian <252317+dotnil@users.noreply.github.com> Date: Thu, 1 Nov 2018 15:06:28 +0800 Subject: [PATCH] New: db.all({ sql, nestTables: true }), fixes #443 This commit changes the Statement callback footprint from `function(err, result)` to `function(err, result, fields)`. The default result structure is changed from `{ field1: value1, field2: value2 }` to `[ value1, value2 ]` to be compliant with the fields array. The newly added fields is in such format: ```js [ { name: 'field1', table: 'table1' }, { name: 'field2', table: 'table1' } ] ``` In this way, when a JOIN query like `SELECT foo.*, bar.* ...` happens, the values in result won't be collided with each other. And the result can be properly processed with the help of fields. To be backward compliant, `Database.prototype.[get|all]` are overriden. The default `.all(sql, callback)` still works the same, with the result containing objects. The added method type is `.all({ sql, nestTables })` in which the `nestTables` is default to false. If `nestTables` is true, the returned `rows` is an array of nested objects in following structure: ```js [ { table1: { field1: value1, field2: value2 }, table2: { ... } }, { table1: { ... }, table2: { ... } } ] ``` --- .travis.yml | 17 ++- deps/extract.py | 14 +- deps/sqlite3.gyp | 6 +- deps/sqlite3.patch | 193 +++++++++++++++++++++++++++ lib/sqlite3.js | 67 ++++++++++ scripts/build_against_node.sh | 55 -------- scripts/build_against_node_webkit.sh | 91 ------------- scripts/build_for_node_webkit.cmd | 15 --- scripts/install_node.sh | 21 --- scripts/validate_tag.sh | 2 +- src/statement.cc | 41 ++++-- src/statement.h | 4 +- test/.eslintrc | 5 + test/named_columns.test.js | 4 +- 14 files changed, 328 insertions(+), 207 deletions(-) create mode 100644 deps/sqlite3.patch delete mode 100755 scripts/build_against_node_webkit.sh delete mode 100644 scripts/build_for_node_webkit.cmd delete mode 100755 scripts/install_node.sh create mode 100644 test/.eslintrc diff --git a/.travis.yml b/.travis.yml index 1522cea74..989c9868d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,18 @@ sudo: false -language: generic +language: node_js +node_js: + - "node" + - "lts/*" + - "7" + - "6" + - "5" + - "4" -dist: precise +dist: trusty +os: + - linux + - osx git: depth: 10 @@ -166,14 +176,11 @@ env: before_install: - export PUBLISHABLE=${PUBLISHABLE:-true} - if [[ $(uname -s) == 'Linux' ]]; then - export CXX="clang++-3.5"; - export CC="clang-3.5"; export PYTHONPATH=$(pwd)/py-local/lib/python2.7/site-packages; else export PYTHONPATH=$(pwd)/py-local/lib/python/site-packages; fi; - scripts/validate_tag.sh -- source ./scripts/install_node.sh ${NODE_VERSION} install: # put node-pre-gyp on path diff --git a/deps/extract.py b/deps/extract.py index 410c931e5..d6ffc79b6 100644 --- a/deps/extract.py +++ b/deps/extract.py @@ -1,9 +1,13 @@ import sys import tarfile -import os +from os import path +import subprocess -tarball = os.path.abspath(sys.argv[1]) -dirname = os.path.abspath(sys.argv[2]) -tfile = tarfile.open(tarball,'r:gz'); +tarball = path.abspath(sys.argv[1]) +dirname = path.abspath(sys.argv[2]) +tfile = tarfile.open(tarball,'r:gz') tfile.extractall(dirname) -sys.exit(0) + +patchfile = path.join(path.dirname(tarball), "sqlite3.patch") +cwd = path.join(dirname, path.basename(tarball).replace(".tar.gz", "")) +subprocess.Popen(["patch", "-p1", "--input", patchfile], cwd=cwd) diff --git a/deps/sqlite3.gyp b/deps/sqlite3.gyp index 94db314b5..e09f65bb2 100755 --- a/deps/sqlite3.gyp +++ b/deps/sqlite3.gyp @@ -55,7 +55,9 @@ 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/sqlite3.c' ], - 'action': ['python','./extract.py','./sqlite-autoconf-<@(sqlite_version).tar.gz','<(SHARED_INTERMEDIATE_DIR)'] + 'action': [ + 'python', './extract.py', './sqlite-autoconf-<@(sqlite_version).tar.gz', '<(SHARED_INTERMEDIATE_DIR)' + ] } ], 'direct_dependent_settings': { @@ -78,6 +80,7 @@ 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)/sqlite-autoconf-<@(sqlite_version)/' ], 'defines': [ 'SQLITE_THREADSAFE=1', + 'SQLITE_ENABLE_COLUMN_METADATA', 'SQLITE_ENABLE_FTS3', 'SQLITE_ENABLE_FTS4', 'SQLITE_ENABLE_FTS5', @@ -91,6 +94,7 @@ 'defines': [ '_REENTRANT=1', 'SQLITE_THREADSAFE=1', + 'SQLITE_ENABLE_COLUMN_METADATA', 'SQLITE_ENABLE_FTS3', 'SQLITE_ENABLE_FTS4', 'SQLITE_ENABLE_FTS5', diff --git a/deps/sqlite3.patch b/deps/sqlite3.patch new file mode 100644 index 000000000..8ce668496 --- /dev/null +++ b/deps/sqlite3.patch @@ -0,0 +1,193 @@ +diff -Naur -Z sqlite-autoconf-3240000/sqlite3.c sqlite-autoconf-3240001/sqlite3.c +--- sqlite-autoconf-3240000/sqlite3.c 2018-06-05 03:51:29.000000000 +0800 ++++ sqlite-autoconf-3240001/sqlite3.c 2018-11-01 14:43:21.832396536 +0800 +@@ -5179,6 +5179,8 @@ + SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); + SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); + SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); ++SQLITE_API const char *sqlite3_column_table_alias_name(sqlite3_stmt*,int); ++SQLITE_API const void *sqlite3_column_table_alias_name16(sqlite3_stmt*,int); + SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); + SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); + +@@ -14443,7 +14445,7 @@ + #define P5_ConstraintFK 4 + + /* +-** The Vdbe.aColName array contains 5n Mem structures, where n is the ++** The Vdbe.aColName array contains 6n Mem structures, where n is the + ** number of columns of data returned by the statement. + */ + #define COLNAME_NAME 0 +@@ -14451,8 +14453,9 @@ + #define COLNAME_DATABASE 2 + #define COLNAME_TABLE 3 + #define COLNAME_COLUMN 4 ++#define COLNAME_TABALIAS 5 + #ifdef SQLITE_ENABLE_COLUMN_METADATA +-# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ ++# define COLNAME_N 6 /* Number of COLNAME_xxx symbols */ + #else + # ifdef SQLITE_OMIT_DECLTYPE + # define COLNAME_N 1 /* Store only the name */ +@@ -80777,7 +80780,7 @@ + ** Convert the N-th element of pStmt->pColName[] into a string using + ** xFunc() then return that string. If N is out of range, return 0. + ** +-** There are up to 5 names for each column. useType determines which ++** There are up to 6 names for each column. useType determines which + ** name is returned. Here are the names: + ** + ** 0 The column name as it should be displayed for output +@@ -80785,9 +80788,10 @@ + ** 2 The name of the database that the column derives from + ** 3 The name of the table that the column derives from + ** 4 The name of the table column that the result column derives from ++** 5 The name of the table for the column as it's refered in the sentence (maybe an alias) + ** + ** If the result is not a simple column reference (if it is an expression +-** or a constant) then useTypes 2, 3, and 4 return NULL. ++** or a constant) then useTypes 2, 3, 4, and 5 return NULL. + */ + static const void *columnName( + sqlite3_stmt *pStmt, +@@ -80902,6 +80906,22 @@ + #endif /* SQLITE_OMIT_UTF16 */ + + /* ++** Return the name of the table for the column (maybe an alias). ++** NULL is returned if the result column is an expression or constant or ++** anything else which is not an unambiguous reference to a database column. ++*/ ++SQLITE_API const char *sqlite3_column_table_alias_name(sqlite3_stmt *pStmt, int N){ ++ return columnName( ++ pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABALIAS); ++} ++#ifndef SQLITE_OMIT_UTF16 ++SQLITE_API const void *sqlite3_column_table_alias_name16(sqlite3_stmt *pStmt, int N){ ++ return columnName( ++ pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABALIAS); ++} ++#endif /* SQLITE_OMIT_UTF16 */ ++ ++/* + ** Return the name of the table column from which a result column derives. + ** NULL is returned if the result column is an expression or constant or + ** anything else which is not an unambiguous reference to a database column. +@@ -122921,13 +122941,13 @@ + ** + ** The declaration type for any expression other than a column is NULL. + ** +-** This routine has either 3 or 6 parameters depending on whether or not ++** This routine has either 2 or 6 parameters depending on whether or not + ** the SQLITE_ENABLE_COLUMN_METADATA compile-time option is used. + */ + #ifdef SQLITE_ENABLE_COLUMN_METADATA +-# define columnType(A,B,C,D,E) columnTypeImpl(A,B,C,D,E) ++# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F) + #else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */ +-# define columnType(A,B,C,D,E) columnTypeImpl(A,B) ++# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B) + #endif + static const char *columnTypeImpl( + NameContext *pNC, +@@ -122937,7 +122957,8 @@ + Expr *pExpr, + const char **pzOrigDb, + const char **pzOrigTab, +- const char **pzOrigCol ++ const char **pzOrigCol, ++ const char **pzTableAlias + #endif + ){ + char const *zType = 0; +@@ -122946,6 +122967,7 @@ + char const *zOrigDb = 0; + char const *zOrigTab = 0; + char const *zOrigCol = 0; ++ char const *zTableAlias = 0; + #endif + + assert( pExpr!=0 ); +@@ -122967,6 +122989,9 @@ + if( jnSrc ){ + pTab = pTabList->a[j].pTab; + pS = pTabList->a[j].pSelect; ++#ifdef SQLITE_ENABLE_COLUMN_METADATA ++ zTableAlias = pTabList->a[j].zAlias ? pTabList->a[j].zAlias : pTabList->a[j].zName; ++#endif + }else{ + pNC = pNC->pNext; + } +@@ -123009,7 +123034,7 @@ + sNC.pSrcList = pS->pSrc; + sNC.pNext = pNC; + sNC.pParse = pNC->pParse; +- zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol); ++ zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol,&zTableAlias); + } + }else{ + /* A real table or a CTE table */ +@@ -123053,7 +123078,7 @@ + sNC.pSrcList = pS->pSrc; + sNC.pNext = pNC; + sNC.pParse = pNC->pParse; +- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol); ++ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol,&zTableAlias); + break; + } + #endif +@@ -123065,6 +123090,11 @@ + *pzOrigDb = zOrigDb; + *pzOrigTab = zOrigTab; + *pzOrigCol = zOrigCol; ++ /* If pzTableAlias is already set from a higher level, do not set again, ++ ** because it could be the same original table with another alias, and ++ ** only the higher level is the correct result. ++ */ ++ if( *pzTableAlias==0 ) *pzTableAlias = zTableAlias; + } + #endif + return zType; +@@ -123093,7 +123123,8 @@ + const char *zOrigDb = 0; + const char *zOrigTab = 0; + const char *zOrigCol = 0; +- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol); ++ const char *zTableAlias = 0; ++ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &zTableAlias); + + /* The vdbe must make its own copy of the column-type and other + ** column specific strings, in case the schema is reset before this +@@ -123102,8 +123133,9 @@ + sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, SQLITE_TRANSIENT); + sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT); + sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT); ++ sqlite3VdbeSetColName(v, i, COLNAME_TABALIAS, zTableAlias, SQLITE_TRANSIENT); + #else +- zType = columnType(&sNC, p, 0, 0, 0); ++ zType = columnType(&sNC, p, 0, 0, 0, 0); + #endif + sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT); + } +@@ -123362,7 +123394,7 @@ + const char *zType; + int n, m; + p = a[i].pExpr; +- zType = columnType(&sNC, p, 0, 0, 0); ++ zType = columnType(&sNC, p, 0, 0, 0, 0); + /* pCol->szEst = ... // Column size est for SELECT tables never used */ + pCol->affinity = sqlite3ExprAffinity(p); + if( zType ){ +diff -Naur -Z sqlite-autoconf-3240000/sqlite3.h sqlite-autoconf-3240001/sqlite3.h +--- sqlite-autoconf-3240000/sqlite3.h 2018-11-01 14:44:01.176218438 +0800 ++++ sqlite-autoconf-3240001/sqlite3.h 2018-11-01 14:44:08.865910623 +0800 +@@ -4152,6 +4152,8 @@ + SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); + SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); + SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); ++SQLITE_API const char *sqlite3_column_table_alias_name(sqlite3_stmt*,int); ++SQLITE_API const void *sqlite3_column_table_alias_name16(sqlite3_stmt*,int); + SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); + SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); + diff --git a/lib/sqlite3.js b/lib/sqlite3.js index e3a570c50..475fc3b2e 100644 --- a/lib/sqlite3.js +++ b/lib/sqlite3.js @@ -153,6 +153,73 @@ Database.prototype.removeAllListeners = function(type) { return val; }; +['all', 'each', 'get'].forEach(function (name) { + var allCallback = function(nestTables, callback) { + return function replacement(err, rows, fields) { + if (err) return callback(err); + if (!fields) return callback(null, rows); + var result = []; + var i, j, row; + if (nestTables) { + for (i = 0; i < rows.length; i++) { + row = {}; + for (j = 0; j < fields.length; j++) { + var table = row[fields[j].table]; + if (!table) table = row[fields[j].table] = {}; + table[fields[j].name] = rows[i][j]; + } + result.push(row); + } + callback(null, result); + } else { + for (i = 0; i < rows.length; i++) { + row = {}; + for (j = 0; j < fields.length; j++) { + row[fields[j].name] = rows[i][j]; + } + result.push(row); + } + callback(null, result); + } + }; + }; + var getCallback = function(nestTables, callback) { + var cb = allCallback(nestTables, function(err, rows, fields) { + callback(err, rows ? rows[0] : null, fields); + }); + return function(err, row, fields) { + cb(err, [row], fields); + }; + }; + var wrapCallback = function(name, nestTables, callback) { + return name == 'all' ? allCallback(nestTables, callback) : getCallback(nestTables, callback); + }; + + var databaseMethodOrig = Database.prototype[name]; + Database.prototype[name] = function () { + var args = Array.prototype.slice.call(arguments); + var sql = args.shift(); + var nestTables = false; + if (sql == null) throw new Error('Unexpected sql null'); + if (typeof sql == 'object') { + if (sql.nestTables) nestTables = sql.nestTables; + sql = sql.sql; + } + var callback = args.pop(); + args.unshift(sql); + args.push(wrapCallback(name, nestTables, callback)); + return databaseMethodOrig.apply(this, args); + }; + + var statementMethodOrig = Statement.prototype[name]; + Statement.prototype[name] = function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + args.push(callback.length < 3 ? wrapCallback(name, false, callback) : callback); + return statementMethodOrig.apply(this, args); + }; +}); + // Save the stack trace over EIO callbacks. sqlite3.verbose = function() { if (!isVerbose) { diff --git a/scripts/build_against_node.sh b/scripts/build_against_node.sh index d75b1c738..52350ca24 100755 --- a/scripts/build_against_node.sh +++ b/scripts/build_against_node.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -source ~/.nvm/nvm.sh - set -e -u function publish() { @@ -20,56 +18,3 @@ nm lib/binding/*/node_sqlite3.node | grep "GLIBC_" | c++filt || true npm test publish - -# now test building against shared sqlite -echo "building from source to test against external libsqlite3" -export NODE_SQLITE3_JSON1=no -if [[ $(uname -s) == 'Darwin' ]]; then - brew install sqlite - npm install --build-from-source --sqlite=$(brew --prefix) --clang=1 -else - npm install --build-from-source --sqlite=/usr --clang=1 -fi -npm test -export NODE_SQLITE3_JSON1=yes - -platform=$(uname -s | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/") - -: ' -if [[ $(uname -s) == 'Linux' ]]; then - # node v0.8 and above provide pre-built 32 bit and 64 bit binaries - # so here we use the 32 bit ones to also test 32 bit builds - NVER=`node -v` - # enable 32 bit node - export PATH=$(pwd)/node-${NVER}-${platform}-x86/bin:$PATH - if [[ ${NODE_VERSION:0:4} == 'iojs' ]]; then - wget https://iojs.org/download/release/${NVER}/iojs-${NVER}-${platform}-x86.tar.gz - tar xf iojs-${NVER}-${platform}-x86.tar.gz - # enable 32 bit iojs - export PATH=$(pwd)/iojs-${NVER}-${platform}-x86/bin:$(pwd)/iojs-${NVER}-${platform}-ia32/bin:$PATH - else - wget http://nodejs.org/dist/${NVER}/node-${NVER}-${platform}-x86.tar.gz - tar xf node-${NVER}-${platform}-x86.tar.gz - # enable 32 bit node - export PATH=$(pwd)/node-${NVER}-${platform}-x86/bin:$(pwd)/node-${NVER}-${platform}-ia32/bin:$PATH - fi - which node - ls -l $(which node) - #node -e "console.log(process.arch,process.execPath)" - # install 32 bit compiler toolchain and X11 - # test source compile in 32 bit mode with internal libsqlite3 - CC=gcc-4.6 CXX=g++-4.6 npm install --build-from-source --clang=1 - node-pre-gyp package testpackage - npm test - publish - make clean - # broken for some unknown reason against io.js - if [[ ${NODE_VERSION:0:4} != 'iojs' ]]; then - # test source compile in 32 bit mode against external libsqlite3 - export NODE_SQLITE3_JSON1=no - CC=gcc-4.6 CXX=g++-4.6 npm install --build-from-source --sqlite=/usr --clang=1 - npm test - fi -fi - -' diff --git a/scripts/build_against_node_webkit.sh b/scripts/build_against_node_webkit.sh deleted file mode 100755 index 94706b4fd..000000000 --- a/scripts/build_against_node_webkit.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash - - -if [[ ! -d ../.nvm ]]; then - git clone https://github.com/creationix/nvm.git ../.nvm -fi -source ../.nvm/nvm.sh -nvm install 0.10 - -set -u -e - -npm install nw-gyp -g - -OLD_PATH="$PATH" - -GYP_ARGS="--runtime=node-webkit --target=${NODE_WEBKIT} --target_arch=${TARGET_ARCH}" -if [[ $(uname -s) == 'Darwin' ]]; then - if [[ '${TARGET_ARCH}' == 'x64' ]]; then - # do not build on Mac OS X x64 until node-webkit 0.10.1 is released - false - fi -fi - -if [[ $(uname -s) == 'Darwin' ]]; then - export NW_DOWNLOAD=node-webkit-v${NODE_WEBKIT}-osx-${TARGET_ARCH} - wget http://dl.node-webkit.org/v${NODE_WEBKIT}/${NW_DOWNLOAD}.zip - unzip -q ${NW_DOWNLOAD}.zip - export PATH=$(pwd)/node-webkit.app/Contents/MacOS/:${PATH} - # v0.10.0-rc1 unzips with extra folder - export PATH=$(pwd)/${NW_DOWNLOAD}/node-webkit.app/Contents/MacOS/:${PATH} - npm install --build-from-source ${GYP_ARGS} -else - sudo apt-get install build-essential - # Linux - export NW_DOWNLOAD=node-webkit-v${NODE_WEBKIT}-linux-${TARGET_ARCH} - # for testing node-webkit, launch a virtual display - export DISPLAY=:99.0 - # NOTE: travis already has xvfb installed - # http://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-GUI-%28e.g.-a-Web-browser%29 - sh -e /etc/init.d/xvfb start +extension RANDR - wget http://dl.node-webkit.org/v${NODE_WEBKIT}/${NW_DOWNLOAD}.tar.gz - tar xf ${NW_DOWNLOAD}.tar.gz - export PATH=$(pwd)/${NW_DOWNLOAD}:${PATH} - if [[ "${TARGET_ARCH}" == 'ia32' ]]; then - # for nw >= 0.11.0 on ia32 we need gcc/g++ 4.8 - IFS='.' read -a NODE_WEBKIT_VERSION <<< "${NODE_WEBKIT}" - if test ${NODE_WEBKIT_VERSION[0]} -ge 0 -a ${NODE_WEBKIT_VERSION[1]} -ge 11; then - # travis-ci runs ubuntu 12.04, so we need this ppa for gcc/g++ 4.8 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - export CC=gcc-4.8 - export CXX=g++-4.8 - export CXXFLAGS="-fpermissive" - COMPILER_PACKAGES="gcc-4.8-multilib g++-4.8-multilib" - else - export CC=gcc-4.6 - export CXX=g++-4.6 - export CXXFLAGS="-fpermissive" - COMPILER_PACKAGES="gcc-multilib g++-multilib" - fi - # need to update to avoid 404 for linux-libc-dev_3.2.0-64.97_amd64.deb - sudo apt-get update - # prepare packages for 32-bit builds on Linux - sudo apt-get -y install $COMPILER_PACKAGES libx11-6:i386 libnotify4:i386 libxtst6:i386 libcap2:i386 libglib2.0-0:i386 libgtk2.0-0:i386 libatk1.0-0:i386 libgdk-pixbuf2.0-0:i386 libcairo2:i386 libfreetype6:i386 libfontconfig1:i386 libxcomposite1:i386 libasound2:i386 libxdamage1:i386 libxext6:i386 libxfixes3:i386 libnss3:i386 libnspr4:i386 libgconf-2-4:i386 libexpat1:i386 libdbus-1-3:i386 libudev0:i386 - # also use ldd to find out if some necessary apt-get is missing - ldd $(pwd)/${NW_DOWNLOAD}/nw - npm install --build-from-source ${GYP_ARGS} - else - npm install --build-from-source ${GYP_ARGS} - fi -fi - -# test the package -node-pre-gyp package testpackage ${GYP_ARGS} - -PUBLISH_BINARY=false -if test "${COMMIT_MESSAGE#*'[publish binary]'}" != "$COMMIT_MESSAGE"; then - node-pre-gyp publish ${GYP_ARGS} - node-pre-gyp info ${GYP_ARGS} - node-pre-gyp clean ${GYP_ARGS} - make clean - # now install from binary - INSTALL_RESULT=$(npm install ${GYP_ARGS} --fallback-to-build=false > /dev/null)$? || true - # if install returned non zero (errored) then we first unpublish and then call false so travis will bail at this line - if [[ $INSTALL_RESULT != 0 ]]; then echo "returned $INSTALL_RESULT";node-pre-gyp unpublish ${GYP_ARGS};false; fi - # If success then we arrive here so lets clean up - node-pre-gyp clean ${GYP_ARGS} -fi - -# restore PATH -export PATH="$OLD_PATH" -rm -rf ${NW_DOWNLOAD} diff --git a/scripts/build_for_node_webkit.cmd b/scripts/build_for_node_webkit.cmd deleted file mode 100644 index 2a0878fc4..000000000 --- a/scripts/build_for_node_webkit.cmd +++ /dev/null @@ -1,15 +0,0 @@ -echo Platform: %1 -echo The list of environment variables: -set -if not "%1" == "x86" goto end -if "%nw_version%" == "" goto end -call npm install nw-gyp -call cinst wget 7zip.commandline -call wget http://dl.node-webkit.org/v%nw_version%/node-webkit-v%nw_version%-win-ia32.zip -call 7z e -onw node-webkit-v%nw_version%-win-ia32.zip -dir nw -set PATH=nw;%PATH% -call node-pre-gyp rebuild --runtime=node-webkit --target=%nw_version% --target_arch=ia32 -call node-pre-gyp package testpackage --runtime=node-webkit --target=%nw_version% --target_arch=ia32 -if not "%CM%" == "%CM:[publish binary]=%" call node-pre-gyp publish --msvs_version=2013 --runtime=node-webkit --target=%nw_version% --target_arch=ia32 -:end \ No newline at end of file diff --git a/scripts/install_node.sh b/scripts/install_node.sh deleted file mode 100755 index d534f77a5..000000000 --- a/scripts/install_node.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -if [[ ${1:-false} == 'false' ]]; then - echo "Error: pass node version as first argument" - exit 1 -fi - -NODE_VERSION=$1 - -# if an existing nvm is already installed we need to unload it -nvm unload || true - -# here we set up the node version on the fly based on the matrix value. -# This is done manually so that the build works the same on OS X -rm -rf ./__nvm/ && git clone --depth 1 https://github.com/creationix/nvm.git ./__nvm -source ./__nvm/nvm.sh -nvm install ${NODE_VERSION} -nvm use ${NODE_VERSION} -node --version -npm --version -which node \ No newline at end of file diff --git a/scripts/validate_tag.sh b/scripts/validate_tag.sh index 67d039b70..7b85d1969 100755 --- a/scripts/validate_tag.sh +++ b/scripts/validate_tag.sh @@ -11,7 +11,7 @@ if [ `git describe --tags --always HEAD` ]; then if [[ $TRAVIS_BRANCH == `git describe --tags --always HEAD` ]]; then echo 'git reports the same tag as travis' # now check to make sure package.json `version` matches - MODULE_VERSION=$(node -e "console.log(require('./package.json').version)") + MODULE_VERSION=$(node -p "require('./package.json').version") if [[ $MODULE_VERSION != $TRAVIS_BRANCH ]] && [[ v$MODULE_VERSION != $TRAVIS_BRANCH ]]; then echo "package.json version ($MODULE_VERSION) does not match tag ($TRAVIS_BRANCH)" exit 1 diff --git a/src/statement.cc b/src/statement.cc index 6efbe5766..569ebcb1c 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -408,8 +408,8 @@ void Statement::Work_AfterGet(uv_work_t* req) { if (!cb.IsEmpty() && cb->IsFunction()) { if (stmt->status == SQLITE_ROW) { // Create the result array from the data we acquired. - Local argv[] = { Nan::Null(), RowToJS(&baton->row) }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + Local argv[] = { Nan::Null(), RowToJS(&baton->row), GetFields(stmt->_handle) }; + TRY_CATCH_CALL(stmt->handle(), cb, 3, argv); } else { Local argv[] = { Nan::Null() }; @@ -552,8 +552,8 @@ void Statement::Work_AfterAll(uv_work_t* req) { delete *it; } - Local argv[] = { Nan::Null(), result }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + Local argv[] = { Nan::Null(), result, GetFields(stmt->_handle) }; + TRY_CATCH_CALL(stmt->handle(), cb, 3, argv); } else { // There were no result rows. @@ -669,15 +669,17 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { Local cb = Nan::New(async->item_cb); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[2]; + Local argv[3]; + Local fields = GetFields(async->stmt->_handle); argv[0] = Nan::Null(); Rows::const_iterator it = rows.begin(); Rows::const_iterator end = rows.end(); for (int i = 0; it < end; ++it, i++) { argv[1] = RowToJS(*it); + argv[2] = fields; async->retrieved++; - TRY_CATCH_CALL(async->stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(async->stmt->handle(), cb, 3, argv); delete *it; } } @@ -746,10 +748,10 @@ void Statement::Work_AfterReset(uv_work_t* req) { STATEMENT_END(); } -Local Statement::RowToJS(Row* row) { +Local Statement::RowToJS(Row* row) { Nan::EscapableHandleScope scope; - Local result = Nan::New(); + Local result = Nan::New(); Row::const_iterator it = row->begin(); Row::const_iterator end = row->end(); @@ -776,7 +778,7 @@ Local Statement::RowToJS(Row* row) { } break; } - Nan::Set(result, Nan::New(field->name.c_str()).ToLocalChecked(), value); + Nan::Set(result, i, value); DELETE_FIELD(field); } @@ -816,6 +818,27 @@ void Statement::GetRow(Row* row, sqlite3_stmt* stmt) { } } +Local Statement::GetFields(sqlite3_stmt* stmt) { + Nan::EscapableHandleScope scope; + + int count = sqlite3_column_count(stmt); + Local fields(Nan::New(sqlite3_column_count(stmt))); + + for (int i = 0; i < count; i++) { + Local field = Nan::New(); + Nan::Set(field, Nan::New("name").ToLocalChecked(), Nan::New(sqlite3_column_name(stmt, i)).ToLocalChecked()); + const char* orgName = sqlite3_column_origin_name(stmt, i); + Nan::Set(field, Nan::New("orgName").ToLocalChecked(), Nan::New(orgName ? orgName : "").ToLocalChecked()); + const char* table = sqlite3_column_table_alias_name(stmt, i); + Nan::Set(field, Nan::New("table").ToLocalChecked(), Nan::New(table ? table : "").ToLocalChecked()); + const char* orgTable = sqlite3_column_table_name(stmt, i); + Nan::Set(field, Nan::New("orgTable").ToLocalChecked(), Nan::New(orgTable ? orgTable : "").ToLocalChecked()); + Nan::Set(fields, i, field); + } + + return scope.Escape(fields); +} + NAN_METHOD(Statement::Finalize) { Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); OPTIONAL_ARGUMENT_FUNCTION(0, callback); diff --git a/src/statement.h b/src/statement.h index 90d295b70..f96c2bd17 100644 --- a/src/statement.h +++ b/src/statement.h @@ -70,7 +70,6 @@ typedef std::vector Rows; typedef Row Parameters; - class Statement : public Nan::ObjectWrap { public: static Nan::Persistent constructor_template; @@ -224,7 +223,8 @@ class Statement : public Nan::ObjectWrap { bool Bind(const Parameters ¶meters); static void GetRow(Row* row, sqlite3_stmt* stmt); - static Local RowToJS(Row* row); + static Local GetFields(sqlite3_stmt* stmt); + static Local RowToJS(Row* row); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue(); diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 000000000..7eeefc33b --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "mocha": true + } +} diff --git a/test/named_columns.test.js b/test/named_columns.test.js index 9973bfcee..1960d82ff 100644 --- a/test/named_columns.test.js +++ b/test/named_columns.test.js @@ -20,7 +20,7 @@ describe('named columns', function() { it('should retrieve the values', function(done) { db.get("SELECT txt, num FROM foo ORDER BY num", function(err, row) { - if (err) throw err; + if (err) done(err); assert.equal(row.txt, "Lorem Ipsum"); assert.equal(row.num, 1); done(); @@ -29,7 +29,7 @@ describe('named columns', function() { it('should be able to retrieve rowid of last inserted value', function(done) { db.get("SELECT last_insert_rowid() as last_id FROM foo", function(err, row) { - if (err) throw err; + if (err) done(err); assert.equal(row.last_id, 1); done(); });