From 7be18e1a084007d5e9b17889cd6ca47a79e2cedf Mon Sep 17 00:00:00 2001 From: ijin08 Date: Thu, 21 Feb 2019 15:05:17 +0100 Subject: [PATCH 01/40] changed some more color variables to use variables --- .../src/themes/_variables.dark.scss.tmpl.ts | 46 +++++++++---------- .../src/themes/_variables.light.scss.tmpl.ts | 46 +++++++++---------- packages/grafana-ui/src/themes/dark.ts | 4 ++ packages/grafana-ui/src/themes/light.ts | 8 +++- packages/grafana-ui/src/types/theme.ts | 4 ++ public/sass/_variables.dark.generated.scss | 38 +++++++-------- public/sass/_variables.light.generated.scss | 44 +++++++++--------- 7 files changed, 101 insertions(+), 89 deletions(-) diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts index 3a2d6db458c..d6df6f51374 100644 --- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts @@ -54,34 +54,34 @@ $orange: ${theme.colors.orange}; $purple: ${theme.colors.purple}; $variable: ${theme.colors.variable}; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $brand-primary; -$brand-danger: $red-base; +$brand-primary: ${theme.colors.brandPrimary}; +$brand-success: ${theme.colors.brandSuccess}; +$brand-warning: ${theme.colors.brandWarning}; +$brand-danger: ${theme.colors.brandDanger}; -$query-red: $red-base; -$query-green: #74e680; -$query-purple: #fe85fc; -$query-keyword: #66d9ef; -$query-orange: $orange; +$query-red: ${theme.colors.queryRed}; +$query-green: ${theme.colors.queryGreen}; +$query-purple: ${theme.colors.queryPurple}; +$query-orange: ${theme.colors.orange}; +$query-keyword: ${theme.colors.queryKeyword}; // Status colors // ------------------------- -$online: $green-base; -$warn: #f79520; -$critical: $red-base; +$online: ${theme.colors.online}; +$warn: ${theme.colors.warn}; +$critical: ${theme.colors.critical}; // Scaffolding // ------------------------- $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: $gray-4; -$text-color: $gray-4; -$text-color-strong: $white; -$text-color-weak: $gray-2; -$text-color-faint: $dark-10; -$text-color-emphasis: $gray-5; +$body-color: ${theme.colors.bodyColor}; +$text-color: ${theme.colors.textColor}; +$text-color-strong: ${theme.colors.textColorStrong}; +$text-color-weak: ${theme.colors.textColorWeak}; +$text-color-faint: ${theme.colors.textColorFaint}; +$text-color-emphasis: ${theme.colors.textColorEmphasis}; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); $textShadow: none; @@ -99,14 +99,14 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- -$link-color: darken($white, 11%); -$link-color-disabled: darken($link-color, 30%); -$link-hover-color: $white; -$external-link-color: $blue-light; +$link-color: ${theme.colors.linkColor}; +$link-color-disabled: ${theme.colors.linkColorDisabled}; +$link-hover-color: ${theme.colors.linkColorHover}; +$external-link-color: ${theme.colors.linkColorExternal}; // Typography // ------------------------- -$headings-color: darken($white, 11%); +$headings-color: ${theme.colors.headingColor}; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; diff --git a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts index 1b017b7eb0d..eb48ab5cc02 100644 --- a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts @@ -46,34 +46,34 @@ $orange: ${theme.colors.orange}; $purple: ${theme.colors.purple}; $variable: ${theme.colors.variable}; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $orange; -$brand-danger: $red-base; +$brand-primary: ${theme.colors.brandPrimary}; +$brand-success: ${theme.colors.brandSuccess}; +$brand-warning: ${theme.colors.brandWarning}; +$brand-danger: ${theme.colors.brandDanger}; -$query-red: $red-base; -$query-green: $green-base; -$query-purple: $purple; -$query-orange: $orange; -$query-keyword: $blue-base; +$query-red: ${theme.colors.queryRed}; +$query-green: ${theme.colors.queryGreen}; +$query-purple: ${theme.colors.queryPurple}; +$query-orange: ${theme.colors.orange}; +$query-keyword: ${theme.colors.queryKeyword}; // Status colors // ------------------------- -$online: $green-shade; -$warn: #f79520; -$critical: $red-shade; +$online: ${theme.colors.online}; +$warn: ${theme.colors.warn}; +$critical: ${theme.colors.critical}; // Scaffolding // ------------------------- $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: $gray-1; -$text-color: $gray-1; -$text-color-strong: $dark-1; -$text-color-weak: $gray-2; -$text-color-faint: $gray-4; -$text-color-emphasis: $dark-2; +$body-color: ${theme.colors.bodyColor}; +$text-color: ${theme.colors.textColor}; +$text-color-strong: ${theme.colors.textColorStrong}; +$text-color-weak: ${theme.colors.textColorWeak}; +$text-color-faint: ${theme.colors.textColorFaint}; +$text-color-emphasis: ${theme.colors.textColorEmphasis}; $text-shadow-faint: none; @@ -85,14 +85,14 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links // ------------------------- -$link-color: $gray-1; -$link-color-disabled: lighten($link-color, 30%); -$link-hover-color: darken($link-color, 20%); -$external-link-color: $blue-shade; +$link-color: ${theme.colors.linkColor}; +$link-color-disabled: ${theme.colors.linkColorDisabled}; +$link-hover-color: ${theme.colors.linkColorHover}; +$external-link-color: ${theme.colors.linkColorExternal}; // Typography // ------------------------- -$headings-color: $text-color; +$headings-color: ${theme.colors.headingColor}; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; diff --git a/packages/grafana-ui/src/themes/dark.ts b/packages/grafana-ui/src/themes/dark.ts index 7c3e81a7d35..3e8057a4acd 100644 --- a/packages/grafana-ui/src/themes/dark.ts +++ b/packages/grafana-ui/src/themes/dark.ts @@ -46,6 +46,10 @@ const darkTheme: GrafanaTheme = { colors: { ...basicColors, inputBlack: '#09090b', + brandPrimary: basicColors.orange, + brandSuccess: basicColors.greenBase, + brandWarning: basicColors.orange, + brandDanger: basicColors.redBase, queryRed: basicColors.redBase, queryGreen: '#74e680', queryPurple: '#fe85fc', diff --git a/packages/grafana-ui/src/themes/light.ts b/packages/grafana-ui/src/themes/light.ts index 7e8f6300d84..2461505d507 100644 --- a/packages/grafana-ui/src/themes/light.ts +++ b/packages/grafana-ui/src/themes/light.ts @@ -47,10 +47,14 @@ const lightTheme: GrafanaTheme = { ...basicColors, variable: basicColors.blue, inputBlack: '#09090b', - queryRed: basicColors.red, + brandPrimary: basicColors.orange, + brandSuccess: basicColors.greenBase, + brandWarning: basicColors.orange, + brandDanger: basicColors.redBase, + queryRed: basicColors.redBase, queryGreen: basicColors.greenBase, queryPurple: basicColors.purple, - queryKeyword: basicColors.blue, + queryKeyword: basicColors.blueBase, queryOrange: basicColors.orange, online: basicColors.greenShade, warn: '#f79520', diff --git a/packages/grafana-ui/src/types/theme.ts b/packages/grafana-ui/src/types/theme.ts index 01886afe3dc..f7adbf80247 100644 --- a/packages/grafana-ui/src/types/theme.ts +++ b/packages/grafana-ui/src/types/theme.ts @@ -113,6 +113,10 @@ export interface GrafanaTheme extends GrafanaThemeCommons { queryPurple: string; queryKeyword: string; queryOrange: string; + brandPrimary: string; + brandSuccess: string; + brandWarning: string; + brandDanger: string; // Status colors online: string; diff --git a/public/sass/_variables.dark.generated.scss b/public/sass/_variables.dark.generated.scss index 3f1e2e4f314..a86134cf4bd 100644 --- a/public/sass/_variables.dark.generated.scss +++ b/public/sass/_variables.dark.generated.scss @@ -57,34 +57,34 @@ $orange: #eb7b18; $purple: #9933cc; $variable: #32d1df; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $brand-primary; -$brand-danger: $red-base; +$brand-primary: #eb7b18; +$brand-success: #299c46; +$brand-warning: #eb7b18; +$brand-danger: #e02f44; -$query-red: $red-base; +$query-red: #e02f44; $query-green: #74e680; $query-purple: #fe85fc; +$query-orange: #eb7b18; $query-keyword: #66d9ef; -$query-orange: $orange; // Status colors // ------------------------- -$online: $green-base; +$online: #299c46; $warn: #f79520; -$critical: $red-base; +$critical: #e02f44; // Scaffolding // ------------------------- $body-bg: #161719; $page-bg: #161719; -$body-color: $gray-4; -$text-color: $gray-4; -$text-color-strong: $white; -$text-color-weak: $gray-2; -$text-color-faint: $dark-10; -$text-color-emphasis: $gray-5; +$body-color: #d8d9da; +$text-color: #d8d9da; +$text-color-strong: #ffffff; +$text-color-weak: #8e8e8e; +$text-color-faint: #222426; +$text-color-emphasis: #ececec; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); $textShadow: none; @@ -102,14 +102,14 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- -$link-color: darken($white, 11%); -$link-color-disabled: darken($link-color, 30%); -$link-hover-color: $white; -$external-link-color: $blue-light; +$link-color: #e3e3e3; +$link-color-disabled: #e3e3e3; +$link-hover-color: #ffffff; +$external-link-color: #33b5e5; // Typography // ------------------------- -$headings-color: darken($white, 11%); +$headings-color: #e3e3e3; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; diff --git a/public/sass/_variables.light.generated.scss b/public/sass/_variables.light.generated.scss index 4aea0a4f993..4d1dd96bccf 100644 --- a/public/sass/_variables.light.generated.scss +++ b/public/sass/_variables.light.generated.scss @@ -49,34 +49,34 @@ $orange: #ff7941; $purple: #9954bb; $variable: #0083b3; -$brand-primary: $orange; -$brand-success: $green-base; -$brand-warning: $orange; -$brand-danger: $red-base; +$brand-primary: #ff7941; +$brand-success: #3eb15b; +$brand-warning: #ff7941; +$brand-danger: #e02f44; -$query-red: $red-base; -$query-green: $green-base; -$query-purple: $purple; -$query-orange: $orange; -$query-keyword: $blue-base; +$query-red: #e02f44; +$query-green: #3eb15b; +$query-purple: #9954bb; +$query-orange: #ff7941; +$query-keyword: #3274d9; // Status colors // ------------------------- -$online: $green-shade; +$online: #369b4f; $warn: #f79520; -$critical: $red-shade; +$critical: #c4162a; // Scaffolding // ------------------------- $body-bg: #f7f8fa; $page-bg: #f7f8fa; -$body-color: $gray-1; -$text-color: $gray-1; -$text-color-strong: $dark-1; -$text-color-weak: $gray-2; -$text-color-faint: $gray-4; -$text-color-emphasis: $dark-2; +$body-color: #52545c; +$text-color: #52545c; +$text-color-strong: #41444b; +$text-color-weak: #767980; +$text-color-faint: #35373f; +$text-color-emphasis: #dde4ed; $text-shadow-faint: none; @@ -88,14 +88,14 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links // ------------------------- -$link-color: $gray-1; -$link-color-disabled: lighten($link-color, 30%); -$link-hover-color: darken($link-color, 20%); -$external-link-color: $blue-shade; +$link-color: #52545c; +$link-color-disabled: #9ea0a9; +$link-hover-color: #222326; +$external-link-color: #5794f2; // Typography // ------------------------- -$headings-color: $text-color; +$headings-color: #52545c; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; From 05d5e796d76f5af73f7990f8571fe463a091acef Mon Sep 17 00:00:00 2001 From: ijin08 Date: Fri, 22 Feb 2019 08:33:07 +0100 Subject: [PATCH 02/40] removed color in color variables names --- .../src/themes/_variables.dark.scss.tmpl.ts | 20 ++++++++-------- .../src/themes/_variables.light.scss.tmpl.ts | 20 ++++++++-------- packages/grafana-ui/src/themes/dark.ts | 20 ++++++++-------- packages/grafana-ui/src/themes/light.ts | 20 ++++++++-------- packages/grafana-ui/src/types/theme.ts | 24 +++++++++++-------- 5 files changed, 54 insertions(+), 50 deletions(-) diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts index d6df6f51374..54c00f9237e 100644 --- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts @@ -76,12 +76,12 @@ $critical: ${theme.colors.critical}; $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: ${theme.colors.bodyColor}; -$text-color: ${theme.colors.textColor}; -$text-color-strong: ${theme.colors.textColorStrong}; -$text-color-weak: ${theme.colors.textColorWeak}; -$text-color-faint: ${theme.colors.textColorFaint}; -$text-color-emphasis: ${theme.colors.textColorEmphasis}; +$body-color: ${theme.colors.body}; +$text-color: ${theme.colors.text}; +$text-color-strong: ${theme.colors.textStrong}; +$text-color-weak: ${theme.colors.textWeak}; +$text-color-faint: ${theme.colors.textFaint}; +$text-color-emphasis: ${theme.colors.textEmphasis}; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); $textShadow: none; @@ -99,10 +99,10 @@ $edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- -$link-color: ${theme.colors.linkColor}; -$link-color-disabled: ${theme.colors.linkColorDisabled}; -$link-hover-color: ${theme.colors.linkColorHover}; -$external-link-color: ${theme.colors.linkColorExternal}; +$link-color: ${theme.colors.link}; +$link-color-disabled: ${theme.colors.linkDisabled}; +$link-hover-color: ${theme.colors.linkHover}; +$external-link-color: ${theme.colors.linkExternal}; // Typography // ------------------------- diff --git a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts index eb48ab5cc02..e90299f619c 100644 --- a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts @@ -68,12 +68,12 @@ $critical: ${theme.colors.critical}; $body-bg: ${theme.colors.bodyBg}; $page-bg: ${theme.colors.pageBg}; -$body-color: ${theme.colors.bodyColor}; -$text-color: ${theme.colors.textColor}; -$text-color-strong: ${theme.colors.textColorStrong}; -$text-color-weak: ${theme.colors.textColorWeak}; -$text-color-faint: ${theme.colors.textColorFaint}; -$text-color-emphasis: ${theme.colors.textColorEmphasis}; +$body-color: ${theme.colors.body}; +$text-color: ${theme.colors.text}; +$text-color-strong: ${theme.colors.textStrong}; +$text-color-weak: ${theme.colors.textWeak}; +$text-color-faint: ${theme.colors.textFaint}; +$text-color-emphasis: ${theme.colors.textEmphasis}; $text-shadow-faint: none; @@ -85,10 +85,10 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); // Links // ------------------------- -$link-color: ${theme.colors.linkColor}; -$link-color-disabled: ${theme.colors.linkColorDisabled}; -$link-hover-color: ${theme.colors.linkColorHover}; -$external-link-color: ${theme.colors.linkColorExternal}; +$link-color: ${theme.colors.link}; +$link-color-disabled: ${theme.colors.linkDisabled}; +$link-hover-color: ${theme.colors.linkHover}; +$external-link-color: ${theme.colors.linkExternal}; // Typography // ------------------------- diff --git a/packages/grafana-ui/src/themes/dark.ts b/packages/grafana-ui/src/themes/dark.ts index 3e8057a4acd..1e424c97154 100644 --- a/packages/grafana-ui/src/themes/dark.ts +++ b/packages/grafana-ui/src/themes/dark.ts @@ -60,16 +60,16 @@ const darkTheme: GrafanaTheme = { critical: basicColors.redBase, bodyBg: basicColors.dark2, pageBg: basicColors.dark2, - bodyColor: basicColors.gray4, - textColor: basicColors.gray4, - textColorStrong: basicColors.white, - textColorWeak: basicColors.gray2, - textColorEmphasis: basicColors.gray5, - textColorFaint: basicColors.dark5, - linkColor: new tinycolor(basicColors.white).darken(11).toString(), - linkColorDisabled: new tinycolor(basicColors.white).darken(11).toString(), - linkColorHover: basicColors.white, - linkColorExternal: basicColors.blue, + body: basicColors.gray4, + text: basicColors.gray4, + textStrong: basicColors.white, + textWeak: basicColors.gray2, + textEmphasis: basicColors.gray5, + textFaint: basicColors.dark5, + link: new tinycolor(basicColors.white).darken(11).toString(), + linkDisabled: new tinycolor(basicColors.white).darken(11).toString(), + linkHover: basicColors.white, + linkExternal: basicColors.blue, headingColor: new tinycolor(basicColors.white).darken(11).toString(), }, background: { diff --git a/packages/grafana-ui/src/themes/light.ts b/packages/grafana-ui/src/themes/light.ts index 2461505d507..a3994fc7458 100644 --- a/packages/grafana-ui/src/themes/light.ts +++ b/packages/grafana-ui/src/themes/light.ts @@ -61,16 +61,16 @@ const lightTheme: GrafanaTheme = { critical: basicColors.redShade, bodyBg: basicColors.gray7, pageBg: basicColors.gray7, - bodyColor: basicColors.gray1, - textColor: basicColors.gray1, - textColorStrong: basicColors.dark2, - textColorWeak: basicColors.gray2, - textColorEmphasis: basicColors.gray5, - textColorFaint: basicColors.dark4, - linkColor: basicColors.gray1, - linkColorDisabled: new tinycolor(basicColors.gray1).lighten(30).toString(), - linkColorHover: new tinycolor(basicColors.gray1).darken(20).toString(), - linkColorExternal: basicColors.blueLight, + body: basicColors.gray1, + text: basicColors.gray1, + textStrong: basicColors.dark2, + textWeak: basicColors.gray2, + textEmphasis: basicColors.gray5, + textFaint: basicColors.dark4, + link: basicColors.gray1, + linkDisabled: new tinycolor(basicColors.gray1).lighten(30).toString(), + linkHover: new tinycolor(basicColors.gray1).darken(20).toString(), + linkExternal: basicColors.blueLight, headingColor: basicColors.gray1, }, background: { diff --git a/packages/grafana-ui/src/types/theme.ts b/packages/grafana-ui/src/types/theme.ts index f7adbf80247..30f1bf4685b 100644 --- a/packages/grafana-ui/src/types/theme.ts +++ b/packages/grafana-ui/src/types/theme.ts @@ -123,19 +123,23 @@ export interface GrafanaTheme extends GrafanaThemeCommons { warn: string; critical: string; + // Link colors + link: string; + linkDisabled: string; + linkHover: string; + linkExternal: string; + + // Text colors + body: string; + text: string; + textStrong: string; + textWeak: string; + textFaint: string; + textEmphasis: string; + // TODO: move to background section bodyBg: string; pageBg: string; - bodyColor: string; - textColor: string; - textColorStrong: string; - textColorWeak: string; - textColorFaint: string; - textColorEmphasis: string; - linkColor: string; - linkColorDisabled: string; - linkColorHover: string; - linkColorExternal: string; headingColor: string; }; } From 73ef864979b4ab21b81c587154bfd5961ade734b Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 08:56:29 +0100 Subject: [PATCH 03/40] Minor refactor of cli tasks (core start, gui publishing) --- package.json | 15 ++-- scripts/cli/index.ts | 66 +++++++++------ scripts/cli/tasks/core.start.ts | 41 +++++---- scripts/cli/tasks/grafanaui.build.ts | 101 +++++++++------------- scripts/cli/tasks/grafanaui.release.ts | 111 +++++++++++++------------ scripts/cli/tasks/task.ts | 23 +++++ scripts/cli/utils/execTask.ts | 17 +++- scripts/cli/utils/startSpinner.ts | 7 -- scripts/cli/utils/useSpinner.ts | 20 +++++ 9 files changed, 219 insertions(+), 182 deletions(-) create mode 100644 scripts/cli/tasks/task.ts delete mode 100644 scripts/cli/utils/startSpinner.ts create mode 100644 scripts/cli/utils/useSpinner.ts diff --git a/package.json b/package.json index 9e0b88804fd..c5136cf5e02 100644 --- a/package.json +++ b/package.json @@ -123,10 +123,10 @@ }, "scripts": { "dev": "webpack --progress --colors --mode development --config scripts/webpack/webpack.dev.js", - "start": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --theme", - "start:hot": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --hot --theme", - "start:ignoreTheme": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --hot", - "watch": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --theme -d watch,start", + "start": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts core:start --watchTheme", + "start:hot": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts core:start --hot --watchTheme", + "start:ignoreTheme": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts core:start --hot", + "watch": "yarn start -d watch,start core:start --watchTheme ", "build": "grunt build", "test": "grunt test", "tslint": "tslint -c tslint.json --project tsconfig.json", @@ -136,8 +136,11 @@ "storybook": "cd packages/grafana-ui && yarn storybook", "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts", "prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"", - "gui:build": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --build", - "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --release" + "gui:build": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:build", + "gui:releasePrepare": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release", + "gui:publish": "cd packages/grafana-ui/dist && npm publish --access public", + "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release -p", + "cli:help": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts --help" }, "husky": { "hooks": { diff --git a/scripts/cli/index.ts b/scripts/cli/index.ts index f980944e2cd..3e54dc97a07 100644 --- a/scripts/cli/index.ts +++ b/scripts/cli/index.ts @@ -1,33 +1,47 @@ import program from 'commander'; -import chalk from 'chalk'; import { execTask } from './utils/execTask'; +import chalk from 'chalk'; +import { startTask } from './tasks/core.start'; +import { buildTask } from './tasks/grafanaui.build'; +import { releaseTask } from './tasks/grafanaui.release'; -export type Task = (options: T) => Promise; +program.option('-d, --depreciate ', 'Inform about npm script deprecation', v => v.split(',')); -// TODO: Refactor to commander commands -// This will enable us to have command scoped options and limit the ifs below program - .option('-h, --hot', 'Runs front-end with hot reload enabled') - .option('-t, --theme', 'Watches for theme changes and regenerates variables.scss files') - .option('-d, --depreciate ', 'Inform about npm script deprecation', v => v.split(',')) - .option('-b, --build', 'Created @grafana/ui build') - .option('-r, --release', 'Releases @grafana/ui to npm') - .parse(process.argv); - -if (program.build) { - execTask('grafanaui.build'); -} else if (program.release) { - execTask('grafanaui.release'); -} else { - if (program.depreciate && program.depreciate.length === 2) { - console.log( - chalk.yellow.bold( - `[NPM script depreciation] ${program.depreciate[0]} is deprecated! Use ${program.depreciate[1]} instead!` - ) - ); - } - execTask('core.start', { - watchThemes: !!program.theme, - hot: !!program.hot, + .command('core:start') + .option('-h, --hot', 'Run front-end with HRM enabled') + .option('-t, --watchTheme', 'Watch for theme changes and regenerate variables.scss files') + .description('Starts Grafana front-end in development mode with watch enabled') + .action(async cmd => { + await execTask(startTask)({ + watchThemes: cmd.theme, + hot: cmd.hot, + }); }); + +program + .command('gui:build') + .description('Builds @grafana/ui package to packages/grafana-ui/dist') + .action(async cmd => { + await execTask(buildTask)(); + }); + +program + .command('gui:release') + .description('Prepares @grafana/ui release (and publishes to npm on demand)') + .option('-p, --publish', 'Publish @grafana/ui to npm registry') + .action(async cmd => { + await execTask(releaseTask)({ + publishToNpm: !!cmd.publish, + }); + }); + +program.parse(process.argv); + +if (program.depreciate && program.depreciate.length === 2) { + console.log( + chalk.yellow.bold( + `[NPM script depreciation] ${program.depreciate[0]} is deprecated! Use ${program.depreciate[1]} instead!` + ) + ); } diff --git a/scripts/cli/tasks/core.start.ts b/scripts/cli/tasks/core.start.ts index 4e546c71b8c..dbd2dfe7119 100644 --- a/scripts/cli/tasks/core.start.ts +++ b/scripts/cli/tasks/core.start.ts @@ -1,35 +1,30 @@ import concurrently from 'concurrently'; -import { Task } from '..'; +import { Task, TaskRunner } from './task'; interface StartTaskOptions { watchThemes: boolean; hot: boolean; } -const startTask: Task = async ({ watchThemes, hot }) => { - const jobs = []; - - if (watchThemes) { - jobs.push({ +const startTaskRunner: TaskRunner = async ({ watchThemes, hot }) => { + const jobs = [ + watchThemes && { command: 'nodemon -e ts -w ./packages/grafana-ui/src/themes -x yarn run themes:generate', name: 'SASS variables generator', - }); - } - - if (!hot) { - jobs.push({ - command: 'webpack --progress --colors --watch --mode development --config scripts/webpack/webpack.dev.js', - name: 'Webpack', - }); - } else { - jobs.push({ - command: 'webpack-dev-server --progress --colors --mode development --config scripts/webpack/webpack.hot.js', - name: 'Dev server', - }); - } + }, + hot + ? { + command: 'webpack-dev-server --progress --colors --mode development --config scripts/webpack/webpack.hot.js', + name: 'Dev server', + } + : { + command: 'webpack --progress --colors --watch --mode development --config scripts/webpack/webpack.dev.js', + name: 'Webpack', + }, + ]; try { - await concurrently(jobs, { + await concurrently(jobs.filter(job => !!job), { killOthers: ['failure', 'failure'], }); } catch (e) { @@ -38,4 +33,6 @@ const startTask: Task = async ({ watchThemes, hot }) => { } }; -export default startTask; +export const startTask = new Task(); +startTask.setName('Core startTask'); +startTask.setRunner(startTaskRunner); diff --git a/scripts/cli/tasks/grafanaui.build.ts b/scripts/cli/tasks/grafanaui.build.ts index f892c13e115..6fce809bef9 100644 --- a/scripts/cli/tasks/grafanaui.build.ts +++ b/scripts/cli/tasks/grafanaui.build.ts @@ -1,95 +1,66 @@ import execa from 'execa'; import fs from 'fs'; -import { Task } from '..'; import { changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd'; import chalk from 'chalk'; -import { startSpinner } from '../utils/startSpinner'; +import { useSpinner } from '../utils/useSpinner'; +import { Task, TaskRunner } from './task'; let distDir, cwd; -const clean = async () => { - const spinner = startSpinner('Cleaning'); - try { - await execa('npm', ['run', 'clean']); - spinner.succeed(); - } catch (e) { - spinner.fail(); - throw e; - } -}; +const clean = useSpinner('Cleaning', async () => await execa('npm', ['run', 'clean'])); -const compile = async () => { - const spinner = startSpinner('Compiling sources'); - try { - await execa('tsc', ['-p', './tsconfig.build.json']); - spinner.succeed(); - } catch (e) { - console.log(e); - spinner.fail(); - } -}; +const compile = useSpinner('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json'])); -const rollup = async () => { - const spinner = startSpinner('Bundling'); - - try { - await execa('npm', ['run', 'build']); - spinner.succeed(); - } catch (e) { - spinner.fail(); - } -}; - -export const savePackage = async (path, pkg) => { - const spinner = startSpinner('Updating package.json'); +const rollup = useSpinner('Bundling', () => execa('npm', ['run', 'build'])); +export const savePackage = useSpinner<{ + path: string; + pkg: {}; +}>('Updating package.json', async ({ path, pkg }) => { return new Promise((resolve, reject) => { fs.writeFile(path, JSON.stringify(pkg, null, 2), err => { if (err) { - spinner.fail(); - console.error(err); reject(err); return; } - spinner.succeed(); resolve(); }); }); -}; +}); const preparePackage = async pkg => { pkg.main = 'index.js'; pkg.types = 'index.d.ts'; - await savePackage(`${cwd}/dist/package.json`, pkg); -}; - -const moveFiles = async () => { - const files = ['README.md', 'CHANGELOG.md', 'index.js']; - const spinner = startSpinner(`Moving ${files.join(', ')} files`); - - const promises = files.map(file => { - return fs.copyFile(`${cwd}/${file}`, `${distDir}/${file}`, err => { - if (err) { - console.error(err); - return; - } - }); + await savePackage({ + path: `${cwd}/dist/package.json`, + pkg, }); - - try { - await Promise.all(promises); - spinner.succeed(); - } catch (e) { - spinner.fail(); - } }; -const buildTask: Task = async () => { +const moveFiles = () => { + const files = ['README.md', 'CHANGELOG.md', 'index.js']; + return useSpinner(`Moving ${files.join(', ')} files`, async () => { + const promises = files.map(file => { + return new Promise((resolve, reject) => { + fs.copyFile(`${cwd}/${file}`, `${distDir}/${file}`, err => { + if (err) { + reject(err); + return; + } + resolve(); + }); + }); + }); + + await Promise.all(promises); + })(); +}; + +const buildTaskRunner: TaskRunner = async () => { cwd = changeCwdToGrafanaUi(); distDir = `${cwd}/dist`; const pkg = require(`${cwd}/package.json`); - - console.log(chalk.yellow(`Building ${pkg.name} @ ${pkg.version}`)); + console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`)); await clean(); await compile(); @@ -100,4 +71,6 @@ const buildTask: Task = async () => { restoreCwd(); }; -export default buildTask; +export const buildTask = new Task(); +buildTask.setName('@grafana/ui build'); +buildTask.setRunner(buildTaskRunner); diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index 412c96e7fb5..b363ace35e0 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -1,14 +1,18 @@ import execa from 'execa'; -import { Task } from '..'; import { execTask } from '../utils/execTask'; import { changeCwdToGrafanaUiDist, changeCwdToGrafanaUi } from '../utils/cwd'; import semver from 'semver'; import inquirer from 'inquirer'; import chalk from 'chalk'; -import { startSpinner } from '../utils/startSpinner'; -import { savePackage } from './grafanaui.build'; +import { useSpinner } from '../utils/useSpinner'; +import { savePackage, buildTask } from './grafanaui.build'; +import { TaskRunner, Task } from './task'; -type VersionBumpType = 'patch' | 'minor' | 'major'; +type VersionBumpType = 'prerelease' | 'patch' | 'minor' | 'major'; + +interface ReleaseTaskOptions { + publishToNpm: boolean; +} const promptBumpType = async () => { return inquirer.prompt<{ type: VersionBumpType }>([ @@ -16,7 +20,7 @@ const promptBumpType = async () => { type: 'list', message: 'Select version bump', name: 'type', - choices: ['patch', 'minor', 'major'], + choices: ['prerelease', 'patch', 'minor', 'major'], validate: answer => { if (answer.length < 1) { return 'You must choose something'; @@ -28,13 +32,13 @@ const promptBumpType = async () => { ]); }; -const promptPrereleaseId = async () => { +const promptPrereleaseId = async (message = 'Is this a prerelease?', allowNo = true) => { return inquirer.prompt<{ id: string }>([ { type: 'list', - message: 'Is this a prerelease?', + message: message, name: 'id', - choices: ['no', 'alpha', 'beta'], + choices: allowNo ? ['no', 'alpha', 'beta'] : ['alpha', 'beta'], validate: answer => { if (answer.length < 1) { return 'You must choose something'; @@ -57,47 +61,31 @@ const promptConfirm = async (message?: string) => { ]); }; -const bumpVersion = async (version: string) => { - const spinner = startSpinner(`Saving version ${version} to package.json`); - changeCwdToGrafanaUi(); - - try { +const bumpVersion = (version: string) => + useSpinner(`Saving version ${version} to package.json`, async () => { + changeCwdToGrafanaUi(); await execa('npm', ['version', version]); - spinner.succeed(); - } catch (e) { - console.log(e); - spinner.fail(); - } + changeCwdToGrafanaUiDist(); + const pkg = require(`${process.cwd()}/package.json`); + pkg.version = version; + await savePackage({ path: `${process.cwd()}/package.json`, pkg }); + })(); - changeCwdToGrafanaUiDist(); - const pkg = require(`${process.cwd()}/package.json`); - pkg.version = version; - await savePackage(`${process.cwd()}/package.json`, pkg); -}; +const publishPackage = (name: string, version: string) => + useSpinner(`Publishing ${name} @ ${version} to npm registry...`, async () => { + changeCwdToGrafanaUiDist(); + console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`)); + const { confirmed } = await promptConfirm('Are you ready to publish to npm?'); -const publishPackage = async (name: string, version: string) => { - changeCwdToGrafanaUiDist(); - console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`)); - const { confirmed } = await promptConfirm('Are you ready to publish to npm?'); - - if (!confirmed) { - process.exit(); - } - - const spinner = startSpinner(`Publishing ${name} @ ${version} to npm registry...`); - - try { + if (!confirmed) { + process.exit(); + } await execa('npm', ['publish', '--access', 'public']); - spinner.succeed(); - } catch (e) { - console.log(e); - spinner.fail(); - process.exit(1); - } -}; + })(); + +const releaseTaskRunner: TaskRunner = async ({ publishToNpm }) => { + await execTask(buildTask)(); -const releaseTask: Task = async () => { - await execTask('grafanaui.build'); let releaseConfirmed = false; let nextVersion; changeCwdToGrafanaUiDist(); @@ -108,12 +96,17 @@ const releaseTask: Task = async () => { do { const { type } = await promptBumpType(); - const { id } = await promptPrereleaseId(); - - if (id !== 'no') { - nextVersion = semver.inc(pkg.version, `pre${type}`, id); + console.log(type); + if (type === 'prerelease') { + const { id } = await promptPrereleaseId('What kind of prerelease?', false); + nextVersion = semver.inc(pkg.version, type, id); } else { - nextVersion = semver.inc(pkg.version, type); + const { id } = await promptPrereleaseId(); + if (id !== 'no') { + nextVersion = semver.inc(pkg.version, `pre${type}`, id); + } else { + nextVersion = semver.inc(pkg.version, type); + } } console.log(chalk.yellowBright.bold(`You are going to release a new version of ${pkg.name}`)); @@ -124,10 +117,22 @@ const releaseTask: Task = async () => { } while (!releaseConfirmed); await bumpVersion(nextVersion); - await publishPackage(pkg.name, nextVersion); - console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`)); - console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created - COMMIT THIS FILE!`)); + if (publishToNpm) { + await publishPackage(pkg.name, nextVersion); + console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`)); + console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created - COMMIT THIS FILE!`)); + process.exit(); + } else { + console.log( + chalk.green( + `\nVersion ${nextVersion} of ${pkg.name} succesfully prepared for release. See packages/grafana-ui/dist` + ) + ); + console.log(chalk.green(`\nTo publish to npm registry run`), chalk.bold.blue(`npm run gui:publish`)); + } }; -export default releaseTask; +export const releaseTask = new Task(); +releaseTask.setName('@grafana/ui release'); +releaseTask.setRunner(releaseTaskRunner); diff --git a/scripts/cli/tasks/task.ts b/scripts/cli/tasks/task.ts new file mode 100644 index 00000000000..d88860b7017 --- /dev/null +++ b/scripts/cli/tasks/task.ts @@ -0,0 +1,23 @@ +export type TaskRunner = (options: T) => Promise; + +export class Task { + name: string; + runner: (options: TOptions) => Promise; + options: TOptions; + + setName = name => { + this.name = name; + }; + + setRunner = (runner: TaskRunner) => { + this.runner = runner; + }; + + setOptions = options => { + this.options = options; + }; + + exec = () => { + return this.runner(this.options); + }; +} diff --git a/scripts/cli/utils/execTask.ts b/scripts/cli/utils/execTask.ts index 36071134331..f404206b7a9 100644 --- a/scripts/cli/utils/execTask.ts +++ b/scripts/cli/utils/execTask.ts @@ -1,6 +1,15 @@ -import { Task } from '..'; +import { Task } from '../tasks/task'; +import chalk from 'chalk'; -export const execTask = async (taskName, options?: T) => { - const task = await import(`${__dirname}/../tasks/${taskName}.ts`); - return task.default(options) as Task; +export const execTask = (task: Task) => async (options: TOptions) => { + console.log(chalk.yellow(`Running ${chalk.bold(task.name)} task`)); + task.setOptions(options); + try { + console.group(); + await task.exec(); + console.groupEnd(); + } catch (e) { + console.log(e); + process.exit(1); + } }; diff --git a/scripts/cli/utils/startSpinner.ts b/scripts/cli/utils/startSpinner.ts deleted file mode 100644 index ce895dec722..00000000000 --- a/scripts/cli/utils/startSpinner.ts +++ /dev/null @@ -1,7 +0,0 @@ -import ora from 'ora'; - -export const startSpinner = (label: string) => { - const spinner = new ora(label); - spinner.start(); - return spinner; -}; diff --git a/scripts/cli/utils/useSpinner.ts b/scripts/cli/utils/useSpinner.ts new file mode 100644 index 00000000000..48167e4ec2a --- /dev/null +++ b/scripts/cli/utils/useSpinner.ts @@ -0,0 +1,20 @@ +import ora from 'ora'; + +type FnToSpin = (options: T) => Promise; + +export const useSpinner = (spinnerLabel: string, fn: FnToSpin, killProcess = true) => { + return async (options: T) => { + const spinner = new ora(spinnerLabel); + spinner.start(); + try { + await fn(options); + spinner.succeed(); + } catch (e) { + spinner.fail(); + console.log(e); + if (killProcess) { + process.exit(1); + } + } + }; +}; From 3b9f0e6ef2cb2c37db20ff94bbdeb10e2c2008bc Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 5 Mar 2019 18:03:07 +0100 Subject: [PATCH 04/40] fix allow anonymous initial bind for ldap search --- pkg/login/ldap.go | 13 ++++++- pkg/login/ldap_test.go | 84 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/pkg/login/ldap.go b/pkg/login/ldap.go index c15cb865bd3..8bb331b7e59 100644 --- a/pkg/login/ldap.go +++ b/pkg/login/ldap.go @@ -18,6 +18,7 @@ import ( type ILdapConn interface { Bind(username, password string) error + UnauthenticatedBind(username string) error Search(*ldap.SearchRequest) (*ldap.SearchResult, error) StartTLS(*tls.Config) error Close() @@ -259,7 +260,17 @@ func (a *ldapAuther) initialBind(username, userPassword string) error { bindPath = fmt.Sprintf(a.server.BindDN, username) } - if err := a.conn.Bind(bindPath, userPassword); err != nil { + bindFn := func() error { + return a.conn.Bind(bindPath, userPassword) + } + + if userPassword == "" { + bindFn = func() error { + return a.conn.UnauthenticatedBind(bindPath) + } + } + + if err := bindFn(); err != nil { a.log.Info("Initial bind failed", "error", err) if ldapErr, ok := err.(*ldap.Error); ok { diff --git a/pkg/login/ldap_test.go b/pkg/login/ldap_test.go index ef20feb1373..dabafee65a6 100644 --- a/pkg/login/ldap_test.go +++ b/pkg/login/ldap_test.go @@ -13,6 +13,70 @@ import ( ) func TestLdapAuther(t *testing.T) { + Convey("initialBind", t, func() { + Convey("Given bind dn and password configured", func() { + conn := &mockLdapConn{} + var actualUsername, actualPassword string + conn.bindProvider = func(username, password string) error { + actualUsername = username + actualPassword = password + return nil + } + ldapAuther := &ldapAuther{ + conn: conn, + server: &LdapServerConf{ + BindDN: "cn=%s,o=users,dc=grafana,dc=org", + BindPassword: "bindpwd", + }, + } + err := ldapAuther.initialBind("user", "pwd") + So(err, ShouldBeNil) + So(ldapAuther.requireSecondBind, ShouldBeTrue) + So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org") + So(actualPassword, ShouldEqual, "bindpwd") + }) + + Convey("Given bind dn configured", func() { + conn := &mockLdapConn{} + var actualUsername, actualPassword string + conn.bindProvider = func(username, password string) error { + actualUsername = username + actualPassword = password + return nil + } + ldapAuther := &ldapAuther{ + conn: conn, + server: &LdapServerConf{ + BindDN: "cn=%s,o=users,dc=grafana,dc=org", + }, + } + err := ldapAuther.initialBind("user", "pwd") + So(err, ShouldBeNil) + So(ldapAuther.requireSecondBind, ShouldBeFalse) + So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org") + So(actualPassword, ShouldEqual, "pwd") + }) + + Convey("Given empty bind dn and password", func() { + conn := &mockLdapConn{} + unauthenticatedBindWasCalled := false + var actualUsername string + conn.unauthenticatedBindProvider = func(username string) error { + unauthenticatedBindWasCalled = true + actualUsername = username + return nil + } + ldapAuther := &ldapAuther{ + conn: conn, + server: &LdapServerConf{}, + } + err := ldapAuther.initialBind("user", "pwd") + So(err, ShouldBeNil) + So(ldapAuther.requireSecondBind, ShouldBeTrue) + So(unauthenticatedBindWasCalled, ShouldBeTrue) + So(actualUsername, ShouldBeEmpty) + }) + }) Convey("When translating ldap user to grafana user", t, func() { @@ -365,12 +429,26 @@ func TestLdapAuther(t *testing.T) { } type mockLdapConn struct { - result *ldap.SearchResult - searchCalled bool - searchAttributes []string + result *ldap.SearchResult + searchCalled bool + searchAttributes []string + bindProvider func(username, password string) error + unauthenticatedBindProvider func(username string) error } func (c *mockLdapConn) Bind(username, password string) error { + if c.bindProvider != nil { + return c.bindProvider(username, password) + } + + return nil +} + +func (c *mockLdapConn) UnauthenticatedBind(username string) error { + if c.unauthenticatedBindProvider != nil { + return c.unauthenticatedBindProvider(username) + } + return nil } From 46fa09fdadc103c225c4ead5ac595d7ed4d32e1f Mon Sep 17 00:00:00 2001 From: Jon Ferreira Date: Mon, 4 Mar 2019 10:57:22 -0500 Subject: [PATCH 05/40] Add a keybinding that toggles all legends in a dashboard --- public/app/core/components/help/help.ts | 1 + public/app/core/services/keybindingSrv.ts | 5 ++++ .../dashboard/state/DashboardModel.test.ts | 28 +++++++++++++++++++ .../dashboard/state/DashboardModel.ts | 14 ++++++++++ .../features/dashboard/state/PanelModel.ts | 1 + 5 files changed, 49 insertions(+) diff --git a/public/app/core/components/help/help.ts b/public/app/core/components/help/help.ts index 8e8a5ed45d2..7ef54339f49 100644 --- a/public/app/core/components/help/help.ts +++ b/public/app/core/components/help/help.ts @@ -27,6 +27,7 @@ export class HelpCtrl { { keys: ['d', 'C'], description: 'Collapse all rows' }, { keys: ['d', 'a'], description: 'Toggle auto fit panels (experimental feature)' }, { keys: ['mod+o'], description: 'Toggle shared graph crosshair' }, + { keys: ['d', 'l'], description: 'Toggle all panel legends' }, ], 'Focused Panel': [ { keys: ['e'], description: 'Toggle panel edit view' }, diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 7dab7cffd6f..da096f261c6 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -256,6 +256,11 @@ export class KeybindingSrv { } }); + // toggle all panel legends + this.bind('d l', () => { + dashboard.toggleLegendsForAll(); + }); + // collapse all rows this.bind('d shift+c', () => { dashboard.collapseRows(); diff --git a/public/app/features/dashboard/state/DashboardModel.test.ts b/public/app/features/dashboard/state/DashboardModel.test.ts index cd30fc2ecdc..0d18a3b5d12 100644 --- a/public/app/features/dashboard/state/DashboardModel.test.ts +++ b/public/app/features/dashboard/state/DashboardModel.test.ts @@ -635,4 +635,32 @@ describe('DashboardModel', () => { expect(saveModel.templating.list[0].filters[0].value).toBe('server 1'); }); }); + + describe('Given a dashboard with one panel legend on and two off', () => { + let model; + + beforeEach(() => { + const data = { + panels: [ + { id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 2 }, legend: { show: true } }, + { id: 3, type: 'graph', gridPos: { x: 0, y: 4, w: 12, h: 2 }, legend: { show: false } }, + { id: 4, type: 'graph', gridPos: { x: 12, y: 4, w: 12, h: 2 }, legend: { show: false } }, + ], + }; + model = new DashboardModel(data); + }); + + it('toggleLegendsForAll should toggle all legends on on first execution', () => { + model.toggleLegendsForAll(); + const legendsOn = model.panels.filter(panel => panel.legend.show === true); + expect(legendsOn.length).toBe(3); + }); + + it('toggleLegendsForAll should toggle all legends off on second execution', () => { + model.toggleLegendsForAll(); + model.toggleLegendsForAll(); + const legendsOn = model.panels.filter(panel => panel.legend.show === true); + expect(legendsOn.length).toBe(0); + }); + }); }); diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 17af2dbd801..239d0f9d2d8 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -917,4 +917,18 @@ export class DashboardModel { } } } + + toggleLegendsForAll() { + const panels = this.panels.filter(panel => { + return panel.legend !== undefined && panel.legend !== null; + }); + // determine if more panels are displaying legends or not + const onCount = panels.filter(panel => panel.legend.show).length; + const offCount = panels.length - onCount; + const panelLegendsOn = onCount >= offCount; + panels.forEach(panel => { + panel.legend.show = !panelLegendsOn; + panel.render(); + }); + } } diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index c0739b6d8bc..0c3ab44d8e8 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -106,6 +106,7 @@ export class PanelModel { events: Emitter; cacheTimeout?: any; cachedPluginOptions?: any; + legend?: { show: boolean }; constructor(model) { this.events = new Emitter(); From b58261100429b5b1751fc9dbbcb07d9fc606658c Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 5 Mar 2019 16:23:41 +0100 Subject: [PATCH 06/40] docker: update prometheus2 block to version 2.7.2 --- devenv/docker/blocks/prometheus2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devenv/docker/blocks/prometheus2/Dockerfile b/devenv/docker/blocks/prometheus2/Dockerfile index 03edf4c9ee2..c9a2327bd4a 100644 --- a/devenv/docker/blocks/prometheus2/Dockerfile +++ b/devenv/docker/blocks/prometheus2/Dockerfile @@ -1,3 +1,3 @@ -FROM prom/prometheus:v2.2.0 +FROM prom/prometheus:v2.7.2 ADD prometheus.yml /etc/prometheus/ ADD alert.rules /etc/prometheus/ From a3da8dc6739735372397f3f0e7f3c845f62e4f48 Mon Sep 17 00:00:00 2001 From: Jon Ferreira Date: Tue, 5 Mar 2019 15:04:10 -0500 Subject: [PATCH 07/40] Expose onQueryChange to angular plugins --- public/app/features/explore/QueryEditor.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/app/features/explore/QueryEditor.tsx b/public/app/features/explore/QueryEditor.tsx index 1d329f1c56e..d158f6bb9f3 100644 --- a/public/app/features/explore/QueryEditor.tsx +++ b/public/app/features/explore/QueryEditor.tsx @@ -43,6 +43,9 @@ export default class QueryEditor extends PureComponent { this.props.onQueryChange(target); this.props.onExecuteQuery(); }, + onQueryChange: () => { + this.props.onQueryChange(target); + }, events: exploreEvents, panel: { datasource, targets: [target] }, dashboard: {}, From f845a3b841cdd5193c94b24ad29ba3e4933feedc Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 4 Mar 2019 16:57:29 +0100 Subject: [PATCH 08/40] upgrade xorm packages to latest versions --- Gopkg.lock | 312 ++++++- Gopkg.toml | 8 +- pkg/services/sqlstore/alert.go | 4 +- pkg/services/sqlstore/annotation.go | 8 +- pkg/services/sqlstore/dashboard_version.go | 5 +- pkg/services/sqlstore/org_users.go | 2 +- pkg/services/sqlstore/team.go | 2 +- pkg/services/sqlstore/user_test.go | 2 +- vendor/github.com/go-ini/ini/LICENSE | 191 ----- vendor/github.com/go-ini/ini/error.go | 32 - vendor/github.com/go-ini/ini/file.go | 407 ---------- vendor/github.com/go-ini/ini/ini.go | 202 ----- vendor/github.com/go-ini/ini/key.go | 751 ----------------- vendor/github.com/go-ini/ini/parser.go | 477 ----------- vendor/github.com/go-ini/ini/section.go | 257 ------ vendor/github.com/go-ini/ini/struct.go | 512 ------------ vendor/github.com/go-xorm/builder/builder.go | 290 ++++++- .../go-xorm/builder/builder_delete.go | 13 +- .../go-xorm/builder/builder_insert.go | 51 +- .../go-xorm/builder/builder_limit.go | 100 +++ .../go-xorm/builder/builder_select.go | 110 ++- .../go-xorm/builder/builder_union.go | 47 ++ .../go-xorm/builder/builder_update.go | 15 +- vendor/github.com/go-xorm/builder/cond.go | 21 +- .../go-xorm/builder/cond_between.go | 29 +- vendor/github.com/go-xorm/builder/cond_or.go | 4 +- vendor/github.com/go-xorm/builder/error.go | 26 +- vendor/github.com/go-xorm/builder/sql.go | 156 ++++ .../go-xorm/builder/string_builder.go | 119 +++ vendor/github.com/go-xorm/core/cache.go | 14 +- vendor/github.com/go-xorm/core/column.go | 31 +- vendor/github.com/go-xorm/core/converstion.go | 4 + vendor/github.com/go-xorm/core/db.go | 341 +++----- vendor/github.com/go-xorm/core/dialect.go | 11 +- vendor/github.com/go-xorm/core/driver.go | 4 + vendor/github.com/go-xorm/core/error.go | 4 + vendor/github.com/go-xorm/core/filter.go | 10 +- vendor/github.com/go-xorm/core/ilogger.go | 4 + vendor/github.com/go-xorm/core/index.go | 18 +- vendor/github.com/go-xorm/core/mapper.go | 4 + vendor/github.com/go-xorm/core/pk.go | 4 + vendor/github.com/go-xorm/core/rows.go | 68 +- vendor/github.com/go-xorm/core/scan.go | 14 + vendor/github.com/go-xorm/core/stmt.go | 165 ++++ vendor/github.com/go-xorm/core/table.go | 6 +- vendor/github.com/go-xorm/core/tx.go | 153 ++++ vendor/github.com/go-xorm/core/type.go | 54 +- .../github.com/go-xorm/xorm/context_cache.go | 30 + .../github.com/go-xorm/xorm/dialect_mysql.go | 77 ++ .../go-xorm/xorm/dialect_postgres.go | 108 ++- .../go-xorm/xorm/dialect_sqlite3.go | 6 +- vendor/github.com/go-xorm/xorm/engine.go | 209 +++-- vendor/github.com/go-xorm/xorm/engine_cond.go | 5 +- .../github.com/go-xorm/xorm/engine_group.go | 10 + .../github.com/go-xorm/xorm/engine_maxlife.go | 22 - .../github.com/go-xorm/xorm/engine_table.go | 113 +++ vendor/github.com/go-xorm/xorm/error.go | 31 +- vendor/github.com/go-xorm/xorm/helpers.go | 162 ---- vendor/github.com/go-xorm/xorm/interface.go | 15 +- vendor/github.com/go-xorm/xorm/rows.go | 6 +- vendor/github.com/go-xorm/xorm/session.go | 767 +++++++++--------- .../github.com/go-xorm/xorm/session_cols.go | 115 +++ .../github.com/go-xorm/xorm/session_delete.go | 6 +- .../github.com/go-xorm/xorm/session_exist.go | 15 +- .../github.com/go-xorm/xorm/session_find.go | 53 +- vendor/github.com/go-xorm/xorm/session_get.go | 42 +- .../github.com/go-xorm/xorm/session_insert.go | 168 ++-- .../github.com/go-xorm/xorm/session_query.go | 76 +- vendor/github.com/go-xorm/xorm/session_raw.go | 26 +- .../github.com/go-xorm/xorm/session_schema.go | 87 +- vendor/github.com/go-xorm/xorm/session_tx.go | 2 + .../github.com/go-xorm/xorm/session_update.go | 115 ++- vendor/github.com/go-xorm/xorm/statement.go | 286 +++---- vendor/github.com/go-xorm/xorm/transaction.go | 26 + vendor/github.com/go-xorm/xorm/xorm.go | 14 +- vendor/github.com/jmespath/go-jmespath/api.go | 2 +- 76 files changed, 3327 insertions(+), 4329 deletions(-) delete mode 100644 vendor/github.com/go-ini/ini/LICENSE delete mode 100644 vendor/github.com/go-ini/ini/error.go delete mode 100644 vendor/github.com/go-ini/ini/file.go delete mode 100644 vendor/github.com/go-ini/ini/ini.go delete mode 100644 vendor/github.com/go-ini/ini/key.go delete mode 100644 vendor/github.com/go-ini/ini/parser.go delete mode 100644 vendor/github.com/go-ini/ini/section.go delete mode 100644 vendor/github.com/go-ini/ini/struct.go create mode 100644 vendor/github.com/go-xorm/builder/builder_limit.go create mode 100644 vendor/github.com/go-xorm/builder/builder_union.go create mode 100644 vendor/github.com/go-xorm/builder/sql.go create mode 100644 vendor/github.com/go-xorm/builder/string_builder.go create mode 100644 vendor/github.com/go-xorm/core/stmt.go create mode 100644 vendor/github.com/go-xorm/core/tx.go create mode 100644 vendor/github.com/go-xorm/xorm/context_cache.go delete mode 100644 vendor/github.com/go-xorm/xorm/engine_maxlife.go create mode 100644 vendor/github.com/go-xorm/xorm/engine_table.go create mode 100644 vendor/github.com/go-xorm/xorm/transaction.go diff --git a/Gopkg.lock b/Gopkg.lock index dca36f1b3d0..235a315f1e8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,30 +2,39 @@ [[projects]] + digest = "1:f8ad8a53fa865a70efbe215b0ca34735523f50ea39e0efde319ab6fc80089b44" name = "cloud.google.com/go" packages = ["compute/metadata"] + pruneopts = "NUT" revision = "056a55f54a6cc77b440b31a56a5e7c3982d32811" version = "v0.22.0" [[projects]] + digest = "1:167b6f65a6656de568092189ae791253939f076df60231fdd64588ac703892a1" name = "github.com/BurntSushi/toml" packages = ["."] + pruneopts = "NUT" revision = "b26d9c308763d68093482582cea63d69be07a0f0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:7d23e6e1889b8bb4bbb37a564708fdab4497ce232c3a99d66406c975b642a6ff" name = "github.com/Unknwon/com" packages = ["."] + pruneopts = "NUT" revision = "7677a1d7c1137cd3dd5ba7a076d0c898a1ef4520" [[projects]] branch = "master" + digest = "1:1610787cd9726e29d8fecc2a80e43e4fced008a1f560fec6688fc4d946f17835" name = "github.com/VividCortex/mysqlerr" packages = ["."] + pruneopts = "NUT" revision = "6c6b55f8796f578c870b7e19bafb16103bc40095" [[projects]] + digest = "1:ebe102b61c1615d2954734e3cfe1b6b06a5088c25a41055b38661d41ad7b8f27" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -69,399 +78,507 @@ "service/resourcegroupstaggingapi", "service/resourcegroupstaggingapi/resourcegroupstaggingapiiface", "service/s3", - "service/sts" + "service/sts", ] + pruneopts = "NUT" revision = "62936e15518acb527a1a9cb4a39d96d94d0fd9a2" version = "v1.16.15" [[projects]] branch = "master" + digest = "1:79cad073c7be02632d3fa52f62486848b089f560db1e94536de83a408c0f4726" name = "github.com/benbjohnson/clock" packages = ["."] + pruneopts = "NUT" revision = "7dc76406b6d3c05b5f71a86293cbcf3c4ea03b19" [[projects]] branch = "master" + digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "NUT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] branch = "master" + digest = "1:433a2ff0ef4e2f8634614aab3174783c5ff80120b487712db96cc3712f409583" name = "github.com/bmizerany/assert" packages = ["."] + pruneopts = "NUT" revision = "b7ed37b82869576c289d7d97fb2bbd8b64a0cb28" [[projects]] branch = "master" + digest = "1:d8f9145c361920507a4f85ffb7f70b96beaedacba2ce8c00aa663adb08689d3e" name = "github.com/bradfitz/gomemcache" packages = ["memcache"] + pruneopts = "NUT" revision = "1952afaa557dc08e8e0d89eafab110fb501c1a2b" [[projects]] branch = "master" + digest = "1:8ecb89af7dfe3ac401bdb0c9390b134ef96a97e85f732d2b0604fb7b3977839f" name = "github.com/codahale/hdrhistogram" packages = ["."] + pruneopts = "NUT" revision = "3a0bb77429bd3a61596f5e8a3172445844342120" [[projects]] + digest = "1:5dba68a1600a235630e208cb7196b24e58fcbb77bb7a6bec08fcd23f081b0a58" name = "github.com/codegangsta/cli" packages = ["."] + pruneopts = "NUT" revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" version = "v1.20.0" [[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "NUT" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:1b318d2dd6cea8a1a8d8ec70348852303bd3e491df74e8bca6e32eb5a4d06970" name = "github.com/denisenkom/go-mssqldb" packages = [ ".", - "internal/cp" + "internal/cp", ] + pruneopts = "NUT" revision = "270bc3860bb94dd3a3ffd047377d746c5e276726" [[projects]] branch = "master" + digest = "1:2da5f11ad66ff01a27a5c3dba4620b7eee2327be75b32c9ee9f87c9a8001ecbf" name = "github.com/facebookgo/inject" packages = ["."] + pruneopts = "NUT" revision = "cc1aa653e50f6a9893bcaef89e673e5b24e1e97b" [[projects]] branch = "master" + digest = "1:1108df7f658c90db041e0d6174d55be689aaeb0585913b9c3c7aab51a3a6b2b1" name = "github.com/facebookgo/structtag" packages = ["."] + pruneopts = "NUT" revision = "217e25fb96916cc60332e399c9aa63f5c422ceed" [[projects]] + digest = "1:ade392a843b2035effb4b4a2efa2c3bab3eb29b992e98bacf9c898b0ecb54e45" name = "github.com/fatih/color" packages = ["."] + pruneopts = "NUT" revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4" version = "v1.7.0" -[[projects]] - name = "github.com/go-ini/ini" - packages = ["."] - revision = "6529cf7c58879c08d927016dde4477f18a0634cb" - version = "v1.36.0" - [[projects]] branch = "master" + digest = "1:682a0aca743a1a4a36697f3d7f86c0ed403c4e3a780db9935f633242855eac9c" name = "github.com/go-macaron/binding" packages = ["."] + pruneopts = "NUT" revision = "ac54ee249c27dca7e76fad851a4a04b73bd1b183" [[projects]] branch = "master" + digest = "1:6326b27f8e0c8e135c8674ddbc619fae879664ac832e8e6fa6a23ce0d279ed4d" name = "github.com/go-macaron/gzip" packages = ["."] + pruneopts = "NUT" revision = "cad1c6580a07c56f5f6bc52d66002a05985c5854" [[projects]] branch = "master" + digest = "1:fb8711b648d1ff03104fc1d9593a13cb1d5120be7ba2b01641c14ccae286a9e3" name = "github.com/go-macaron/inject" packages = ["."] + pruneopts = "NUT" revision = "d8a0b8677191f4380287cfebd08e462217bac7ad" [[projects]] branch = "master" + digest = "1:21577aafe885f088e8086a3415f154c63c0b7ce956a6994df2ac5776bc01b7e3" name = "github.com/go-macaron/session" packages = [ ".", "memcache", "postgres", - "redis" + "redis", ] + pruneopts = "NUT" revision = "068d408f9c54c7fa7fcc5e2bdd3241ab21280c9e" [[projects]] + digest = "1:fddd4bada6100d6fc49a9f32f18ba5718db45a58e4b00aa6377e1cfbf06af34f" name = "github.com/go-sql-driver/mysql" packages = ["."] + pruneopts = "NUT" revision = "2cc627ac8defc45d65066ae98f898166f580f9a4" [[projects]] + digest = "1:a1efdbc2762667c8a41cbf02b19a0549c846bf2c1d08cad4f445e3344089f1f0" name = "github.com/go-stack/stack" packages = ["."] + pruneopts = "NUT" revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" version = "v1.7.0" [[projects]] + digest = "1:06d21295033f211588d0ad7ff391cc1b27e72b60cb6d4b7db0d70cffae4cf228" name = "github.com/go-xorm/builder" packages = ["."] - revision = "bad0a612f0d6277b953910822ab5dfb30dd18237" - version = "v0.2.0" + pruneopts = "NUT" + revision = "1d658d7596c25394aab557ef5b50ef35bf706384" + version = "v0.3.4" [[projects]] + digest = "1:b26928aab0fff92592e8728c5bc9d6e404fa2017d6a8e841ae5e60a42237f6fc" name = "github.com/go-xorm/core" packages = ["."] - revision = "da1adaf7a28ca792961721a34e6e04945200c890" - version = "v0.5.7" + pruneopts = "NUT" + revision = "ccc80c1adf1f6172bbc548877f50a1163041a40a" + version = "v0.6.2" [[projects]] + digest = "1:407316703b32d68ccf5d39bdae57d411b6954e253e07d0fff0988a3f39861f2f" name = "github.com/go-xorm/xorm" packages = ["."] - revision = "1933dd69e294c0a26c0266637067f24dbb25770c" - version = "v0.6.4" + pruneopts = "NUT" + revision = "1f39c590c64924f358c0d89016ac9b2bb84e9125" + version = "v0.7.1" [[projects]] branch = "master" + digest = "1:ffbb19fb66f140b5ea059428d1f84246a055d1bc3d9456c1e5c3d143611f03d0" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "NUT" revision = "927b65914520a8b7d44f5c9057611cfec6b2e2d0" [[projects]] branch = "master" + digest = "1:f14d1b50e0075fb00177f12a96dd7addf93d1e2883c25befd17285b779549795" name = "github.com/gopherjs/gopherjs" packages = ["js"] + pruneopts = "NUT" revision = "8dffc02ea1cb8398bb73f30424697c60fcf8d4c5" [[projects]] + digest = "1:3b708ebf63bfa9ba3313bedb8526bc0bb284e51474e65e958481476a9d4a12aa" name = "github.com/gorilla/websocket" packages = ["."] + pruneopts = "NUT" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] + digest = "1:4e771d1c6e15ca4516ad971c34205c822b5cff2747179679d7b321e4e1bfe431" name = "github.com/gosimple/slug" packages = ["."] + pruneopts = "NUT" revision = "e9f42fa127660e552d0ad2b589868d403a9be7c6" version = "v1.1.1" [[projects]] branch = "master" + digest = "1:08e53c69cd267ef7d71eeae5d953153d0d2bc1b8e0b498731fe9acaead7001b6" name = "github.com/grafana/grafana-plugin-model" packages = [ "go/datasource", - "go/renderer" + "go/renderer", ] + pruneopts = "NUT" revision = "84176c64269d8060f99e750ee8aba6f062753336" [[projects]] branch = "master" + digest = "1:58ba5285227b0f635652cd4aa82c4cfd00b590191eadd823462f0c9f64e3ae07" name = "github.com/hashicorp/go-hclog" packages = ["."] + pruneopts = "NUT" revision = "69ff559dc25f3b435631604f573a5fa1efdb6433" [[projects]] + digest = "1:532090ffc3b05a7e4c0229dd2698d79149f2e0683df993224a8b202f607fb605" name = "github.com/hashicorp/go-plugin" packages = ["."] + pruneopts = "NUT" revision = "e8d22c780116115ae5624720c9af0c97afe4f551" [[projects]] branch = "master" + digest = "1:8925116d1edcd85fc0c014e1aa69ce12892489b48ee633a605c46d893b8c151f" name = "github.com/hashicorp/go-version" packages = ["."] + pruneopts = "NUT" revision = "23480c0665776210b5fbbac6eaaee40e3e6a96b7" [[projects]] branch = "master" + digest = "1:8deb0c5545c824dfeb0ac77ab8eb67a3d541eab76df5c85ce93064ef02d44cd0" name = "github.com/hashicorp/yamux" packages = ["."] + pruneopts = "NUT" revision = "7221087c3d281fda5f794e28c2ea4c6e4d5c4558" [[projects]] + digest = "1:efbe016b6d198cf44f1db0ed2fbdf1b36ebf1f6956cc9b76d6affa96f022d368" name = "github.com/inconshreveable/log15" packages = ["."] + pruneopts = "NUT" revision = "0decfc6c20d9ca0ad143b0e89dcaa20f810b4fb3" version = "v2.13" [[projects]] + digest = "1:1f2aebae7e7c856562355ec0198d8ca2fa222fb05e5b1b66632a1fce39631885" name = "github.com/jmespath/go-jmespath" packages = ["."] - revision = "0b12d6b5" + pruneopts = "NUT" + revision = "c2b33e84" [[projects]] + digest = "1:6ddab442e52381bab82fb6c07ef3f4b565ff7ec4b8fae96d8dd4b8573a460597" name = "github.com/jtolds/gls" packages = ["."] + pruneopts = "NUT" revision = "77f18212c9c7edc9bd6a33d383a7b545ce62f064" version = "v4.2.1" [[projects]] + digest = "1:1da1796a71eb70f1e3e085984d044f67840bb0326816ec8276231aa87b1b9fc3" name = "github.com/klauspost/compress" packages = [ "flate", - "gzip" + "gzip", ] + pruneopts = "NUT" revision = "6c8db69c4b49dd4df1fff66996cf556176d0b9bf" version = "v1.2.1" [[projects]] + digest = "1:5e55a8699c9ff7aba1e4c8952aeda209685d88d4cb63a8766c338e333b8e65d6" name = "github.com/klauspost/cpuid" packages = ["."] + pruneopts = "NUT" revision = "ae7887de9fa5d2db4eaa8174a7eff2c1ac00f2da" version = "v1.1" [[projects]] + digest = "1:b95da1293525625ef6f07be79d537b9bf2ecd7901efcf9a92193edafbd55b9ef" name = "github.com/klauspost/crc32" packages = ["."] + pruneopts = "NUT" revision = "cb6bfca970f6908083f26f39a79009d608efd5cd" version = "v1.1" [[projects]] + digest = "1:7b21c7fc5551b46d1308b4ffa9e9e49b66c7a8b0ba88c0130474b0e7a20d859f" name = "github.com/kr/pretty" packages = ["."] + pruneopts = "NUT" revision = "73f6ac0b30a98e433b289500d779f50c1a6f0712" version = "v0.1.0" [[projects]] + digest = "1:c3a7836b5904db0f8b609595b619916a6831cb35b8b714aec39f96d00c6155d8" name = "github.com/kr/text" packages = ["."] + pruneopts = "NUT" revision = "e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f" version = "v0.1.0" [[projects]] branch = "master" + digest = "1:7a1e592f0349d56fac8ce47f28469e4e7f4ce637cb26f40c88da9dff25db1c98" name = "github.com/lib/pq" packages = [ ".", - "oid" + "oid", ] + pruneopts = "NUT" revision = "d34b9ff171c21ad295489235aec8b6626023cd04" [[projects]] + digest = "1:08c231ec84231a7e23d67e4b58f975e1423695a32467a362ee55a803f9de8061" name = "github.com/mattn/go-colorable" packages = ["."] + pruneopts = "NUT" revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" version = "v0.0.9" [[projects]] + digest = "1:bc4f7eec3b7be8c6cb1f0af6c1e3333d5bb71072951aaaae2f05067b0803f287" name = "github.com/mattn/go-isatty" packages = ["."] + pruneopts = "NUT" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" [[projects]] + digest = "1:536979f1c56397dbf91c2785159b37dec37e35d3bffa3cd1cfe66d25f51f8088" name = "github.com/mattn/go-sqlite3" packages = ["."] + pruneopts = "NUT" revision = "323a32be5a2421b8c7087225079c6c900ec397cd" version = "v1.7.0" [[projects]] + digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "NUT" revision = "3247c84500bff8d9fb6d579d800f20b3e091582c" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:18b773b92ac82a451c1276bd2776c1e55ce057ee202691ab33c8d6690efcc048" name = "github.com/mitchellh/go-testing-interface" packages = ["."] + pruneopts = "NUT" revision = "a61a99592b77c9ba629d254a693acffaeb4b7e28" [[projects]] + digest = "1:3b517122f3aad1ecce45a630ea912b3092b4729f25532a911d0cb2935a1f9352" name = "github.com/oklog/run" packages = ["."] + pruneopts = "NUT" revision = "4dadeb3030eda0273a12382bb2348ffc7c9d1a39" version = "v1.0.0" [[projects]] + digest = "1:7da29c22bcc5c2ffb308324377dc00b5084650348c2799e573ed226d8cc9faf0" name = "github.com/opentracing/opentracing-go" packages = [ ".", "ext", - "log" + "log", ] + pruneopts = "NUT" revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" version = "v1.0.2" [[projects]] + digest = "1:748946761cf99c8b73cef5a3c0ee3e040859dd713a20cece0d0e0dc04e6ceca7" name = "github.com/patrickmn/go-cache" packages = ["."] + pruneopts = "NUT" revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0" version = "v2.1.0" [[projects]] + digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "NUT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:4759bed95e3a52febc18c071db28790a5c6e9e106ee201a37add6f6a056f8f9c" name = "github.com/prometheus/client_golang" packages = [ "api", "api/prometheus/v1", "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "NUT" revision = "967789050ba94deca04a5e84cce8ad472ce313c1" version = "v0.9.0-pre1" [[projects]] branch = "master" + digest = "1:32d10bdfa8f09ecf13598324dba86ab891f11db3c538b6a34d1c3b5b99d7c36b" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "NUT" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:768b555b86742de2f28beb37f1dedce9a75f91f871d75b5717c96399c1a78c08" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model" + "model", ] + pruneopts = "NUT" revision = "d811d2e9bf898806ecfb6ef6296774b13ffc314c" [[projects]] branch = "master" + digest = "1:c4a213a8d73fbb0b13f717ba7996116602ef18ecb42b91d77405877914cb0349" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "NUT" revision = "8b1c2da0d56deffdbb9e48d4414b4e674bd8083e" [[projects]] branch = "master" + digest = "1:16e2136a67ec44aa2d1d6b0fd65394b3c4a8b2a1b6730c77967f7b7b06b179b2" name = "github.com/rainycape/unidecode" packages = ["."] + pruneopts = "NUT" revision = "cb7f23ec59bec0d61b19c56cd88cee3d0cc1870c" [[projects]] + digest = "1:d917313f309bda80d27274d53985bc65651f81a5b66b820749ac7f8ef061fd04" name = "github.com/sergi/go-diff" packages = ["diffmatchpatch"] + pruneopts = "NUT" revision = "1744e2970ca51c86172c8190fadad617561ed6e7" version = "v1.0.0" [[projects]] + digest = "1:1f0b284a6858827de4c27c66b49b2b25df3e16b031c2b57b7892273131e7dd2b" name = "github.com/smartystreets/assertions" packages = [ ".", "internal/go-render/render", - "internal/oglematchers" + "internal/oglematchers", ] + pruneopts = "NUT" revision = "7678a5452ebea5b7090a6b163f844c133f523da2" version = "1.8.3" [[projects]] + digest = "1:7efd0b2309cdd6468029fa30c808c50a820c9344df07e1a4bbdaf18f282907aa" name = "github.com/smartystreets/goconvey" packages = [ "convey", "convey/gotest", - "convey/reporting" + "convey/reporting", ] + pruneopts = "NUT" revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" version = "1.6.3" [[projects]] branch = "master" + digest = "1:a66add8dd963bfc72649017c1b321198f596cb4958cb1a11ff91a1be8691020b" name = "github.com/teris-io/shortid" packages = ["."] + pruneopts = "NUT" revision = "771a37caa5cf0c81f585d7b6df4dfc77e0615b5c" [[projects]] + digest = "1:3d48c38e0eca8c66df62379c5ae7a83fb5cd839b94f241354c07ba077da7bc45" name = "github.com/uber/jaeger-client-go" packages = [ ".", @@ -479,45 +596,55 @@ "thrift-gen/jaeger", "thrift-gen/sampling", "thrift-gen/zipkincore", - "utils" + "utils", ] + pruneopts = "NUT" revision = "b043381d944715b469fd6b37addfd30145ca1758" version = "v2.14.0" [[projects]] + digest = "1:0f09db8429e19d57c8346ad76fbbc679341fa86073d3b8fb5ac919f0357d8f4c" name = "github.com/uber/jaeger-lib" packages = ["metrics"] + pruneopts = "NUT" revision = "ed3a127ec5fef7ae9ea95b01b542c47fbd999ce5" version = "v1.5.0" [[projects]] + digest = "1:4c7d12ad3ef47bb03892a52e2609dc9a9cff93136ca9c7d31c00b79fcbc23c7b" name = "github.com/yudai/gojsondiff" packages = [ ".", - "formatter" + "formatter", ] + pruneopts = "NUT" revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6" version = "1.0.0" [[projects]] branch = "master" + digest = "1:e50cbf8eba568d59b71e08c22c2a77809ed4646ae06ef4abb32b3d3d3fdb1a77" name = "github.com/yudai/golcs" packages = ["."] + pruneopts = "NUT" revision = "ecda9a501e8220fae3b4b600c3db4b0ba22cfc68" [[projects]] branch = "master" + digest = "1:758f363e0dff33cf00b234be2efb12f919d79b42d5ae3909ff9eb69ef2c3cca5" name = "golang.org/x/crypto" packages = [ "ed25519", "ed25519/internal/edwards25519", "md4", - "pbkdf2" + "pbkdf2", ] + pruneopts = "NUT" revision = "1a580b3eff7814fc9b40602fd35256c63b50f491" [[projects]] branch = "master" + digest = "1:0b3fee9c4472022a0982ee0d81e08b3cc3e595f50befd7a4b358b48540d9d8c5" name = "golang.org/x/net" packages = [ "context", @@ -527,35 +654,43 @@ "http2/hpack", "idna", "internal/timeseries", - "trace" + "trace", ] + pruneopts = "NUT" revision = "2491c5de3490fced2f6cff376127c667efeed857" [[projects]] branch = "master" + digest = "1:46bd4e66bfce5e77f08fc2e8dcacc3676e679241ce83d9c150ff0397d686dd44" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] + pruneopts = "NUT" revision = "cdc340f7c179dbbfa4afd43b7614e8fcadde4269" [[projects]] branch = "master" + digest = "1:39ebcc2b11457b703ae9ee2e8cca0f68df21969c6102cb3b705f76cca0ea0239" name = "golang.org/x/sync" packages = ["errgroup"] + pruneopts = "NUT" revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" [[projects]] branch = "master" + digest = "1:ec21c5bf0572488865b93e30ffd9132afbf85bec0b20c2d6cbcf349cf2031ed5" name = "golang.org/x/sys" packages = ["unix"] + pruneopts = "NUT" revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b" [[projects]] + digest = "1:e7071ed636b5422cc51c0e3a6cebc229d6c9fffc528814b519a980641422d619" name = "golang.org/x/text" packages = [ "collate", @@ -571,12 +706,14 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "NUT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + digest = "1:dbd5568923513ee74aa626d027e2a8a352cf8f35df41d19f4e34491d1858c38b" name = "google.golang.org/appengine" packages = [ ".", @@ -589,18 +726,22 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch" + "urlfetch", ] + pruneopts = "NUT" revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:3c24554c312721e98fa6b76403e7100cf974eb46b1255ea7fc6471db9a9ce498" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "NUT" revision = "7bb2a897381c9c5ab2aeb8614f758d7766af68ff" [[projects]] + digest = "1:840b77b6eb539b830bb760b6e30b688ed2ff484bd83466fce2395835ed9367fe" name = "google.golang.org/grpc" packages = [ ".", @@ -627,78 +768,177 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "NUT" revision = "1e2570b1b19ade82d8dbb31bba4e65e9f9ef5b34" version = "v1.11.1" [[projects]] branch = "v3" + digest = "1:1244a9b3856f70d5ffb74bbfd780fc9d47f93f2049fa265c6fb602878f507bf8" name = "gopkg.in/alexcesaro/quotedprintable.v3" packages = ["."] + pruneopts = "NUT" revision = "2caba252f4dc53eaf6b553000885530023f54623" [[projects]] + digest = "1:aea6e9483c167cc6fdf1274c442558c5dda8fd3373372be04d98c79100868da1" name = "gopkg.in/asn1-ber.v1" packages = ["."] + pruneopts = "NUT" revision = "379148ca0225df7a432012b8df0355c2a2063ac0" version = "v1.2" [[projects]] + digest = "1:24bfc2e8bf971485cb5ba0f0e5b08a1b806cca5828134df76b32d1ea50f2ab49" name = "gopkg.in/bufio.v1" packages = ["."] + pruneopts = "NUT" revision = "567b2bfa514e796916c4747494d6ff5132a1dfce" version = "v1" [[projects]] + digest = "1:e05711632e1515319b014e8fe4cbe1d30ab024c473403f60cf0fdeb4c586a474" name = "gopkg.in/ini.v1" packages = ["."] + pruneopts = "NUT" revision = "6529cf7c58879c08d927016dde4477f18a0634cb" version = "v1.36.0" [[projects]] + digest = "1:c847b7fea4c7e6db5281a37dffc4620cb78c1227403a79e5aa290db517657ac1" name = "gopkg.in/ldap.v3" packages = ["."] + pruneopts = "NUT" revision = "5c2c0f997205c29de14cb6c35996370c2c5dfab1" version = "v3" [[projects]] + digest = "1:3b0cf3a465fd07f76e5fc1a9d0783c662dac0de9fc73d713ebe162768fd87b5f" name = "gopkg.in/macaron.v1" packages = ["."] + pruneopts = "NUT" revision = "c1be95e6d21e769e44e1ec33cec9da5837861c10" version = "v1.3.1" [[projects]] branch = "v2" + digest = "1:d52332f9e9f2c6343652e13aa3fd40cfd03353520c9a48d90f21215d3012d50f" name = "gopkg.in/mail.v2" packages = ["."] + pruneopts = "NUT" revision = "5bc5c8bb07bd8d2803831fbaf8cbd630fcde2c68" [[projects]] + digest = "1:00126f697efdcab42f07c89ac8bf0095fb2328aef6464e070055154088cea859" name = "gopkg.in/redis.v2" packages = ["."] + pruneopts = "NUT" revision = "e6179049628164864e6e84e973cfb56335748dea" version = "v2.3.2" [[projects]] + digest = "1:a50fabe7a46692dc7c656310add3d517abe7914df02afd151ef84da884605dc8" name = "gopkg.in/square/go-jose.v2" packages = [ ".", "cipher", - "json" + "json", ] + pruneopts = "NUT" revision = "ef984e69dd356202fd4e4910d4d9c24468bdf0b8" version = "v2.1.9" [[projects]] branch = "v2" + digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "NUT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "88f0eb826b9c154ba46ea3bb64767707d86db75449ec75199eb2b8cf2b337fd4" + input-imports = [ + "github.com/BurntSushi/toml", + "github.com/Unknwon/com", + "github.com/VividCortex/mysqlerr", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/awserr", + "github.com/aws/aws-sdk-go/aws/awsutil", + "github.com/aws/aws-sdk-go/aws/credentials", + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", + "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds", + "github.com/aws/aws-sdk-go/aws/defaults", + "github.com/aws/aws-sdk-go/aws/ec2metadata", + "github.com/aws/aws-sdk-go/aws/endpoints", + "github.com/aws/aws-sdk-go/aws/request", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/cloudwatch", + "github.com/aws/aws-sdk-go/service/ec2", + "github.com/aws/aws-sdk-go/service/ec2/ec2iface", + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi", + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface", + "github.com/aws/aws-sdk-go/service/s3", + "github.com/aws/aws-sdk-go/service/sts", + "github.com/benbjohnson/clock", + "github.com/bmizerany/assert", + "github.com/codegangsta/cli", + "github.com/davecgh/go-spew/spew", + "github.com/denisenkom/go-mssqldb", + "github.com/facebookgo/inject", + "github.com/fatih/color", + "github.com/go-macaron/binding", + "github.com/go-macaron/gzip", + "github.com/go-macaron/session", + "github.com/go-macaron/session/memcache", + "github.com/go-macaron/session/postgres", + "github.com/go-macaron/session/redis", + "github.com/go-sql-driver/mysql", + "github.com/go-stack/stack", + "github.com/go-xorm/core", + "github.com/go-xorm/xorm", + "github.com/gorilla/websocket", + "github.com/gosimple/slug", + "github.com/grafana/grafana-plugin-model/go/datasource", + "github.com/grafana/grafana-plugin-model/go/renderer", + "github.com/hashicorp/go-hclog", + "github.com/hashicorp/go-plugin", + "github.com/hashicorp/go-version", + "github.com/inconshreveable/log15", + "github.com/lib/pq", + "github.com/mattn/go-isatty", + "github.com/mattn/go-sqlite3", + "github.com/opentracing/opentracing-go", + "github.com/opentracing/opentracing-go/ext", + "github.com/opentracing/opentracing-go/log", + "github.com/patrickmn/go-cache", + "github.com/pkg/errors", + "github.com/prometheus/client_golang/api", + "github.com/prometheus/client_golang/api/prometheus/v1", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/prometheus/client_model/go", + "github.com/prometheus/common/expfmt", + "github.com/prometheus/common/model", + "github.com/smartystreets/goconvey/convey", + "github.com/teris-io/shortid", + "github.com/uber/jaeger-client-go/config", + "github.com/yudai/gojsondiff", + "github.com/yudai/gojsondiff/formatter", + "golang.org/x/net/context/ctxhttp", + "golang.org/x/oauth2", + "golang.org/x/oauth2/google", + "golang.org/x/oauth2/jwt", + "golang.org/x/sync/errgroup", + "gopkg.in/ini.v1", + "gopkg.in/ldap.v3", + "gopkg.in/macaron.v1", + "gopkg.in/mail.v2", + "gopkg.in/square/go-jose.v2", + "gopkg.in/yaml.v2", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 83e6890b0f4..d1bc0f55bae 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -81,11 +81,15 @@ ignored = [ [[constraint]] name = "github.com/go-xorm/core" - version = "=0.5.7" + version = "=0.6.2" + +[[override]] + name = "github.com/go-xorm/builder" + version = "=0.3.4" [[constraint]] name = "github.com/go-xorm/xorm" - version = "=0.6.4" + version = "=0.7.1" [[constraint]] name = "github.com/gorilla/websocket" diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 62ab348664f..7796cfd0dc7 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -309,7 +309,9 @@ func PauseAlert(cmd *m.PauseAlertCommand) error { params = append(params, v) } - res, err := sess.Exec(buffer.String(), params...) + sqlOrArgs := append([]interface{}{buffer.String()}, params...) + + res, err := sess.Exec(sqlOrArgs...) if err != nil { return err } diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index 274481baeca..a285b231aae 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -258,11 +258,15 @@ func (r *SqlAnnotationRepo) Delete(params *annotations.DeleteParams) error { queryParams = []interface{}{params.DashboardId, params.PanelId, params.OrgId} } - if _, err := sess.Exec(annoTagSql, queryParams...); err != nil { + sqlOrArgs := append([]interface{}{annoTagSql}, queryParams...) + + if _, err := sess.Exec(sqlOrArgs...); err != nil { return err } - if _, err := sess.Exec(sql, queryParams...); err != nil { + sqlOrArgs = append([]interface{}{sql}, queryParams...) + + if _, err := sess.Exec(sqlOrArgs...); err != nil { return err } diff --git a/pkg/services/sqlstore/dashboard_version.go b/pkg/services/sqlstore/dashboard_version.go index 1f2850b2021..7619e2ab269 100644 --- a/pkg/services/sqlstore/dashboard_version.go +++ b/pkg/services/sqlstore/dashboard_version.go @@ -51,7 +51,7 @@ func GetDashboardVersions(query *m.GetDashboardVersionsQuery) error { dashboard_version.message, dashboard_version.data,`+ dialect.Quote("user")+`.login as created_by`). - Join("LEFT", "user", `dashboard_version.created_by = `+dialect.Quote("user")+`.id`). + Join("LEFT", dialect.Quote("user"), `dashboard_version.created_by = `+dialect.Quote("user")+`.id`). Join("LEFT", "dashboard", `dashboard.id = dashboard_version.dashboard_id`). Where("dashboard_version.dashboard_id=? AND dashboard.org_id=?", query.DashboardId, query.OrgId). OrderBy("dashboard_version.version DESC"). @@ -102,7 +102,8 @@ func DeleteExpiredVersions(cmd *m.DeleteExpiredVersionsCommand) error { if len(versionIdsToDelete) > 0 { deleteExpiredSql := `DELETE FROM dashboard_version WHERE id IN (?` + strings.Repeat(",?", len(versionIdsToDelete)-1) + `)` - expiredResponse, err := sess.Exec(deleteExpiredSql, versionIdsToDelete...) + sqlOrArgs := append([]interface{}{deleteExpiredSql}, versionIdsToDelete...) + expiredResponse, err := sess.Exec(sqlOrArgs...) if err != nil { return err } diff --git a/pkg/services/sqlstore/org_users.go b/pkg/services/sqlstore/org_users.go index abbc320020e..897ef0ea1ad 100644 --- a/pkg/services/sqlstore/org_users.go +++ b/pkg/services/sqlstore/org_users.go @@ -98,7 +98,7 @@ func GetOrgUsers(query *m.GetOrgUsersQuery) error { query.Result = make([]*m.OrgUserDTO, 0) sess := x.Table("org_user") - sess.Join("INNER", "user", fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Join("INNER", x.Dialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) whereConditions := make([]string, 0) whereParams := make([]interface{}, 0) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index a3010a086e5..83593e6f2d7 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -280,7 +280,7 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { func GetTeamMembers(query *m.GetTeamMembersQuery) error { query.Result = make([]*m.TeamMemberDTO, 0) sess := x.Table("team_member") - sess.Join("INNER", "user", fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Join("INNER", x.Dialect().Quote("user"), fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user"))) if query.OrgId != 0 { sess.Where("team_member.org_id=?", query.OrgId) } diff --git a/pkg/services/sqlstore/user_test.go b/pkg/services/sqlstore/user_test.go index 526c17a8256..84640687ed9 100644 --- a/pkg/services/sqlstore/user_test.go +++ b/pkg/services/sqlstore/user_test.go @@ -208,7 +208,7 @@ func TestUserDataAccess(t *testing.T) { func GetOrgUsersForTest(query *m.GetOrgUsersQuery) error { query.Result = make([]*m.OrgUserDTO, 0) sess := x.Table("org_user") - sess.Join("LEFT ", "user", fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Join("LEFT ", x.Dialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user"))) sess.Where("org_user.org_id=?", query.OrgId) sess.Cols("org_user.org_id", "org_user.user_id", "user.email", "user.login", "org_user.role") diff --git a/vendor/github.com/go-ini/ini/LICENSE b/vendor/github.com/go-ini/ini/LICENSE deleted file mode 100644 index d361bbcdf5c..00000000000 --- a/vendor/github.com/go-ini/ini/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright 2014 Unknwon - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/go-ini/ini/error.go b/vendor/github.com/go-ini/ini/error.go deleted file mode 100644 index 80afe743158..00000000000 --- a/vendor/github.com/go-ini/ini/error.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "fmt" -) - -type ErrDelimiterNotFound struct { - Line string -} - -func IsErrDelimiterNotFound(err error) bool { - _, ok := err.(ErrDelimiterNotFound) - return ok -} - -func (err ErrDelimiterNotFound) Error() string { - return fmt.Sprintf("key-value delimiter not found: %s", err.Line) -} diff --git a/vendor/github.com/go-ini/ini/file.go b/vendor/github.com/go-ini/ini/file.go deleted file mode 100644 index d7982c32357..00000000000 --- a/vendor/github.com/go-ini/ini/file.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2017 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "sync" -) - -// File represents a combination of a or more INI file(s) in memory. -type File struct { - options LoadOptions - dataSources []dataSource - - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - lock sync.RWMutex - - // To keep data in order. - sectionList []string - // Actual data is stored here. - sections map[string]*Section - - NameMapper - ValueMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource, opts LoadOptions) *File { - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), - options: opts, - } -} - -// Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } else if f.options.Insensitive && name != DEFAULT_SECTION { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if inSlice(name, f.sectionList) { - return f.sections[name], nil - } - - f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil -} - -// NewRawSection creates a new section with an unparseable body. -func (f *File) NewRawSection(name, body string) (*Section, error) { - section, err := f.NewSection(name) - if err != nil { - return nil, err - } - - section.isRawSection = true - section.rawBody = body - return section, nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - if len(name) == 0 { - name = DEFAULT_SECTION - } - if f.options.Insensitive { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("section '%s' does not exist", name) - } - return sec, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - // Note: It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// Section returns list of Section. -func (f *File) Sections() []*Section { - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sections := make([]*Section, len(f.sectionList)) - for i, name := range f.sectionList { - sections[i] = f.sections[name] - } - return sections -} - -// ChildSections returns a list of child sections of given section name. -func (f *File) ChildSections(name string) []*Section { - return f.Section(name).ChildSections() -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section. -func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if len(name) == 0 { - name = DEFAULT_SECTION - } - - for i, s := range f.sectionList { - if s == name { - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return - } - } -} - -func (f *File) reload(s dataSource) error { - r, err := s.ReadCloser() - if err != nil { - return err - } - defer r.Close() - - return f.parse(r) -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() (err error) { - for _, s := range f.dataSources { - if err = f.reload(s); err != nil { - // In loose mode, we create an empty default section for nonexistent files. - if os.IsNotExist(err) && f.options.Loose { - f.parse(bytes.NewBuffer(nil)) - continue - } - return err - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { - equalSign := "=" - if PrettyFormat || PrettyEqual { - equalSign = " = " - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - for i, sname := range f.sectionList { - sec := f.Section(sname) - if len(sec.Comment) > 0 { - if sec.Comment[0] != '#' && sec.Comment[0] != ';' { - sec.Comment = "; " + sec.Comment - } else { - sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:]) - } - if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil { - return nil, err - } - } - - if i > 0 || DefaultHeader { - if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return nil, err - } - } else { - // Write nothing if default section is empty - if len(sec.keyList) == 0 { - continue - } - } - - if sec.isRawSection { - if _, err := buf.WriteString(sec.rawBody); err != nil { - return nil, err - } - - if PrettySection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - continue - } - - // Count and generate alignment length and buffer spaces using the - // longest key. Keys may be modifed if they contain certain characters so - // we need to take that into account in our calculation. - alignLength := 0 - if PrettyFormat { - for _, kname := range sec.keyList { - keyLength := len(kname) - // First case will surround key by ` and second by """ - if strings.ContainsAny(kname, "\"=:") { - keyLength += 2 - } else if strings.Contains(kname, "`") { - keyLength += 6 - } - - if keyLength > alignLength { - alignLength = keyLength - } - } - } - alignSpaces := bytes.Repeat([]byte(" "), alignLength) - - KEY_LIST: - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - if key.Comment[0] != '#' && key.Comment[0] != ';' { - key.Comment = "; " + key.Comment - } else { - key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:]) - } - - // Support multiline comments - key.Comment = strings.Replace(key.Comment, "\n", "\n; ", -1) - - if _, err := buf.WriteString(key.Comment + LineBreak); err != nil { - return nil, err - } - } - - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - - switch { - case key.isAutoIncrement: - kname = "-" - case strings.ContainsAny(kname, "\"=:"): - kname = "`" + kname + "`" - case strings.Contains(kname, "`"): - kname = `"""` + kname + `"""` - } - - for _, val := range key.ValueWithShadows() { - if _, err := buf.WriteString(kname); err != nil { - return nil, err - } - - if key.isBooleanType { - if kname != sec.keyList[len(sec.keyList)-1] { - buf.WriteString(LineBreak) - } - continue KEY_LIST - } - - // Write out alignment spaces before "=" sign - if PrettyFormat { - buf.Write(alignSpaces[:alignLength-len(kname)]) - } - - // In case key value contains "\n", "`", "\"", "#" or ";" - if strings.ContainsAny(val, "\n`") { - val = `"""` + val + `"""` - } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { - val = "`" + val + "`" - } - if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { - return nil, err - } - } - - for _, val := range key.nestedValues { - if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil { - return nil, err - } - } - } - - if PrettySection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - } - - return buf, nil -} - -// WriteToIndent writes content into io.Writer with given indention. -// If PrettyFormat has been set to be true, -// it will align "=" sign with spaces under each section. -func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { - buf, err := f.writeToBuffer(indent) - if err != nil { - return 0, err - } - return buf.WriteTo(w) -} - -// WriteTo writes file content into io.Writer. -func (f *File) WriteTo(w io.Writer) (int64, error) { - return f.WriteToIndent(w, "") -} - -// SaveToIndent writes content to file system with given value indention. -func (f *File) SaveToIndent(filename, indent string) error { - // Note: Because we are truncating with os.Create, - // so it's safer to save to a temporary file location and rename afte done. - buf, err := f.writeToBuffer(indent) - if err != nil { - return err - } - - return ioutil.WriteFile(filename, buf.Bytes(), 0666) -} - -// SaveTo writes content to file system. -func (f *File) SaveTo(filename string) error { - return f.SaveToIndent(filename, "") -} diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go deleted file mode 100644 index d983532299e..00000000000 --- a/vendor/github.com/go-ini/ini/ini.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -// Package ini provides INI file read and write functionality in Go. -package ini - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "regexp" - "runtime" -) - -const ( - // Name for default section. You can use this constant or the string literal. - // In most of cases, an empty string is all you need to access the section. - DEFAULT_SECTION = "DEFAULT" - - // Maximum allowed depth when recursively substituing variable names. - _DEPTH_VALUES = 99 - _VERSION = "1.36.0" -) - -// Version returns current package version literal. -func Version() string { - return _VERSION -} - -var ( - // Delimiter to determine or compose a new line. - // This variable will be changed to "\r\n" automatically on Windows - // at package init time. - LineBreak = "\n" - - // Variable regexp pattern: %(variable)s - varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) - - // Indicate whether to align "=" sign with spaces to produce pretty output - // or reduce all possible spaces for compact format. - PrettyFormat = true - - // Place spaces around "=" sign even when PrettyFormat is false - PrettyEqual = false - - // Explicitly write DEFAULT section header - DefaultHeader = false - - // Indicate whether to put a line between sections - PrettySection = true -) - -func init() { - if runtime.GOOS == "windows" { - LineBreak = "\r\n" - } -} - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} - -// dataSource is an interface that returns object which can be read and closed. -type dataSource interface { - ReadCloser() (io.ReadCloser, error) -} - -// sourceFile represents an object that contains content on the local file system. -type sourceFile struct { - name string -} - -func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { - return os.Open(s.name) -} - -// sourceData represents an object that contains content in memory. -type sourceData struct { - data []byte -} - -func (s *sourceData) ReadCloser() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewReader(s.data)), nil -} - -// sourceReadCloser represents an input stream with Close method. -type sourceReadCloser struct { - reader io.ReadCloser -} - -func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { - return s.reader, nil -} - -func parseDataSource(source interface{}) (dataSource, error) { - switch s := source.(type) { - case string: - return sourceFile{s}, nil - case []byte: - return &sourceData{s}, nil - case io.ReadCloser: - return &sourceReadCloser{s}, nil - default: - return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s) - } -} - -type LoadOptions struct { - // Loose indicates whether the parser should ignore nonexistent files or return error. - Loose bool - // Insensitive indicates whether the parser forces all section and key names to lowercase. - Insensitive bool - // IgnoreContinuation indicates whether to ignore continuation lines while parsing. - IgnoreContinuation bool - // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. - IgnoreInlineComment bool - // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. - // This type of keys are mostly used in my.cnf. - AllowBooleanKeys bool - // AllowShadows indicates whether to keep track of keys with same name under same section. - AllowShadows bool - // AllowNestedValues indicates whether to allow AWS-like nested values. - // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values - AllowNestedValues bool - // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. - // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure - // Relevant quote: Values can also span multiple lines, as long as they are indented deeper - // than the first line of the value. - AllowPythonMultilineValues bool - // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format - // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" - UnescapeValueDoubleQuotes bool - // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format - // when value is NOT surrounded by any quotes. - // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all. - UnescapeValueCommentSymbols bool - // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise - // conform to key/value pairs. Specify the names of those blocks here. - UnparseableSections []string -} - -func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { - sources := make([]dataSource, len(others)+1) - sources[0], err = parseDataSource(source) - if err != nil { - return nil, err - } - for i := range others { - sources[i+1], err = parseDataSource(others[i]) - if err != nil { - return nil, err - } - } - f := newFile(sources, opts) - if err = f.Reload(); err != nil { - return nil, err - } - return f, nil -} - -// Load loads and parses from INI data sources. -// Arguments can be mixed of file name with string type, or raw data in []byte. -// It will return error if list contains nonexistent files. -func Load(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{}, source, others...) -} - -// LooseLoad has exactly same functionality as Load function -// except it ignores nonexistent files instead of returning error. -func LooseLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Loose: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it forces all section and key names to be lowercased. -func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Insensitive: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it allows have shadow keys. -func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{AllowShadows: true}, source, others...) -} diff --git a/vendor/github.com/go-ini/ini/key.go b/vendor/github.com/go-ini/ini/key.go deleted file mode 100644 index 7c8566a1b4c..00000000000 --- a/vendor/github.com/go-ini/ini/key.go +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -// Key represents a key under a section. -type Key struct { - s *Section - Comment string - name string - value string - isAutoIncrement bool - isBooleanType bool - - isShadow bool - shadows []*Key - - nestedValues []string -} - -// newKey simply return a key object with given values. -func newKey(s *Section, name, val string) *Key { - return &Key{ - s: s, - name: name, - value: val, - } -} - -func (k *Key) addShadow(val string) error { - if k.isShadow { - return errors.New("cannot add shadow to another shadow key") - } else if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add shadow to auto-increment or boolean key") - } - - shadow := newKey(k.s, k.name, val) - shadow.isShadow = true - k.shadows = append(k.shadows, shadow) - return nil -} - -// AddShadow adds a new shadow key to itself. -func (k *Key) AddShadow(val string) error { - if !k.s.f.options.AllowShadows { - return errors.New("shadow key is not allowed") - } - return k.addShadow(val) -} - -func (k *Key) addNestedValue(val string) error { - if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add nested value to auto-increment or boolean key") - } - - k.nestedValues = append(k.nestedValues, val) - return nil -} - -func (k *Key) AddNestedValue(val string) error { - if !k.s.f.options.AllowNestedValues { - return errors.New("nested value is not allowed") - } - return k.addNestedValue(val) -} - -// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv -type ValueMapper func(string) string - -// Name returns name of key. -func (k *Key) Name() string { - return k.name -} - -// Value returns raw value of key for performance purpose. -func (k *Key) Value() string { - return k.value -} - -// ValueWithShadows returns raw values of key and its shadows if any. -func (k *Key) ValueWithShadows() []string { - if len(k.shadows) == 0 { - return []string{k.value} - } - vals := make([]string, len(k.shadows)+1) - vals[0] = k.value - for i := range k.shadows { - vals[i+1] = k.shadows[i].value - } - return vals -} - -// NestedValues returns nested values stored in the key. -// It is possible returned value is nil if no nested values stored in the key. -func (k *Key) NestedValues() []string { - return k.nestedValues -} - -// transformValue takes a raw value and transforms to its final string. -func (k *Key) transformValue(val string) string { - if k.s.f.ValueMapper != nil { - val = k.s.f.ValueMapper(val) - } - - // Fail-fast if no indicate char found for recursive value - if !strings.Contains(val, "%") { - return val - } - for i := 0; i < _DEPTH_VALUES; i++ { - vr := varPattern.FindString(val) - if len(vr) == 0 { - break - } - - // Take off leading '%(' and trailing ')s'. - noption := strings.TrimLeft(vr, "%(") - noption = strings.TrimRight(noption, ")s") - - // Search in the same section. - nk, err := k.s.GetKey(noption) - if err != nil || k == nk { - // Search again in default section. - nk, _ = k.s.f.Section("").GetKey(noption) - } - - // Substitute by new value and take off leading '%(' and trailing ')s'. - val = strings.Replace(val, vr, nk.value, -1) - } - return val -} - -// String returns string representation of value. -func (k *Key) String() string { - return k.transformValue(k.value) -} - -// Validate accepts a validate function which can -// return modifed result as key value. -func (k *Key) Validate(fn func(string) string) string { - return fn(k.String()) -} - -// parseBool returns the boolean value represented by the string. -// -// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On, -// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off. -// Any other value returns an error. -func parseBool(str string) (value bool, err error) { - switch str { - case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On": - return true, nil - case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off": - return false, nil - } - return false, fmt.Errorf("parsing \"%s\": invalid syntax", str) -} - -// Bool returns bool type value. -func (k *Key) Bool() (bool, error) { - return parseBool(k.String()) -} - -// Float64 returns float64 type value. -func (k *Key) Float64() (float64, error) { - return strconv.ParseFloat(k.String(), 64) -} - -// Int returns int type value. -func (k *Key) Int() (int, error) { - return strconv.Atoi(k.String()) -} - -// Int64 returns int64 type value. -func (k *Key) Int64() (int64, error) { - return strconv.ParseInt(k.String(), 10, 64) -} - -// Uint returns uint type valued. -func (k *Key) Uint() (uint, error) { - u, e := strconv.ParseUint(k.String(), 10, 64) - return uint(u), e -} - -// Uint64 returns uint64 type value. -func (k *Key) Uint64() (uint64, error) { - return strconv.ParseUint(k.String(), 10, 64) -} - -// Duration returns time.Duration type value. -func (k *Key) Duration() (time.Duration, error) { - return time.ParseDuration(k.String()) -} - -// TimeFormat parses with given format and returns time.Time type value. -func (k *Key) TimeFormat(format string) (time.Time, error) { - return time.Parse(format, k.String()) -} - -// Time parses with RFC3339 format and returns time.Time type value. -func (k *Key) Time() (time.Time, error) { - return k.TimeFormat(time.RFC3339) -} - -// MustString returns default value if key value is empty. -func (k *Key) MustString(defaultVal string) string { - val := k.String() - if len(val) == 0 { - k.value = defaultVal - return defaultVal - } - return val -} - -// MustBool always returns value without error, -// it returns false if error occurs. -func (k *Key) MustBool(defaultVal ...bool) bool { - val, err := k.Bool() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatBool(defaultVal[0]) - return defaultVal[0] - } - return val -} - -// MustFloat64 always returns value without error, -// it returns 0.0 if error occurs. -func (k *Key) MustFloat64(defaultVal ...float64) float64 { - val, err := k.Float64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64) - return defaultVal[0] - } - return val -} - -// MustInt always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt(defaultVal ...int) int { - val, err := k.Int() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(int64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustInt64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt64(defaultVal ...int64) int64 { - val, err := k.Int64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustUint always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint(defaultVal ...uint) uint { - val, err := k.Uint() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(uint64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustUint64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint64(defaultVal ...uint64) uint64 { - val, err := k.Uint64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustDuration always returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration { - val, err := k.Duration() - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].String() - return defaultVal[0] - } - return val -} - -// MustTimeFormat always parses with given format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time { - val, err := k.TimeFormat(format) - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].Format(format) - return defaultVal[0] - } - return val -} - -// MustTime always parses with RFC3339 format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTime(defaultVal ...time.Time) time.Time { - return k.MustTimeFormat(time.RFC3339, defaultVal...) -} - -// In always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) In(defaultVal string, candidates []string) string { - val := k.String() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InFloat64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 { - val := k.MustFloat64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt(defaultVal int, candidates []int) int { - val := k.MustInt() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 { - val := k.MustInt64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint(defaultVal uint, candidates []uint) uint { - val := k.MustUint() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 { - val := k.MustUint64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTimeFormat always parses with given format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time { - val := k.MustTimeFormat(format) - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTime always parses with RFC3339 format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time { - return k.InTimeFormat(time.RFC3339, defaultVal, candidates) -} - -// RangeFloat64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 { - val := k.MustFloat64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt(defaultVal, min, max int) int { - val := k.MustInt() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt64(defaultVal, min, max int64) int64 { - val := k.MustInt64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeTimeFormat checks if value with given format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { - val := k.MustTimeFormat(format) - if val.Unix() < min.Unix() || val.Unix() > max.Unix() { - return defaultVal - } - return val -} - -// RangeTime checks if value with RFC3339 format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time { - return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max) -} - -// Strings returns list of string divided by given delimiter. -func (k *Key) Strings(delim string) []string { - str := k.String() - if len(str) == 0 { - return []string{} - } - - runes := []rune(str) - vals := make([]string, 0, 2) - var buf bytes.Buffer - escape := false - idx := 0 - for { - if escape { - escape = false - if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) { - buf.WriteRune('\\') - } - buf.WriteRune(runes[idx]) - } else { - if runes[idx] == '\\' { - escape = true - } else if strings.HasPrefix(string(runes[idx:]), delim) { - idx += len(delim) - 1 - vals = append(vals, strings.TrimSpace(buf.String())) - buf.Reset() - } else { - buf.WriteRune(runes[idx]) - } - } - idx += 1 - if idx == len(runes) { - break - } - } - - if buf.Len() > 0 { - vals = append(vals, strings.TrimSpace(buf.String())) - } - - return vals -} - -// StringsWithShadows returns list of string divided by given delimiter. -// Shadows will also be appended if any. -func (k *Key) StringsWithShadows(delim string) []string { - vals := k.ValueWithShadows() - results := make([]string, 0, len(vals)*2) - for i := range vals { - if len(vals) == 0 { - continue - } - - results = append(results, strings.Split(vals[i], delim)...) - } - - for i := range results { - results[i] = k.transformValue(strings.TrimSpace(results[i])) - } - return results -} - -// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Float64s(delim string) []float64 { - vals, _ := k.parseFloat64s(k.Strings(delim), true, false) - return vals -} - -// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Ints(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), true, false) - return vals -} - -// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Int64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), true, false) - return vals -} - -// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uints(delim string) []uint { - vals, _ := k.parseUints(k.Strings(delim), true, false) - return vals -} - -// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uint64s(delim string) []uint64 { - vals, _ := k.parseUint64s(k.Strings(delim), true, false) - return vals -} - -// TimesFormat parses with given format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) TimesFormat(format, delim string) []time.Time { - vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false) - return vals -} - -// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) Times(delim string) []time.Time { - return k.TimesFormat(time.RFC3339, delim) -} - -// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then -// it will not be included to result list. -func (k *Key) ValidFloat64s(delim string) []float64 { - vals, _ := k.parseFloat64s(k.Strings(delim), false, false) - return vals -} - -// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will -// not be included to result list. -func (k *Key) ValidInts(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), false, false) - return vals -} - -// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer, -// then it will not be included to result list. -func (k *Key) ValidInt64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), false, false) - return vals -} - -// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer, -// then it will not be included to result list. -func (k *Key) ValidUints(delim string) []uint { - vals, _ := k.parseUints(k.Strings(delim), false, false) - return vals -} - -// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned -// integer, then it will not be included to result list. -func (k *Key) ValidUint64s(delim string) []uint64 { - vals, _ := k.parseUint64s(k.Strings(delim), false, false) - return vals -} - -// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimesFormat(format, delim string) []time.Time { - vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false) - return vals -} - -// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimes(delim string) []time.Time { - return k.ValidTimesFormat(time.RFC3339, delim) -} - -// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictFloat64s(delim string) ([]float64, error) { - return k.parseFloat64s(k.Strings(delim), false, true) -} - -// StrictInts returns list of int divided by given delimiter or error on first invalid input. -func (k *Key) StrictInts(delim string) ([]int, error) { - return k.parseInts(k.Strings(delim), false, true) -} - -// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictInt64s(delim string) ([]int64, error) { - return k.parseInt64s(k.Strings(delim), false, true) -} - -// StrictUints returns list of uint divided by given delimiter or error on first invalid input. -func (k *Key) StrictUints(delim string) ([]uint, error) { - return k.parseUints(k.Strings(delim), false, true) -} - -// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictUint64s(delim string) ([]uint64, error) { - return k.parseUint64s(k.Strings(delim), false, true) -} - -// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) { - return k.parseTimesFormat(format, k.Strings(delim), false, true) -} - -// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimes(delim string) ([]time.Time, error) { - return k.StrictTimesFormat(time.RFC3339, delim) -} - -// parseFloat64s transforms strings to float64s. -func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) { - vals := make([]float64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseFloat(str, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseInts transforms strings to ints. -func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) { - vals := make([]int, 0, len(strs)) - for _, str := range strs { - val, err := strconv.Atoi(str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseInt64s transforms strings to int64s. -func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) { - vals := make([]int64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseInt(str, 10, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseUints transforms strings to uints. -func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) { - vals := make([]uint, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 10, 0) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, uint(val)) - } - } - return vals, nil -} - -// parseUint64s transforms strings to uint64s. -func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) { - vals := make([]uint64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 10, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseTimesFormat transforms strings to times in given format. -func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { - vals := make([]time.Time, 0, len(strs)) - for _, str := range strs { - val, err := time.Parse(format, str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// SetValue changes key value. -func (k *Key) SetValue(v string) { - if k.s.f.BlockMode { - k.s.f.lock.Lock() - defer k.s.f.lock.Unlock() - } - - k.value = v - k.s.keysHash[k.name] = v -} diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go deleted file mode 100644 index 826e893c0d7..00000000000 --- a/vendor/github.com/go-ini/ini/parser.go +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright 2015 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bufio" - "bytes" - "fmt" - "io" - "regexp" - "strconv" - "strings" - "unicode" -) - -var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)") - -type tokenType int - -const ( - _TOKEN_INVALID tokenType = iota - _TOKEN_COMMENT - _TOKEN_SECTION - _TOKEN_KEY -) - -type parser struct { - buf *bufio.Reader - isEOF bool - count int - comment *bytes.Buffer -} - -func newParser(r io.Reader) *parser { - return &parser{ - buf: bufio.NewReader(r), - count: 1, - comment: &bytes.Buffer{}, - } -} - -// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format. -// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding -func (p *parser) BOM() error { - mask, err := p.buf.Peek(2) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 2 { - return nil - } - - switch { - case mask[0] == 254 && mask[1] == 255: - fallthrough - case mask[0] == 255 && mask[1] == 254: - p.buf.Read(mask) - case mask[0] == 239 && mask[1] == 187: - mask, err := p.buf.Peek(3) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 3 { - return nil - } - if mask[2] == 191 { - p.buf.Read(mask) - } - } - return nil -} - -func (p *parser) readUntil(delim byte) ([]byte, error) { - data, err := p.buf.ReadBytes(delim) - if err != nil { - if err == io.EOF { - p.isEOF = true - } else { - return nil, err - } - } - return data, nil -} - -func cleanComment(in []byte) ([]byte, bool) { - i := bytes.IndexAny(in, "#;") - if i == -1 { - return nil, false - } - return in[i:], true -} - -func readKeyName(in []byte) (string, int, error) { - line := string(in) - - // Check if key name surrounded by quotes. - var keyQuote string - if line[0] == '"' { - if len(line) > 6 && string(line[0:3]) == `"""` { - keyQuote = `"""` - } else { - keyQuote = `"` - } - } else if line[0] == '`' { - keyQuote = "`" - } - - // Get out key name - endIdx := -1 - if len(keyQuote) > 0 { - startIdx := len(keyQuote) - // FIXME: fail case -> """"""name"""=value - pos := strings.Index(line[startIdx:], keyQuote) - if pos == -1 { - return "", -1, fmt.Errorf("missing closing key quote: %s", line) - } - pos += startIdx - - // Find key-value delimiter - i := strings.IndexAny(line[pos+startIdx:], "=:") - if i < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - endIdx = pos + i - return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil - } - - endIdx = strings.IndexAny(line, "=:") - if endIdx < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil -} - -func (p *parser) readMultilines(line, val, valQuote string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := string(data) - - pos := strings.LastIndex(next, valQuote) - if pos > -1 { - val += next[:pos] - - comment, has := cleanComment([]byte(next[pos:])) - if has { - p.comment.Write(bytes.TrimSpace(comment)) - } - break - } - val += next - if p.isEOF { - return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next) - } - } - return val, nil -} - -func (p *parser) readContinuationLines(val string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := strings.TrimSpace(string(data)) - - if len(next) == 0 { - break - } - val += next - if val[len(val)-1] != '\\' { - break - } - val = val[:len(val)-1] - } - return val, nil -} - -// hasSurroundedQuote check if and only if the first and last characters -// are quotes \" or \'. -// It returns false if any other parts also contain same kind of quotes. -func hasSurroundedQuote(in string, quote byte) bool { - return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote && - strings.IndexByte(in[1:], quote) == len(in)-2 -} - -func (p *parser) readValue(in []byte, - parserBufferSize int, - ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines bool) (string, error) { - - line := strings.TrimLeftFunc(string(in), unicode.IsSpace) - if len(line) == 0 { - return "", nil - } - - var valQuote string - if len(line) > 3 && string(line[0:3]) == `"""` { - valQuote = `"""` - } else if line[0] == '`' { - valQuote = "`" - } else if unescapeValueDoubleQuotes && line[0] == '"' { - valQuote = `"` - } - - if len(valQuote) > 0 { - startIdx := len(valQuote) - pos := strings.LastIndex(line[startIdx:], valQuote) - // Check for multi-line value - if pos == -1 { - return p.readMultilines(line, line[startIdx:], valQuote) - } - - if unescapeValueDoubleQuotes && valQuote == `"` { - return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil - } - return line[startIdx : pos+startIdx], nil - } - - lastChar := line[len(line)-1] - // Won't be able to reach here if value only contains whitespace - line = strings.TrimSpace(line) - trimmedLastChar := line[len(line)-1] - - // Check continuation lines when desired - if !ignoreContinuation && trimmedLastChar == '\\' { - return p.readContinuationLines(line[:len(line)-1]) - } - - // Check if ignore inline comment - if !ignoreInlineComment { - i := strings.IndexAny(line, "#;") - if i > -1 { - p.comment.WriteString(line[i:]) - line = strings.TrimSpace(line[:i]) - } - } - - // Trim single and double quotes - if hasSurroundedQuote(line, '\'') || - hasSurroundedQuote(line, '"') { - line = line[1 : len(line)-1] - } else if len(valQuote) == 0 && unescapeValueCommentSymbols { - if strings.Contains(line, `\;`) { - line = strings.Replace(line, `\;`, ";", -1) - } - if strings.Contains(line, `\#`) { - line = strings.Replace(line, `\#`, "#", -1) - } - } else if allowPythonMultilines && lastChar == '\n' { - parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize) - peekBuffer := bytes.NewBuffer(parserBufferPeekResult) - - identSize := -1 - val := line - - for { - peekData, peekErr := peekBuffer.ReadBytes('\n') - if peekErr != nil { - if peekErr == io.EOF { - return val, nil - } - return "", peekErr - } - - peekMatches := pythonMultiline.FindStringSubmatch(string(peekData)) - if len(peekMatches) != 3 { - return val, nil - } - - currentIdentSize := len(peekMatches[1]) - // NOTE: Return if not a python-ini multi-line value. - if currentIdentSize < 0 { - return val, nil - } - identSize = currentIdentSize - - // NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer. - _, err := p.readUntil('\n') - if err != nil { - return "", err - } - - val += fmt.Sprintf("\n%s", peekMatches[2]) - } - - // NOTE: If it was a Python multi-line value, - // return the appended value. - if identSize > 0 { - return val, nil - } - } - - return line, nil -} - -// parse parses data through an io.Reader. -func (f *File) parse(reader io.Reader) (err error) { - p := newParser(reader) - if err = p.BOM(); err != nil { - return fmt.Errorf("BOM: %v", err) - } - - // Ignore error because default section name is never empty string. - name := DEFAULT_SECTION - if f.options.Insensitive { - name = strings.ToLower(DEFAULT_SECTION) - } - section, _ := f.NewSection(name) - - // This "last" is not strictly equivalent to "previous one" if current key is not the first nested key - var isLastValueEmpty bool - var lastRegularKey *Key - - var line []byte - var inUnparseableSection bool - - // NOTE: Iterate and increase `currentPeekSize` until - // the size of the parser buffer is found. - // TODO: When Golang 1.10 is the lowest version supported, - // replace with `parserBufferSize := p.buf.Size()`. - parserBufferSize := 0 - // NOTE: Peek 1kb at a time. - currentPeekSize := 1024 - - if f.options.AllowPythonMultilineValues { - for { - peekBytes, _ := p.buf.Peek(currentPeekSize) - peekBytesLength := len(peekBytes) - - if parserBufferSize >= peekBytesLength { - break - } - - currentPeekSize *= 2 - parserBufferSize = peekBytesLength - } - } - - for !p.isEOF { - line, err = p.readUntil('\n') - if err != nil { - return err - } - - if f.options.AllowNestedValues && - isLastValueEmpty && len(line) > 0 { - if line[0] == ' ' || line[0] == '\t' { - lastRegularKey.addNestedValue(string(bytes.TrimSpace(line))) - continue - } - } - - line = bytes.TrimLeftFunc(line, unicode.IsSpace) - if len(line) == 0 { - continue - } - - // Comments - if line[0] == '#' || line[0] == ';' { - // Note: we do not care ending line break, - // it is needed for adding second line, - // so just clean it once at the end when set to value. - p.comment.Write(line) - continue - } - - // Section - if line[0] == '[' { - // Read to the next ']' (TODO: support quoted strings) - // TODO(unknwon): use LastIndexByte when stop supporting Go1.4 - closeIdx := bytes.LastIndex(line, []byte("]")) - if closeIdx == -1 { - return fmt.Errorf("unclosed section: %s", line) - } - - name := string(line[1:closeIdx]) - section, err = f.NewSection(name) - if err != nil { - return err - } - - comment, has := cleanComment(line[closeIdx+1:]) - if has { - p.comment.Write(comment) - } - - section.Comment = strings.TrimSpace(p.comment.String()) - - // Reset aotu-counter and comments - p.comment.Reset() - p.count = 1 - - inUnparseableSection = false - for i := range f.options.UnparseableSections { - if f.options.UnparseableSections[i] == name || - (f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) { - inUnparseableSection = true - continue - } - } - continue - } - - if inUnparseableSection { - section.isRawSection = true - section.rawBody += string(line) - continue - } - - kname, offset, err := readKeyName(line) - if err != nil { - // Treat as boolean key when desired, and whole line is key name. - if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { - kname, err := p.readValue(line, - parserBufferSize, - f.options.IgnoreContinuation, - f.options.IgnoreInlineComment, - f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols, - f.options.AllowPythonMultilineValues) - if err != nil { - return err - } - key, err := section.NewBooleanKey(kname) - if err != nil { - return err - } - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - continue - } - return err - } - - // Auto increment. - isAutoIncr := false - if kname == "-" { - isAutoIncr = true - kname = "#" + strconv.Itoa(p.count) - p.count++ - } - - value, err := p.readValue(line[offset:], - parserBufferSize, - f.options.IgnoreContinuation, - f.options.IgnoreInlineComment, - f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols, - f.options.AllowPythonMultilineValues) - if err != nil { - return err - } - isLastValueEmpty = len(value) == 0 - - key, err := section.NewKey(kname, value) - if err != nil { - return err - } - key.isAutoIncrement = isAutoIncr - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - lastRegularKey = key - } - return nil -} diff --git a/vendor/github.com/go-ini/ini/section.go b/vendor/github.com/go-ini/ini/section.go deleted file mode 100644 index d8a40261920..00000000000 --- a/vendor/github.com/go-ini/ini/section.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "errors" - "fmt" - "strings" -) - -// Section represents a config section. -type Section struct { - f *File - Comment string - name string - keys map[string]*Key - keyList []string - keysHash map[string]string - - isRawSection bool - rawBody string -} - -func newSection(f *File, name string) *Section { - return &Section{ - f: f, - name: name, - keys: make(map[string]*Key), - keyList: make([]string, 0, 10), - keysHash: make(map[string]string), - } -} - -// Name returns name of Section. -func (s *Section) Name() string { - return s.name -} - -// Body returns rawBody of Section if the section was marked as unparseable. -// It still follows the other rules of the INI format surrounding leading/trailing whitespace. -func (s *Section) Body() string { - return strings.TrimSpace(s.rawBody) -} - -// SetBody updates body content only if section is raw. -func (s *Section) SetBody(body string) { - if !s.isRawSection { - return - } - s.rawBody = body -} - -// NewKey creates a new key to given section. -func (s *Section) NewKey(name, val string) (*Key, error) { - if len(name) == 0 { - return nil, errors.New("error creating new key: empty key name") - } else if s.f.options.Insensitive { - name = strings.ToLower(name) - } - - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - if inSlice(name, s.keyList) { - if s.f.options.AllowShadows { - if err := s.keys[name].addShadow(val); err != nil { - return nil, err - } - } else { - s.keys[name].value = val - } - return s.keys[name], nil - } - - s.keyList = append(s.keyList, name) - s.keys[name] = newKey(s, name, val) - s.keysHash[name] = val - return s.keys[name], nil -} - -// NewBooleanKey creates a new boolean type key to given section. -func (s *Section) NewBooleanKey(name string) (*Key, error) { - key, err := s.NewKey(name, "true") - if err != nil { - return nil, err - } - - key.isBooleanType = true - return key, nil -} - -// GetKey returns key in section by given name. -func (s *Section) GetKey(name string) (*Key, error) { - // FIXME: change to section level lock? - if s.f.BlockMode { - s.f.lock.RLock() - } - if s.f.options.Insensitive { - name = strings.ToLower(name) - } - key := s.keys[name] - if s.f.BlockMode { - s.f.lock.RUnlock() - } - - if key == nil { - // Check if it is a child-section. - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - return sec.GetKey(name) - } else { - break - } - } - return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) - } - return key, nil -} - -// HasKey returns true if section contains a key with given name. -func (s *Section) HasKey(name string) bool { - key, _ := s.GetKey(name) - return key != nil -} - -// Haskey is a backwards-compatible name for HasKey. -// TODO: delete me in v2 -func (s *Section) Haskey(name string) bool { - return s.HasKey(name) -} - -// HasValue returns true if section contains given raw value. -func (s *Section) HasValue(value string) bool { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - for _, k := range s.keys { - if value == k.value { - return true - } - } - return false -} - -// Key assumes named Key exists in section and returns a zero-value when not. -func (s *Section) Key(name string) *Key { - key, err := s.GetKey(name) - if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") - return key - } - return key -} - -// Keys returns list of keys of section. -func (s *Section) Keys() []*Key { - keys := make([]*Key, len(s.keyList)) - for i := range s.keyList { - keys[i] = s.Key(s.keyList[i]) - } - return keys -} - -// ParentKeys returns list of keys of parent section. -func (s *Section) ParentKeys() []*Key { - var parentKeys []*Key - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - parentKeys = append(parentKeys, sec.Keys()...) - } else { - break - } - - } - return parentKeys -} - -// KeyStrings returns list of key names of section. -func (s *Section) KeyStrings() []string { - list := make([]string, len(s.keyList)) - copy(list, s.keyList) - return list -} - -// KeysHash returns keys hash consisting of names and values. -func (s *Section) KeysHash() map[string]string { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - hash := map[string]string{} - for key, value := range s.keysHash { - hash[key] = value - } - return hash -} - -// DeleteKey deletes a key from section. -func (s *Section) DeleteKey(name string) { - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - for i, k := range s.keyList { - if k == name { - s.keyList = append(s.keyList[:i], s.keyList[i+1:]...) - delete(s.keys, name) - return - } - } -} - -// ChildSections returns a list of child sections of current section. -// For example, "[parent.child1]" and "[parent.child12]" are child sections -// of section "[parent]". -func (s *Section) ChildSections() []*Section { - prefix := s.name + "." - children := make([]*Section, 0, 3) - for _, name := range s.f.sectionList { - if strings.HasPrefix(name, prefix) { - children = append(children, s.f.sections[name]) - } - } - return children -} diff --git a/vendor/github.com/go-ini/ini/struct.go b/vendor/github.com/go-ini/ini/struct.go deleted file mode 100644 index 9719dc6985a..00000000000 --- a/vendor/github.com/go-ini/ini/struct.go +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "time" - "unicode" -) - -// NameMapper represents a ini tag name mapper. -type NameMapper func(string) string - -// Built-in name getters. -var ( - // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. - AllCapsUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - } - newstr = append(newstr, unicode.ToUpper(chr)) - } - return string(newstr) - } - // TitleUnderscore converts to format title_underscore. - TitleUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - chr -= ('A' - 'a') - } - newstr = append(newstr, chr) - } - return string(newstr) - } -) - -func (s *Section) parseFieldName(raw, actual string) string { - if len(actual) > 0 { - return actual - } - if s.f.NameMapper != nil { - return s.f.NameMapper(raw) - } - return raw -} - -func parseDelim(actual string) string { - if len(actual) > 0 { - return actual - } - return "," -} - -var reflectTime = reflect.TypeOf(time.Now()).Kind() - -// setSliceWithProperType sets proper values to slice based on its type. -func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { - var strs []string - if allowShadow { - strs = key.StringsWithShadows(delim) - } else { - strs = key.Strings(delim) - } - - numVals := len(strs) - if numVals == 0 { - return nil - } - - var vals interface{} - var err error - - sliceOf := field.Type().Elem().Kind() - switch sliceOf { - case reflect.String: - vals = strs - case reflect.Int: - vals, err = key.parseInts(strs, true, false) - case reflect.Int64: - vals, err = key.parseInt64s(strs, true, false) - case reflect.Uint: - vals, err = key.parseUints(strs, true, false) - case reflect.Uint64: - vals, err = key.parseUint64s(strs, true, false) - case reflect.Float64: - vals, err = key.parseFloat64s(strs, true, false) - case reflectTime: - vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - if err != nil && isStrict { - return err - } - - slice := reflect.MakeSlice(field.Type(), numVals, numVals) - for i := 0; i < numVals; i++ { - switch sliceOf { - case reflect.String: - slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i])) - case reflect.Int: - slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i])) - case reflect.Int64: - slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i])) - case reflect.Uint: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i])) - case reflect.Uint64: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) - case reflect.Float64: - slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) - case reflectTime: - slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) - } - } - field.Set(slice) - return nil -} - -func wrapStrictError(err error, isStrict bool) error { - if isStrict { - return err - } - return nil -} - -// setWithProperType sets proper value to field based on its type, -// but it does not return error for failing parsing, -// because we want to use default value that is already assigned to strcut. -func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { - switch t.Kind() { - case reflect.String: - if len(key.String()) == 0 { - return nil - } - field.SetString(key.String()) - case reflect.Bool: - boolVal, err := key.Bool() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetBool(boolVal) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && int64(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - return nil - } - - intVal, err := key.Int64() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetInt(intVal) - // byte is an alias for uint8, so supporting uint8 breaks support for byte - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && int(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - return nil - } - - uintVal, err := key.Uint64() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetUint(uintVal) - - case reflect.Float32, reflect.Float64: - floatVal, err := key.Float64() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.SetFloat(floatVal) - case reflectTime: - timeVal, err := key.Time() - if err != nil { - return wrapStrictError(err, isStrict) - } - field.Set(reflect.ValueOf(timeVal)) - case reflect.Slice: - return setSliceWithProperType(key, field, delim, allowShadow, isStrict) - default: - return fmt.Errorf("unsupported type '%s'", t) - } - return nil -} - -func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) { - opts := strings.SplitN(tag, ",", 3) - rawName = opts[0] - if len(opts) > 1 { - omitEmpty = opts[1] == "omitempty" - } - if len(opts) > 2 { - allowShadow = opts[2] == "allowshadow" - } - return rawName, omitEmpty, allowShadow -} - -func (s *Section) mapTo(val reflect.Value, isStrict bool) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - rawName, _, allowShadow := parseTagOptions(tag) - fieldName := s.parseFieldName(tpField.Name, rawName) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous - isStruct := tpField.Type.Kind() == reflect.Struct - if isAnonymous { - field.Set(reflect.New(tpField.Type.Elem())) - } - - if isAnonymous || isStruct { - if sec, err := s.f.GetSection(fieldName); err == nil { - if err = sec.mapTo(field, isStrict); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - continue - } - } - - if key, err := s.GetKey(fieldName); err == nil { - delim := parseDelim(tpField.Tag.Get("delim")) - if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - } - } - return nil -} - -// MapTo maps section to given struct. -func (s *Section) MapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val, false) -} - -// MapTo maps section to given struct in strict mode, -// which returns all possible error including value parsing error. -func (s *Section) StrictMapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val, true) -} - -// MapTo maps file to given struct. -func (f *File) MapTo(v interface{}) error { - return f.Section("").MapTo(v) -} - -// MapTo maps file to given struct in strict mode, -// which returns all possible error including value parsing error. -func (f *File) StrictMapTo(v interface{}) error { - return f.Section("").StrictMapTo(v) -} - -// MapTo maps data sources to given struct with name mapper. -func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.MapTo(v) -} - -// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode, -// which returns all possible error including value parsing error. -func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.StrictMapTo(v) -} - -// MapTo maps data sources to given struct. -func MapTo(v, source interface{}, others ...interface{}) error { - return MapToWithMapper(v, nil, source, others...) -} - -// StrictMapTo maps data sources to given struct in strict mode, -// which returns all possible error including value parsing error. -func StrictMapTo(v, source interface{}, others ...interface{}) error { - return StrictMapToWithMapper(v, nil, source, others...) -} - -// reflectSliceWithProperType does the opposite thing as setSliceWithProperType. -func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error { - slice := field.Slice(0, field.Len()) - if field.Len() == 0 { - return nil - } - - var buf bytes.Buffer - sliceOf := field.Type().Elem().Kind() - for i := 0; i < field.Len(); i++ { - switch sliceOf { - case reflect.String: - buf.WriteString(slice.Index(i).String()) - case reflect.Int, reflect.Int64: - buf.WriteString(fmt.Sprint(slice.Index(i).Int())) - case reflect.Uint, reflect.Uint64: - buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) - case reflect.Float64: - buf.WriteString(fmt.Sprint(slice.Index(i).Float())) - case reflectTime: - buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - buf.WriteString(delim) - } - key.SetValue(buf.String()[:buf.Len()-1]) - return nil -} - -// reflectWithProperType does the opposite thing as setWithProperType. -func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error { - switch t.Kind() { - case reflect.String: - key.SetValue(field.String()) - case reflect.Bool: - key.SetValue(fmt.Sprint(field.Bool())) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - key.SetValue(fmt.Sprint(field.Int())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - key.SetValue(fmt.Sprint(field.Uint())) - case reflect.Float32, reflect.Float64: - key.SetValue(fmt.Sprint(field.Float())) - case reflectTime: - key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) - case reflect.Slice: - return reflectSliceWithProperType(key, field, delim) - default: - return fmt.Errorf("unsupported type '%s'", t) - } - return nil -} - -// CR: copied from encoding/json/encode.go with modifications of time.Time support. -// TODO: add more test coverage. -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflectTime: - t, ok := v.Interface().(time.Time) - return ok && t.IsZero() - } - return false -} - -func (s *Section) reflectFrom(val reflect.Value) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - opts := strings.SplitN(tag, ",", 2) - if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) { - continue - } - - fieldName := s.parseFieldName(tpField.Name, opts[0]) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) || - (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { - // Note: The only error here is section doesn't exist. - sec, err := s.f.GetSection(fieldName) - if err != nil { - // Note: fieldName can never be empty here, ignore error. - sec, _ = s.f.NewSection(fieldName) - } - - // Add comment from comment tag - if len(sec.Comment) == 0 { - sec.Comment = tpField.Tag.Get("comment") - } - - if err = sec.reflectFrom(field); err != nil { - return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) - } - continue - } - - // Note: Same reason as secion. - key, err := s.GetKey(fieldName) - if err != nil { - key, _ = s.NewKey(fieldName, "") - } - - // Add comment from comment tag - if len(key.Comment) == 0 { - key.Comment = tpField.Tag.Get("comment") - } - - if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { - return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) - } - - } - return nil -} - -// ReflectFrom reflects secion from given struct. -func (s *Section) ReflectFrom(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot reflect from non-pointer struct") - } - - return s.reflectFrom(val) -} - -// ReflectFrom reflects file from given struct. -func (f *File) ReflectFrom(v interface{}) error { - return f.Section("").ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct with name mapper. -func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error { - cfg.NameMapper = mapper - return cfg.ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct. -func ReflectFrom(cfg *File, v interface{}) error { - return ReflectFromWithMapper(cfg, v, nil) -} diff --git a/vendor/github.com/go-xorm/builder/builder.go b/vendor/github.com/go-xorm/builder/builder.go index 1253b9887e1..ffe86d4dcb5 100644 --- a/vendor/github.com/go-xorm/builder/builder.go +++ b/vendor/github.com/go-xorm/builder/builder.go @@ -4,6 +4,12 @@ package builder +import ( + sql2 "database/sql" + "fmt" + "sort" +) + type optype byte const ( @@ -12,6 +18,15 @@ const ( insertType // insert updateType // update deleteType // delete + unionType // union +) + +const ( + POSTGRES = "postgres" + SQLITE = "sqlite3" + MYSQL = "mysql" + MSSQL = "mssql" + ORACLE = "oracle" ) type join struct { @@ -20,60 +35,115 @@ type join struct { joinCond Cond } +type union struct { + unionType string + builder *Builder +} + +type limit struct { + limitN int + offset int +} + // Builder describes a SQL statement type Builder struct { optype - tableName string - cond Cond - selects []string - joins []join - inserts Eq - updates []Eq + dialect string + isNested bool + into string + from string + subQuery *Builder + cond Cond + selects []string + joins []join + unions []union + limitation *limit + insertCols []string + insertVals []interface{} + updates []Eq + orderBy string + groupBy string + having string } -// Select creates a select Builder -func Select(cols ...string) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Select(cols...) +// Dialect sets the db dialect of Builder. +func Dialect(dialect string) *Builder { + builder := &Builder{cond: NewCond(), dialect: dialect} + return builder } -// Insert creates an insert Builder -func Insert(eq Eq) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Insert(eq) +// MySQL is shortcut of Dialect(MySQL) +func MySQL() *Builder { + return Dialect(MYSQL) } -// Update creates an update Builder -func Update(updates ...Eq) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Update(updates...) +// MsSQL is shortcut of Dialect(MsSQL) +func MsSQL() *Builder { + return Dialect(MSSQL) } -// Delete creates a delete Builder -func Delete(conds ...Cond) *Builder { - builder := &Builder{cond: NewCond()} - return builder.Delete(conds...) +// Oracle is shortcut of Dialect(Oracle) +func Oracle() *Builder { + return Dialect(ORACLE) +} + +// Postgres is shortcut of Dialect(Postgres) +func Postgres() *Builder { + return Dialect(POSTGRES) +} + +// SQLite is shortcut of Dialect(SQLITE) +func SQLite() *Builder { + return Dialect(SQLITE) } // Where sets where SQL func (b *Builder) Where(cond Cond) *Builder { - b.cond = b.cond.And(cond) + if b.cond.IsValid() { + b.cond = b.cond.And(cond) + } else { + b.cond = cond + } return b } -// From sets the table name -func (b *Builder) From(tableName string) *Builder { - b.tableName = tableName +// From sets from subject(can be a table name in string or a builder pointer) and its alias +func (b *Builder) From(subject interface{}, alias ...string) *Builder { + switch subject.(type) { + case *Builder: + b.subQuery = subject.(*Builder) + + if len(alias) > 0 { + b.from = alias[0] + } else { + b.isNested = true + } + case string: + b.from = subject.(string) + + if len(alias) > 0 { + b.from = b.from + " " + alias[0] + } + } + return b } +// TableName returns the table name +func (b *Builder) TableName() string { + if b.optype == insertType { + return b.into + } + return b.from +} + // Into sets insert table name func (b *Builder) Into(tableName string) *Builder { - b.tableName = tableName + b.into = tableName return b } -// Join sets join table and contions +// Join sets join table and conditions func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builder { switch joinCond.(type) { case Cond: @@ -85,6 +155,50 @@ func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builde return b } +// Union sets union conditions +func (b *Builder) Union(unionTp string, unionCond *Builder) *Builder { + var builder *Builder + if b.optype != unionType { + builder = &Builder{cond: NewCond()} + builder.optype = unionType + builder.dialect = b.dialect + builder.selects = b.selects + + currentUnions := b.unions + // erase sub unions (actually append to new Builder.unions) + b.unions = nil + + for e := range currentUnions { + currentUnions[e].builder.dialect = b.dialect + } + + builder.unions = append(append(builder.unions, union{"", b}), currentUnions...) + } else { + builder = b + } + + if unionCond != nil { + if unionCond.dialect == "" && builder.dialect != "" { + unionCond.dialect = builder.dialect + } + + builder.unions = append(builder.unions, union{unionTp, unionCond}) + } + + return builder +} + +// Limit sets limitN condition +func (b *Builder) Limit(limitN int, offset ...int) *Builder { + b.limitation = &limit{limitN: limitN} + + if len(offset) > 0 { + b.limitation.offset = offset[0] + } + + return b +} + // InnerJoin sets inner join func (b *Builder) InnerJoin(joinTable string, joinCond interface{}) *Builder { return b.Join("INNER", joinTable, joinCond) @@ -113,7 +227,9 @@ func (b *Builder) FullJoin(joinTable string, joinCond interface{}) *Builder { // Select sets select SQL func (b *Builder) Select(cols ...string) *Builder { b.selects = cols - b.optype = selectType + if b.optype == condType { + b.optype = selectType + } return b } @@ -129,16 +245,70 @@ func (b *Builder) Or(cond Cond) *Builder { return b } +type insertColsSorter struct { + cols []string + vals []interface{} +} + +func (s insertColsSorter) Len() int { + return len(s.cols) +} +func (s insertColsSorter) Swap(i, j int) { + s.cols[i], s.cols[j] = s.cols[j], s.cols[i] + s.vals[i], s.vals[j] = s.vals[j], s.vals[i] +} + +func (s insertColsSorter) Less(i, j int) bool { + return s.cols[i] < s.cols[j] +} + // Insert sets insert SQL -func (b *Builder) Insert(eq Eq) *Builder { - b.inserts = eq +func (b *Builder) Insert(eq ...interface{}) *Builder { + if len(eq) > 0 { + var paramType = -1 + for _, e := range eq { + switch t := e.(type) { + case Eq: + if paramType == -1 { + paramType = 0 + } + if paramType != 0 { + break + } + for k, v := range t { + b.insertCols = append(b.insertCols, k) + b.insertVals = append(b.insertVals, v) + } + case string: + if paramType == -1 { + paramType = 1 + } + if paramType != 1 { + break + } + b.insertCols = append(b.insertCols, t) + } + } + } + + if len(b.insertCols) == len(b.insertVals) { + sort.Sort(insertColsSorter{ + cols: b.insertCols, + vals: b.insertVals, + }) + } b.optype = insertType return b } // Update sets update SQL func (b *Builder) Update(updates ...Eq) *Builder { - b.updates = updates + b.updates = make([]Eq, 0, len(updates)) + for _, update := range updates { + if update.IsValid() { + b.updates = append(b.updates, update) + } + } b.optype = updateType return b } @@ -153,8 +323,8 @@ func (b *Builder) Delete(conds ...Cond) *Builder { // WriteTo implements Writer interface func (b *Builder) WriteTo(w Writer) error { switch b.optype { - case condType: - return b.cond.WriteTo(w) + /*case condType: + return b.cond.WriteTo(w)*/ case selectType: return b.selectWriteTo(w) case insertType: @@ -163,6 +333,8 @@ func (b *Builder) WriteTo(w Writer) error { return b.updateWriteTo(w) case deleteType: return b.deleteWriteTo(w) + case unionType: + return b.unionWriteTo(w) } return ErrNotSupportType @@ -175,16 +347,48 @@ func (b *Builder) ToSQL() (string, []interface{}, error) { return "", nil, err } - return w.writer.String(), w.args, nil + // in case of sql.NamedArg in args + for e := range w.args { + if namedArg, ok := w.args[e].(sql2.NamedArg); ok { + w.args[e] = namedArg.Value + } + } + + var sql = w.writer.String() + var err error + + switch b.dialect { + case ORACLE, MSSQL: + // This is for compatibility with different sql drivers + for e := range w.args { + w.args[e] = sql2.Named(fmt.Sprintf("p%d", e+1), w.args[e]) + } + + var prefix string + if b.dialect == ORACLE { + prefix = ":p" + } else { + prefix = "@p" + } + + if sql, err = ConvertPlaceholder(sql, prefix); err != nil { + return "", nil, err + } + case POSTGRES: + if sql, err = ConvertPlaceholder(sql, "$"); err != nil { + return "", nil, err + } + } + + return sql, w.args, nil } -// ToSQL convert a builder or condtions to SQL and args -func ToSQL(cond interface{}) (string, []interface{}, error) { - switch cond.(type) { - case Cond: - return condToSQL(cond.(Cond)) - case *Builder: - return cond.(*Builder).ToSQL() +// ToBoundSQL +func (b *Builder) ToBoundSQL() (string, error) { + w := NewWriter() + if err := b.WriteTo(w); err != nil { + return "", err } - return "", nil, ErrNotSupportType + + return ConvertToBoundSQL(w.writer.String(), w.args) } diff --git a/vendor/github.com/go-xorm/builder/builder_delete.go b/vendor/github.com/go-xorm/builder/builder_delete.go index 743f1a4a91b..317cc3ff9e0 100644 --- a/vendor/github.com/go-xorm/builder/builder_delete.go +++ b/vendor/github.com/go-xorm/builder/builder_delete.go @@ -5,16 +5,21 @@ package builder import ( - "errors" "fmt" ) +// Delete creates a delete Builder +func Delete(conds ...Cond) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Delete(conds...) +} + func (b *Builder) deleteWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") + if len(b.from) <= 0 { + return ErrNoTableName } - if _, err := fmt.Fprintf(w, "DELETE FROM %s WHERE ", b.tableName); err != nil { + if _, err := fmt.Fprintf(w, "DELETE FROM %s WHERE ", b.from); err != nil { return err } diff --git a/vendor/github.com/go-xorm/builder/builder_insert.go b/vendor/github.com/go-xorm/builder/builder_insert.go index 9b213ec7317..202cad51d84 100644 --- a/vendor/github.com/go-xorm/builder/builder_insert.go +++ b/vendor/github.com/go-xorm/builder/builder_insert.go @@ -6,39 +6,63 @@ package builder import ( "bytes" - "errors" "fmt" ) -func (b *Builder) insertWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") - } - if len(b.inserts) <= 0 { - return errors.New("no column to be insert") +// Insert creates an insert Builder +func Insert(eq ...interface{}) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Insert(eq...) +} + +func (b *Builder) insertSelectWriteTo(w Writer) error { + if _, err := fmt.Fprintf(w, "INSERT INTO %s ", b.into); err != nil { + return err } - if _, err := fmt.Fprintf(w, "INSERT INTO %s (", b.tableName); err != nil { + if len(b.insertCols) > 0 { + fmt.Fprintf(w, "(") + for _, col := range b.insertCols { + fmt.Fprintf(w, col) + } + fmt.Fprintf(w, ") ") + } + + return b.selectWriteTo(w) +} + +func (b *Builder) insertWriteTo(w Writer) error { + if len(b.into) <= 0 { + return ErrNoTableName + } + if len(b.insertCols) <= 0 && b.from == "" { + return ErrNoColumnToInsert + } + + if b.into != "" && b.from != "" { + return b.insertSelectWriteTo(w) + } + + if _, err := fmt.Fprintf(w, "INSERT INTO %s (", b.into); err != nil { return err } var args = make([]interface{}, 0) var bs []byte var valBuffer = bytes.NewBuffer(bs) - var i = 0 - for _, col := range b.inserts.sortedKeys() { - value := b.inserts[col] + for i, col := range b.insertCols { + value := b.insertVals[i] fmt.Fprint(w, col) if e, ok := value.(expr); ok { - fmt.Fprint(valBuffer, e.sql) + fmt.Fprintf(valBuffer, "(%s)", e.sql) args = append(args, e.args...) } else { fmt.Fprint(valBuffer, "?") args = append(args, value) } - if i != len(b.inserts)-1 { + if i != len(b.insertCols)-1 { if _, err := fmt.Fprint(w, ","); err != nil { return err } @@ -46,7 +70,6 @@ func (b *Builder) insertWriteTo(w Writer) error { return err } } - i = i + 1 } if _, err := fmt.Fprint(w, ") Values ("); err != nil { diff --git a/vendor/github.com/go-xorm/builder/builder_limit.go b/vendor/github.com/go-xorm/builder/builder_limit.go new file mode 100644 index 00000000000..82435dacbd2 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_limit.go @@ -0,0 +1,100 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "strings" +) + +func (b *Builder) limitWriteTo(w Writer) error { + if strings.TrimSpace(b.dialect) == "" { + return ErrDialectNotSetUp + } + + if b.limitation != nil { + limit := b.limitation + if limit.offset < 0 || limit.limitN <= 0 { + return ErrInvalidLimitation + } + // erase limit condition + b.limitation = nil + ow := w.(*BytesWriter) + + switch strings.ToLower(strings.TrimSpace(b.dialect)) { + case ORACLE: + if len(b.selects) == 0 { + b.selects = append(b.selects, "*") + } + + var final *Builder + selects := b.selects + b.selects = append(selects, "ROWNUM RN") + + var wb *Builder + if b.optype == unionType { + wb = Dialect(b.dialect).Select("at.*", "ROWNUM RN"). + From(b, "at") + } else { + wb = b + } + + if limit.offset == 0 { + final = Dialect(b.dialect).Select(selects...).From(wb, "at"). + Where(Lte{"at.RN": limit.limitN}) + } else { + sub := Dialect(b.dialect).Select("*"). + From(b, "at").Where(Lte{"at.RN": limit.offset + limit.limitN}) + + final = Dialect(b.dialect).Select(selects...).From(sub, "att"). + Where(Gt{"att.RN": limit.offset}) + } + + return final.WriteTo(ow) + case SQLITE, MYSQL, POSTGRES: + // if type UNION, we need to write previous content back to current writer + if b.optype == unionType { + if err := b.WriteTo(ow); err != nil { + return err + } + } + + if limit.offset == 0 { + fmt.Fprint(ow, " LIMIT ", limit.limitN) + } else { + fmt.Fprintf(ow, " LIMIT %v OFFSET %v", limit.limitN, limit.offset) + } + case MSSQL: + if len(b.selects) == 0 { + b.selects = append(b.selects, "*") + } + + var final *Builder + selects := b.selects + b.selects = append(append([]string{fmt.Sprintf("TOP %d %v", limit.limitN+limit.offset, b.selects[0])}, + b.selects[1:]...), "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN") + + var wb *Builder + if b.optype == unionType { + wb = Dialect(b.dialect).Select("*", "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN"). + From(b, "at") + } else { + wb = b + } + + if limit.offset == 0 { + final = Dialect(b.dialect).Select(selects...).From(wb, "at") + } else { + final = Dialect(b.dialect).Select(selects...).From(wb, "at").Where(Gt{"at.RN": limit.offset}) + } + + return final.WriteTo(ow) + default: + return ErrNotSupportType + } + } + + return nil +} diff --git a/vendor/github.com/go-xorm/builder/builder_select.go b/vendor/github.com/go-xorm/builder/builder_select.go index 3a3967cccc7..c33b38698be 100644 --- a/vendor/github.com/go-xorm/builder/builder_select.go +++ b/vendor/github.com/go-xorm/builder/builder_select.go @@ -5,13 +5,24 @@ package builder import ( - "errors" "fmt" ) +// Select creates a select Builder +func Select(cols ...string) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Select(cols...) +} + func (b *Builder) selectWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") + if len(b.from) <= 0 && !b.isNested { + return ErrNoTableName + } + + // perform limit before writing to writer when b.dialect between ORACLE and MSSQL + // this avoid a duplicate writing problem in simple limit query + if b.limitation != nil && (b.dialect == ORACLE || b.dialect == MSSQL) { + return b.limitWriteTo(w) } if _, err := fmt.Fprint(w, "SELECT "); err != nil { @@ -34,24 +45,101 @@ func (b *Builder) selectWriteTo(w Writer) error { } } - if _, err := fmt.Fprintf(w, " FROM %s", b.tableName); err != nil { - return err + if b.subQuery == nil { + if _, err := fmt.Fprint(w, " FROM ", b.from); err != nil { + return err + } + } else { + if b.cond.IsValid() && len(b.from) <= 0 { + return ErrUnnamedDerivedTable + } + if b.subQuery.dialect != "" && b.dialect != b.subQuery.dialect { + return ErrInconsistentDialect + } + + // dialect of sub-query will inherit from the main one (if not set up) + if b.dialect != "" && b.subQuery.dialect == "" { + b.subQuery.dialect = b.dialect + } + + switch b.subQuery.optype { + case selectType, unionType: + fmt.Fprint(w, " FROM (") + if err := b.subQuery.WriteTo(w); err != nil { + return err + } + + if len(b.from) == 0 { + fmt.Fprintf(w, ")") + } else { + fmt.Fprintf(w, ") %v", b.from) + } + default: + return ErrUnexpectedSubQuery + } } for _, v := range b.joins { - fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable) + if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil { + return err + } + if err := v.joinCond.WriteTo(w); err != nil { return err } } - if !b.cond.IsValid() { - return nil + if b.cond.IsValid() { + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + + if err := b.cond.WriteTo(w); err != nil { + return err + } } - if _, err := fmt.Fprint(w, " WHERE "); err != nil { - return err + if len(b.groupBy) > 0 { + if _, err := fmt.Fprint(w, " GROUP BY ", b.groupBy); err != nil { + return err + } } - return b.cond.WriteTo(w) + if len(b.having) > 0 { + if _, err := fmt.Fprint(w, " HAVING ", b.having); err != nil { + return err + } + } + + if len(b.orderBy) > 0 { + if _, err := fmt.Fprint(w, " ORDER BY ", b.orderBy); err != nil { + return err + } + } + + if b.limitation != nil { + if err := b.limitWriteTo(w); err != nil { + return err + } + } + + return nil +} + +// OrderBy orderBy SQL +func (b *Builder) OrderBy(orderBy string) *Builder { + b.orderBy = orderBy + return b +} + +// GroupBy groupby SQL +func (b *Builder) GroupBy(groupby string) *Builder { + b.groupBy = groupby + return b +} + +// Having having SQL +func (b *Builder) Having(having string) *Builder { + b.having = having + return b } diff --git a/vendor/github.com/go-xorm/builder/builder_union.go b/vendor/github.com/go-xorm/builder/builder_union.go new file mode 100644 index 00000000000..4ba92161787 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_union.go @@ -0,0 +1,47 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "strings" +) + +func (b *Builder) unionWriteTo(w Writer) error { + if b.limitation != nil || b.cond.IsValid() || + b.orderBy != "" || b.having != "" || b.groupBy != "" { + return ErrNotUnexpectedUnionConditions + } + + for idx, u := range b.unions { + current := u.builder + if current.optype != selectType { + return ErrUnsupportedUnionMembers + } + + if len(b.unions) == 1 { + if err := current.selectWriteTo(w); err != nil { + return err + } + } else { + if b.dialect != "" && b.dialect != current.dialect { + return ErrInconsistentDialect + } + + if idx != 0 { + fmt.Fprint(w, fmt.Sprintf(" UNION %v ", strings.ToUpper(u.unionType))) + } + fmt.Fprint(w, "(") + + if err := current.selectWriteTo(w); err != nil { + return err + } + + fmt.Fprint(w, ")") + } + } + + return nil +} diff --git a/vendor/github.com/go-xorm/builder/builder_update.go b/vendor/github.com/go-xorm/builder/builder_update.go index 182af830fdc..37b45515268 100644 --- a/vendor/github.com/go-xorm/builder/builder_update.go +++ b/vendor/github.com/go-xorm/builder/builder_update.go @@ -5,19 +5,24 @@ package builder import ( - "errors" "fmt" ) +// Update creates an update Builder +func Update(updates ...Eq) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Update(updates...) +} + func (b *Builder) updateWriteTo(w Writer) error { - if len(b.tableName) <= 0 { - return errors.New("no table indicated") + if len(b.from) <= 0 { + return ErrNoTableName } if len(b.updates) <= 0 { - return errors.New("no column to be update") + return ErrNoColumnToUpdate } - if _, err := fmt.Fprintf(w, "UPDATE %s SET ", b.tableName); err != nil { + if _, err := fmt.Fprintf(w, "UPDATE %s SET ", b.from); err != nil { return err } diff --git a/vendor/github.com/go-xorm/builder/cond.go b/vendor/github.com/go-xorm/builder/cond.go index 77dd139bfef..e44173bbd5a 100644 --- a/vendor/github.com/go-xorm/builder/cond.go +++ b/vendor/github.com/go-xorm/builder/cond.go @@ -5,7 +5,6 @@ package builder import ( - "bytes" "io" ) @@ -19,15 +18,15 @@ var _ Writer = NewWriter() // BytesWriter implments Writer and save SQL in bytes.Buffer type BytesWriter struct { - writer *bytes.Buffer - buffer []byte + writer *StringBuilder args []interface{} } // NewWriter creates a new string writer func NewWriter() *BytesWriter { - w := &BytesWriter{} - w.writer = bytes.NewBuffer(w.buffer) + w := &BytesWriter{ + writer: &StringBuilder{}, + } return w } @@ -73,15 +72,3 @@ func (condEmpty) Or(conds ...Cond) Cond { func (condEmpty) IsValid() bool { return false } - -func condToSQL(cond Cond) (string, []interface{}, error) { - if cond == nil || !cond.IsValid() { - return "", nil, nil - } - - w := NewWriter() - if err := cond.WriteTo(w); err != nil { - return "", nil, err - } - return w.writer.String(), w.args, nil -} diff --git a/vendor/github.com/go-xorm/builder/cond_between.go b/vendor/github.com/go-xorm/builder/cond_between.go index f2b29ed15bb..10e0b831521 100644 --- a/vendor/github.com/go-xorm/builder/cond_between.go +++ b/vendor/github.com/go-xorm/builder/cond_between.go @@ -17,10 +17,35 @@ var _ Cond = Between{} // WriteTo write data to Writer func (between Between) WriteTo(w Writer) error { - if _, err := fmt.Fprintf(w, "%s BETWEEN ? AND ?", between.Col); err != nil { + if _, err := fmt.Fprintf(w, "%s BETWEEN ", between.Col); err != nil { return err } - w.Append(between.LessVal, between.MoreVal) + if lv, ok := between.LessVal.(expr); ok { + if err := lv.WriteTo(w); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, "?"); err != nil { + return err + } + w.Append(between.LessVal) + } + + if _, err := fmt.Fprint(w, " AND "); err != nil { + return err + } + + if mv, ok := between.MoreVal.(expr); ok { + if err := mv.WriteTo(w); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, "?"); err != nil { + return err + } + w.Append(between.MoreVal) + } + return nil } diff --git a/vendor/github.com/go-xorm/builder/cond_or.go b/vendor/github.com/go-xorm/builder/cond_or.go index 35c3da0251e..52442653a83 100644 --- a/vendor/github.com/go-xorm/builder/cond_or.go +++ b/vendor/github.com/go-xorm/builder/cond_or.go @@ -27,10 +27,12 @@ func (o condOr) WriteTo(w Writer) error { for i, cond := range o { var needQuote bool switch cond.(type) { - case condAnd: + case condAnd, expr: needQuote = true case Eq: needQuote = (len(cond.(Eq)) > 1) + case Neq: + needQuote = (len(cond.(Neq)) > 1) } if needQuote { diff --git a/vendor/github.com/go-xorm/builder/error.go b/vendor/github.com/go-xorm/builder/error.go index d7ac51ea1fb..d830ee99557 100644 --- a/vendor/github.com/go-xorm/builder/error.go +++ b/vendor/github.com/go-xorm/builder/error.go @@ -8,9 +8,33 @@ import "errors" var ( // ErrNotSupportType not supported SQL type error - ErrNotSupportType = errors.New("not supported SQL type") + ErrNotSupportType = errors.New("Not supported SQL type") // ErrNoNotInConditions no NOT IN params error ErrNoNotInConditions = errors.New("No NOT IN conditions") // ErrNoInConditions no IN params error ErrNoInConditions = errors.New("No IN conditions") + // ErrNeedMoreArguments need more arguments + ErrNeedMoreArguments = errors.New("Need more sql arguments") + // ErrNoTableName no table name + ErrNoTableName = errors.New("No table indicated") + // ErrNoColumnToInsert no column to update + ErrNoColumnToUpdate = errors.New("No column(s) to update") + // ErrNoColumnToInsert no column to update + ErrNoColumnToInsert = errors.New("No column(s) to insert") + // ErrNotSupportDialectType not supported dialect type error + ErrNotSupportDialectType = errors.New("Not supported dialect type") + // ErrNotUnexpectedUnionConditions using union in a wrong way + ErrNotUnexpectedUnionConditions = errors.New("Unexpected conditional fields in UNION query") + // ErrUnsupportedUnionMembers unexpected members in UNION query + ErrUnsupportedUnionMembers = errors.New("Unexpected members in UNION query") + // ErrUnexpectedSubQuery Unexpected sub-query in SELECT query + ErrUnexpectedSubQuery = errors.New("Unexpected sub-query in SELECT query") + // ErrDialectNotSetUp dialect is not setup yet + ErrDialectNotSetUp = errors.New("Dialect is not setup yet, try to use `Dialect(dbType)` at first") + // ErrInvalidLimitation offset or limit is not correct + ErrInvalidLimitation = errors.New("Offset or limit is not correct") + // ErrUnnamedDerivedTable Every derived table must have its own alias + ErrUnnamedDerivedTable = errors.New("Every derived table must have its own alias") + // ErrInconsistentDialect Inconsistent dialect in same builder + ErrInconsistentDialect = errors.New("Inconsistent dialect in same builder") ) diff --git a/vendor/github.com/go-xorm/builder/sql.go b/vendor/github.com/go-xorm/builder/sql.go new file mode 100644 index 00000000000..08342427686 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/sql.go @@ -0,0 +1,156 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + sql2 "database/sql" + "fmt" + "reflect" + "time" +) + +func condToSQL(cond Cond) (string, []interface{}, error) { + if cond == nil || !cond.IsValid() { + return "", nil, nil + } + + w := NewWriter() + if err := cond.WriteTo(w); err != nil { + return "", nil, err + } + return w.writer.String(), w.args, nil +} + +func condToBoundSQL(cond Cond) (string, error) { + if cond == nil || !cond.IsValid() { + return "", nil + } + + w := NewWriter() + if err := cond.WriteTo(w); err != nil { + return "", err + } + return ConvertToBoundSQL(w.writer.String(), w.args) +} + +// ToSQL convert a builder or conditions to SQL and args +func ToSQL(cond interface{}) (string, []interface{}, error) { + switch cond.(type) { + case Cond: + return condToSQL(cond.(Cond)) + case *Builder: + return cond.(*Builder).ToSQL() + } + return "", nil, ErrNotSupportType +} + +// ToBoundSQL convert a builder or conditions to parameters bound SQL +func ToBoundSQL(cond interface{}) (string, error) { + switch cond.(type) { + case Cond: + return condToBoundSQL(cond.(Cond)) + case *Builder: + return cond.(*Builder).ToBoundSQL() + } + return "", ErrNotSupportType +} + +func noSQLQuoteNeeded(a interface{}) bool { + switch a.(type) { + case int, int8, int16, int32, int64: + return true + case uint, uint8, uint16, uint32, uint64: + return true + case float32, float64: + return true + case bool: + return true + case string: + return false + case time.Time, *time.Time: + return false + } + + t := reflect.TypeOf(a) + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.Bool: + return true + case reflect.String: + return false + } + + return false +} + +// ConvertToBoundSQL will convert SQL and args to a bound SQL +func ConvertToBoundSQL(sql string, args []interface{}) (string, error) { + buf := StringBuilder{} + var i, j, start int + for ; i < len(sql); i++ { + if sql[i] == '?' { + _, err := buf.WriteString(sql[start:i]) + if err != nil { + return "", err + } + start = i + 1 + + if len(args) == j { + return "", ErrNeedMoreArguments + } + + arg := args[j] + if namedArg, ok := arg.(sql2.NamedArg); ok { + arg = namedArg.Value + } + + if noSQLQuoteNeeded(arg) { + _, err = fmt.Fprint(&buf, arg) + } else { + _, err = fmt.Fprintf(&buf, "'%v'", arg) + } + if err != nil { + return "", err + } + j = j + 1 + } + } + _, err := buf.WriteString(sql[start:]) + if err != nil { + return "", err + } + return buf.String(), nil +} + +// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix +func ConvertPlaceholder(sql, prefix string) (string, error) { + buf := StringBuilder{} + var i, j, start int + for ; i < len(sql); i++ { + if sql[i] == '?' { + if _, err := buf.WriteString(sql[start:i]); err != nil { + return "", err + } + + start = i + 1 + j = j + 1 + + if _, err := buf.WriteString(fmt.Sprintf("%v%d", prefix, j)); err != nil { + return "", err + } + } + } + + if _, err := buf.WriteString(sql[start:]); err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/vendor/github.com/go-xorm/builder/string_builder.go b/vendor/github.com/go-xorm/builder/string_builder.go new file mode 100644 index 00000000000..d4de8717e77 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/string_builder.go @@ -0,0 +1,119 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "unicode/utf8" + "unsafe" +) + +// A StringBuilder is used to efficiently build a string using Write methods. +// It minimizes memory copying. The zero value is ready to use. +// Do not copy a non-zero Builder. +type StringBuilder struct { + addr *StringBuilder // of receiver, to detect copies by value + buf []byte +} + +// noescape hides a pointer from escape analysis. noescape is +// the identity function but escape analysis doesn't think the +// output depends on the input. noescape is inlined and currently +// compiles down to zero instructions. +// USE CAREFULLY! +// This was copied from the runtime; see issues 23382 and 7921. +//go:nosplit +func noescape(p unsafe.Pointer) unsafe.Pointer { + x := uintptr(p) + return unsafe.Pointer(x ^ 0) +} + +func (b *StringBuilder) copyCheck() { + if b.addr == nil { + // This hack works around a failing of Go's escape analysis + // that was causing b to escape and be heap allocated. + // See issue 23382. + // TODO: once issue 7921 is fixed, this should be reverted to + // just "b.addr = b". + b.addr = (*StringBuilder)(noescape(unsafe.Pointer(b))) + } else if b.addr != b { + panic("strings: illegal use of non-zero Builder copied by value") + } +} + +// String returns the accumulated string. +func (b *StringBuilder) String() string { + return *(*string)(unsafe.Pointer(&b.buf)) +} + +// Len returns the number of accumulated bytes; b.Len() == len(b.String()). +func (b *StringBuilder) Len() int { return len(b.buf) } + +// Reset resets the Builder to be empty. +func (b *StringBuilder) Reset() { + b.addr = nil + b.buf = nil +} + +// grow copies the buffer to a new, larger buffer so that there are at least n +// bytes of capacity beyond len(b.buf). +func (b *StringBuilder) grow(n int) { + buf := make([]byte, len(b.buf), 2*cap(b.buf)+n) + copy(buf, b.buf) + b.buf = buf +} + +// Grow grows b's capacity, if necessary, to guarantee space for +// another n bytes. After Grow(n), at least n bytes can be written to b +// without another allocation. If n is negative, Grow panics. +func (b *StringBuilder) Grow(n int) { + b.copyCheck() + if n < 0 { + panic("strings.Builder.Grow: negative count") + } + if cap(b.buf)-len(b.buf) < n { + b.grow(n) + } +} + +// Write appends the contents of p to b's buffer. +// Write always returns len(p), nil. +func (b *StringBuilder) Write(p []byte) (int, error) { + b.copyCheck() + b.buf = append(b.buf, p...) + return len(p), nil +} + +// WriteByte appends the byte c to b's buffer. +// The returned error is always nil. +func (b *StringBuilder) WriteByte(c byte) error { + b.copyCheck() + b.buf = append(b.buf, c) + return nil +} + +// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer. +// It returns the length of r and a nil error. +func (b *StringBuilder) WriteRune(r rune) (int, error) { + b.copyCheck() + if r < utf8.RuneSelf { + b.buf = append(b.buf, byte(r)) + return 1, nil + } + l := len(b.buf) + if cap(b.buf)-l < utf8.UTFMax { + b.grow(utf8.UTFMax) + } + n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r) + b.buf = b.buf[:l+n] + return n, nil +} + +// WriteString appends the contents of s to b's buffer. +// It returns the length of s and a nil error. +func (b *StringBuilder) WriteString(s string) (int, error) { + b.copyCheck() + b.buf = append(b.buf, s...) + return len(s), nil +} diff --git a/vendor/github.com/go-xorm/core/cache.go b/vendor/github.com/go-xorm/core/cache.go index bf81bd52ba4..dc4992dfb11 100644 --- a/vendor/github.com/go-xorm/core/cache.go +++ b/vendor/github.com/go-xorm/core/cache.go @@ -1,11 +1,16 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( - "errors" - "fmt" - "time" "bytes" "encoding/gob" + "errors" + "fmt" + "strings" + "time" ) const ( @@ -55,11 +60,10 @@ func encodeIds(ids []PK) (string, error) { return buf.String(), err } - func decodeIds(s string) ([]PK, error) { pks := make([]PK, 0) - dec := gob.NewDecoder(bytes.NewBufferString(s)) + dec := gob.NewDecoder(strings.NewReader(s)) err := dec.Decode(&pks) return pks, err diff --git a/vendor/github.com/go-xorm/core/column.go b/vendor/github.com/go-xorm/core/column.go index d9362e98578..40d8f9268d7 100644 --- a/vendor/github.com/go-xorm/core/column.go +++ b/vendor/github.com/go-xorm/core/column.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -41,6 +45,7 @@ type Column struct { Comment string } +// NewColumn creates a new column func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { return &Column{ Name: name, @@ -66,7 +71,7 @@ func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable } } -// generate column description string according dialect +// String generate column description string according dialect func (col *Column) String(d Dialect) string { sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " @@ -79,6 +84,10 @@ func (col *Column) String(d Dialect) string { } } + if col.Default != "" { + sql += "DEFAULT " + col.Default + " " + } + if d.ShowCreateNull() { if col.Nullable { sql += "NULL " @@ -87,18 +96,19 @@ func (col *Column) String(d Dialect) string { } } - if col.Default != "" { - sql += "DEFAULT " + col.Default + " " - } - return sql } +// StringNoPk generate column description string according dialect without primary keys func (col *Column) StringNoPk(d Dialect) string { sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " sql += d.SqlType(col) + " " + if col.Default != "" { + sql += "DEFAULT " + col.Default + " " + } + if d.ShowCreateNull() { if col.Nullable { sql += "NULL " @@ -107,19 +117,16 @@ func (col *Column) StringNoPk(d Dialect) string { } } - if col.Default != "" { - sql += "DEFAULT " + col.Default + " " - } - return sql } -// return col's filed of struct's value +// ValueOf returns column's filed of struct's value func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) { dataStruct := reflect.Indirect(reflect.ValueOf(bean)) return col.ValueOfV(&dataStruct) } +// ValueOfV returns column's filed of struct's value accept reflevt value func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { var fieldValue reflect.Value fieldPath := strings.Split(col.FieldName, ".") @@ -147,12 +154,12 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { } fieldValue = fieldValue.Elem().FieldByName(fieldPath[i+1]) } else { - return nil, fmt.Errorf("field %v is not valid", col.FieldName) + return nil, fmt.Errorf("field %v is not valid", col.FieldName) } } if !fieldValue.IsValid() { - return nil, fmt.Errorf("field %v is not valid", col.FieldName) + return nil, fmt.Errorf("field %v is not valid", col.FieldName) } return &fieldValue, nil diff --git a/vendor/github.com/go-xorm/core/converstion.go b/vendor/github.com/go-xorm/core/converstion.go index 18522fbeebd..9703c36e085 100644 --- a/vendor/github.com/go-xorm/core/converstion.go +++ b/vendor/github.com/go-xorm/core/converstion.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core // Conversion is an interface. A type implements Conversion will according diff --git a/vendor/github.com/go-xorm/core/db.go b/vendor/github.com/go-xorm/core/db.go index 6111c4b332f..3e50a14795d 100644 --- a/vendor/github.com/go-xorm/core/db.go +++ b/vendor/github.com/go-xorm/core/db.go @@ -1,12 +1,21 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( + "context" "database/sql" "database/sql/driver" - "errors" "fmt" "reflect" "regexp" + "sync" +) + +var ( + DefaultCacheSize = 200 ) func MapToSlice(query string, mp interface{}) (string, []interface{}, error) { @@ -58,189 +67,129 @@ func StructToSlice(query string, st interface{}) (string, []interface{}, error) return query, args, nil } -type DB struct { - *sql.DB - Mapper IMapper +type cacheStruct struct { + value reflect.Value + idx int } +// DB is a wrap of sql.DB with extra contents +type DB struct { + *sql.DB + Mapper IMapper + reflectCache map[reflect.Type]*cacheStruct + reflectCacheMutex sync.RWMutex +} + +// Open opens a database func Open(driverName, dataSourceName string) (*DB, error) { db, err := sql.Open(driverName, dataSourceName) if err != nil { return nil, err } - return &DB{db, NewCacheMapper(&SnakeMapper{})}, nil + return &DB{ + DB: db, + Mapper: NewCacheMapper(&SnakeMapper{}), + reflectCache: make(map[reflect.Type]*cacheStruct), + }, nil } +// FromDB creates a DB from a sql.DB func FromDB(db *sql.DB) *DB { - return &DB{db, NewCacheMapper(&SnakeMapper{})} + return &DB{ + DB: db, + Mapper: NewCacheMapper(&SnakeMapper{}), + reflectCache: make(map[reflect.Type]*cacheStruct), + } } -func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { - rows, err := db.DB.Query(query, args...) +func (db *DB) reflectNew(typ reflect.Type) reflect.Value { + db.reflectCacheMutex.Lock() + defer db.reflectCacheMutex.Unlock() + cs, ok := db.reflectCache[typ] + if !ok || cs.idx+1 > DefaultCacheSize-1 { + cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), DefaultCacheSize, DefaultCacheSize), 0} + db.reflectCache[typ] = cs + } else { + cs.idx = cs.idx + 1 + } + return cs.value.Index(cs.idx).Addr() +} + +// QueryContext overwrites sql.DB.QueryContext +func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { + rows, err := db.DB.QueryContext(ctx, query, args...) if err != nil { if rows != nil { rows.Close() } return nil, err } - return &Rows{rows, db.Mapper}, nil + return &Rows{rows, db}, nil } -func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { +// Query overwrites sql.DB.Query +func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { + return db.QueryContext(context.Background(), query, args...) +} + +func (db *DB) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) { query, args, err := MapToSlice(query, mp) if err != nil { return nil, err } - return db.Query(query, args...) + return db.QueryContext(ctx, query, args...) } -func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { +func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { + return db.QueryMapContext(context.Background(), query, mp) +} + +func (db *DB) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) { query, args, err := StructToSlice(query, st) if err != nil { return nil, err } - return db.Query(query, args...) + return db.QueryContext(ctx, query, args...) } -func (db *DB) QueryRow(query string, args ...interface{}) *Row { - rows, err := db.Query(query, args...) +func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { + return db.QueryStructContext(context.Background(), query, st) +} + +func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { + rows, err := db.QueryContext(ctx, query, args...) if err != nil { return &Row{nil, err} } return &Row{rows, nil} } -func (db *DB) QueryRowMap(query string, mp interface{}) *Row { +func (db *DB) QueryRow(query string, args ...interface{}) *Row { + return db.QueryRowContext(context.Background(), query, args...) +} + +func (db *DB) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row { query, args, err := MapToSlice(query, mp) if err != nil { return &Row{nil, err} } - return db.QueryRow(query, args...) + return db.QueryRowContext(ctx, query, args...) } -func (db *DB) QueryRowStruct(query string, st interface{}) *Row { +func (db *DB) QueryRowMap(query string, mp interface{}) *Row { + return db.QueryRowMapContext(context.Background(), query, mp) +} + +func (db *DB) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row { query, args, err := StructToSlice(query, st) if err != nil { return &Row{nil, err} } - return db.QueryRow(query, args...) + return db.QueryRowContext(ctx, query, args...) } -type Stmt struct { - *sql.Stmt - Mapper IMapper - names map[string]int -} - -func (db *DB) Prepare(query string) (*Stmt, error) { - names := make(map[string]int) - var i int - query = re.ReplaceAllStringFunc(query, func(src string) string { - names[src[1:]] = i - i += 1 - return "?" - }) - - stmt, err := db.DB.Prepare(query) - if err != nil { - return nil, err - } - return &Stmt{stmt, db.Mapper, names}, nil -} - -func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() - } - return s.Stmt.Exec(args...) -} - -func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().FieldByName(k).Interface() - } - return s.Stmt.Exec(args...) -} - -func (s *Stmt) Query(args ...interface{}) (*Rows, error) { - rows, err := s.Stmt.Query(args...) - if err != nil { - return nil, err - } - return &Rows{rows, s.Mapper}, nil -} - -func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() - } - - return s.Query(args...) -} - -func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return nil, errors.New("mp should be a map's pointer") - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().FieldByName(k).Interface() - } - - return s.Query(args...) -} - -func (s *Stmt) QueryRow(args ...interface{}) *Row { - rows, err := s.Query(args...) - return &Row{rows, err} -} - -func (s *Stmt) QueryRowMap(mp interface{}) *Row { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return &Row{nil, errors.New("mp should be a map's pointer")} - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() - } - - return s.QueryRow(args...) -} - -func (s *Stmt) QueryRowStruct(st interface{}) *Row { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return &Row{nil, errors.New("st should be a struct's pointer")} - } - - args := make([]interface{}, len(s.names)) - for k, i := range s.names { - args[i] = vv.Elem().FieldByName(k).Interface() - } - - return s.QueryRow(args...) +func (db *DB) QueryRowStruct(query string, st interface{}) *Row { + return db.QueryRowStructContext(context.Background(), query, st) } var ( @@ -249,120 +198,26 @@ var ( // insert into (name) values (?) // insert into (name) values (?name) -func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { +func (db *DB) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) { query, args, err := MapToSlice(query, mp) if err != nil { return nil, err } - return db.DB.Exec(query, args...) + return db.DB.ExecContext(ctx, query, args...) +} + +func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { + return db.ExecMapContext(context.Background(), query, mp) +} + +func (db *DB) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return db.DB.ExecContext(ctx, query, args...) } func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) { - query, args, err := StructToSlice(query, st) - if err != nil { - return nil, err - } - return db.DB.Exec(query, args...) -} - -type EmptyScanner struct { -} - -func (EmptyScanner) Scan(src interface{}) error { - return nil -} - -type Tx struct { - *sql.Tx - Mapper IMapper -} - -func (db *DB) Begin() (*Tx, error) { - tx, err := db.DB.Begin() - if err != nil { - return nil, err - } - return &Tx{tx, db.Mapper}, nil -} - -func (tx *Tx) Prepare(query string) (*Stmt, error) { - names := make(map[string]int) - var i int - query = re.ReplaceAllStringFunc(query, func(src string) string { - names[src[1:]] = i - i += 1 - return "?" - }) - - stmt, err := tx.Tx.Prepare(query) - if err != nil { - return nil, err - } - return &Stmt{stmt, tx.Mapper, names}, nil -} - -func (tx *Tx) Stmt(stmt *Stmt) *Stmt { - // TODO: - return stmt -} - -func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) { - query, args, err := MapToSlice(query, mp) - if err != nil { - return nil, err - } - return tx.Tx.Exec(query, args...) -} - -func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) { - query, args, err := StructToSlice(query, st) - if err != nil { - return nil, err - } - return tx.Tx.Exec(query, args...) -} - -func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { - rows, err := tx.Tx.Query(query, args...) - if err != nil { - return nil, err - } - return &Rows{rows, tx.Mapper}, nil -} - -func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) { - query, args, err := MapToSlice(query, mp) - if err != nil { - return nil, err - } - return tx.Query(query, args...) -} - -func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) { - query, args, err := StructToSlice(query, st) - if err != nil { - return nil, err - } - return tx.Query(query, args...) -} - -func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { - rows, err := tx.Query(query, args...) - return &Row{rows, err} -} - -func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { - query, args, err := MapToSlice(query, mp) - if err != nil { - return &Row{nil, err} - } - return tx.QueryRow(query, args...) -} - -func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { - query, args, err := StructToSlice(query, st) - if err != nil { - return &Row{nil, err} - } - return tx.QueryRow(query, args...) + return db.ExecStructContext(context.Background(), query, st) } diff --git a/vendor/github.com/go-xorm/core/dialect.go b/vendor/github.com/go-xorm/core/dialect.go index 6f2e81d017b..5d35a4f11d9 100644 --- a/vendor/github.com/go-xorm/core/dialect.go +++ b/vendor/github.com/go-xorm/core/dialect.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -74,6 +78,7 @@ type Dialect interface { GetIndexes(tableName string) (map[string]*Index, error) Filters() []Filter + SetParams(params map[string]string) } func OpenDialect(dialect Dialect) (*DB, error) { @@ -148,7 +153,8 @@ func (db *Base) SupportDropIfExists() bool { } func (db *Base) DropTableSql(tableName string) string { - return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName) + quote := db.dialect.Quote + return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName)) } func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) { @@ -289,6 +295,9 @@ func (b *Base) LogSQL(sql string, args []interface{}) { } } +func (b *Base) SetParams(params map[string]string) { +} + var ( dialects = map[string]func() Dialect{} ) diff --git a/vendor/github.com/go-xorm/core/driver.go b/vendor/github.com/go-xorm/core/driver.go index 0f1020b403b..ceef4ba6182 100644 --- a/vendor/github.com/go-xorm/core/driver.go +++ b/vendor/github.com/go-xorm/core/driver.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core type Driver interface { diff --git a/vendor/github.com/go-xorm/core/error.go b/vendor/github.com/go-xorm/core/error.go index 640e6036e66..63ea53e466c 100644 --- a/vendor/github.com/go-xorm/core/error.go +++ b/vendor/github.com/go-xorm/core/error.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import "errors" diff --git a/vendor/github.com/go-xorm/core/filter.go b/vendor/github.com/go-xorm/core/filter.go index 60caaf29026..6aeed4244c1 100644 --- a/vendor/github.com/go-xorm/core/filter.go +++ b/vendor/github.com/go-xorm/core/filter.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -37,9 +41,9 @@ func (q *Quoter) Quote(content string) string { func (i *IdFilter) Do(sql string, dialect Dialect, table *Table) string { quoter := NewQuoter(dialect) if table != nil && len(table.PrimaryKeys) == 1 { - sql = strings.Replace(sql, "`(id)`", quoter.Quote(table.PrimaryKeys[0]), -1) - sql = strings.Replace(sql, quoter.Quote("(id)"), quoter.Quote(table.PrimaryKeys[0]), -1) - return strings.Replace(sql, "(id)", quoter.Quote(table.PrimaryKeys[0]), -1) + sql = strings.Replace(sql, " `(id)` ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1) + sql = strings.Replace(sql, " "+quoter.Quote("(id)")+" ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1) + return strings.Replace(sql, " (id) ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1) } return sql } diff --git a/vendor/github.com/go-xorm/core/ilogger.go b/vendor/github.com/go-xorm/core/ilogger.go index c8d78496054..348ab88f4f0 100644 --- a/vendor/github.com/go-xorm/core/ilogger.go +++ b/vendor/github.com/go-xorm/core/ilogger.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core type LogLevel int diff --git a/vendor/github.com/go-xorm/core/index.go b/vendor/github.com/go-xorm/core/index.go index 73b95175adc..ac97b685053 100644 --- a/vendor/github.com/go-xorm/core/index.go +++ b/vendor/github.com/go-xorm/core/index.go @@ -1,8 +1,11 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( "fmt" - "sort" "strings" ) @@ -22,6 +25,8 @@ type Index struct { func (index *Index) XName(tableName string) string { if !strings.HasPrefix(index.Name, "UQE_") && !strings.HasPrefix(index.Name, "IDX_") { + tableName = strings.Replace(tableName, `"`, "", -1) + tableName = strings.Replace(tableName, `.`, "_", -1) if index.Type == UniqueType { return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) } @@ -44,11 +49,16 @@ func (index *Index) Equal(dst *Index) bool { if len(index.Cols) != len(dst.Cols) { return false } - sort.StringSlice(index.Cols).Sort() - sort.StringSlice(dst.Cols).Sort() for i := 0; i < len(index.Cols); i++ { - if index.Cols[i] != dst.Cols[i] { + var found bool + for j := 0; j < len(dst.Cols); j++ { + if index.Cols[i] == dst.Cols[j] { + found = true + break + } + } + if !found { return false } } diff --git a/vendor/github.com/go-xorm/core/mapper.go b/vendor/github.com/go-xorm/core/mapper.go index bb72a156624..ec44ea0db9b 100644 --- a/vendor/github.com/go-xorm/core/mapper.go +++ b/vendor/github.com/go-xorm/core/mapper.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( diff --git a/vendor/github.com/go-xorm/core/pk.go b/vendor/github.com/go-xorm/core/pk.go index 1810dd944be..05a7672d86b 100644 --- a/vendor/github.com/go-xorm/core/pk.go +++ b/vendor/github.com/go-xorm/core/pk.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( diff --git a/vendor/github.com/go-xorm/core/rows.go b/vendor/github.com/go-xorm/core/rows.go index 4a4acaa4c26..2b046d84cc7 100644 --- a/vendor/github.com/go-xorm/core/rows.go +++ b/vendor/github.com/go-xorm/core/rows.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -9,7 +13,7 @@ import ( type Rows struct { *sql.Rows - Mapper IMapper + db *DB } func (rs *Rows) ToMapString() ([]map[string]string, error) { @@ -105,7 +109,7 @@ func (rs *Rows) ScanStructByName(dest interface{}) error { newDest := make([]interface{}, len(cols)) var v EmptyScanner for j, name := range cols { - f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name)) + f := fieldByName(vv.Elem(), rs.db.Mapper.Table2Obj(name)) if f.IsValid() { newDest[j] = f.Addr().Interface() } else { @@ -116,36 +120,6 @@ func (rs *Rows) ScanStructByName(dest interface{}) error { return rs.Rows.Scan(newDest...) } -type cacheStruct struct { - value reflect.Value - idx int -} - -var ( - reflectCache = make(map[reflect.Type]*cacheStruct) - reflectCacheMutex sync.RWMutex -) - -func ReflectNew(typ reflect.Type) reflect.Value { - reflectCacheMutex.RLock() - cs, ok := reflectCache[typ] - reflectCacheMutex.RUnlock() - - const newSize = 200 - - if !ok || cs.idx+1 > newSize-1 { - cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), newSize, newSize), 0} - reflectCacheMutex.Lock() - reflectCache[typ] = cs - reflectCacheMutex.Unlock() - } else { - reflectCacheMutex.Lock() - cs.idx = cs.idx + 1 - reflectCacheMutex.Unlock() - } - return cs.value.Index(cs.idx).Addr() -} - // scan data to a slice's pointer, slice's length should equal to columns' number func (rs *Rows) ScanSlice(dest interface{}) error { vv := reflect.ValueOf(dest) @@ -197,9 +171,7 @@ func (rs *Rows) ScanMap(dest interface{}) error { vvv := vv.Elem() for i, _ := range cols { - newDest[i] = ReflectNew(vvv.Type().Elem()).Interface() - //v := reflect.New(vvv.Type().Elem()) - //newDest[i] = v.Interface() + newDest[i] = rs.db.reflectNew(vvv.Type().Elem()).Interface() } err = rs.Rows.Scan(newDest...) @@ -215,32 +187,6 @@ func (rs *Rows) ScanMap(dest interface{}) error { return nil } -/*func (rs *Rows) ScanMap(dest interface{}) error { - vv := reflect.ValueOf(dest) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return errors.New("dest should be a map's pointer") - } - - cols, err := rs.Columns() - if err != nil { - return err - } - - newDest := make([]interface{}, len(cols)) - err = rs.ScanSlice(newDest) - if err != nil { - return err - } - - vvv := vv.Elem() - - for i, name := range cols { - vname := reflect.ValueOf(name) - vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) - } - - return nil -}*/ type Row struct { rows *Rows // One of these two will be non-nil: diff --git a/vendor/github.com/go-xorm/core/scan.go b/vendor/github.com/go-xorm/core/scan.go index 7da338d8645..897b534159e 100644 --- a/vendor/github.com/go-xorm/core/scan.go +++ b/vendor/github.com/go-xorm/core/scan.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -44,9 +48,19 @@ func convertTime(dest *NullTime, src interface{}) error { } *dest = NullTime(t) return nil + case time.Time: + *dest = NullTime(s) + return nil case nil: default: return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) } return nil } + +type EmptyScanner struct { +} + +func (EmptyScanner) Scan(src interface{}) error { + return nil +} diff --git a/vendor/github.com/go-xorm/core/stmt.go b/vendor/github.com/go-xorm/core/stmt.go new file mode 100644 index 00000000000..20ee202b9b7 --- /dev/null +++ b/vendor/github.com/go-xorm/core/stmt.go @@ -0,0 +1,165 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package core + +import ( + "context" + "database/sql" + "errors" + "reflect" +) + +type Stmt struct { + *sql.Stmt + db *DB + names map[string]int +} + +func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) { + names := make(map[string]int) + var i int + query = re.ReplaceAllStringFunc(query, func(src string) string { + names[src[1:]] = i + i += 1 + return "?" + }) + + stmt, err := db.DB.PrepareContext(ctx, query) + if err != nil { + return nil, err + } + return &Stmt{stmt, db, names}, nil +} + +func (db *DB) Prepare(query string) (*Stmt, error) { + return db.PrepareContext(context.Background(), query) +} + +func (s *Stmt) ExecMapContext(ctx context.Context, mp interface{}) (sql.Result, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + return s.Stmt.ExecContext(ctx, args...) +} + +func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) { + return s.ExecMapContext(context.Background(), mp) +} + +func (s *Stmt) ExecStructContext(ctx context.Context, st interface{}) (sql.Result, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + return s.Stmt.ExecContext(ctx, args...) +} + +func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { + return s.ExecStructContext(context.Background(), st) +} + +func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, error) { + rows, err := s.Stmt.QueryContext(ctx, args...) + if err != nil { + return nil, err + } + return &Rows{rows, s.db}, nil +} + +func (s *Stmt) Query(args ...interface{}) (*Rows, error) { + return s.QueryContext(context.Background(), args...) +} + +func (s *Stmt) QueryMapContext(ctx context.Context, mp interface{}) (*Rows, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + + return s.QueryContext(ctx, args...) +} + +func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) { + return s.QueryMapContext(context.Background(), mp) +} + +func (s *Stmt) QueryStructContext(ctx context.Context, st interface{}) (*Rows, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + + return s.Query(args...) +} + +func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) { + return s.QueryStructContext(context.Background(), st) +} + +func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row { + rows, err := s.QueryContext(ctx, args...) + return &Row{rows, err} +} + +func (s *Stmt) QueryRow(args ...interface{}) *Row { + return s.QueryRowContext(context.Background(), args...) +} + +func (s *Stmt) QueryRowMapContext(ctx context.Context, mp interface{}) *Row { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return &Row{nil, errors.New("mp should be a map's pointer")} + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + + return s.QueryRowContext(ctx, args...) +} + +func (s *Stmt) QueryRowMap(mp interface{}) *Row { + return s.QueryRowMapContext(context.Background(), mp) +} + +func (s *Stmt) QueryRowStructContext(ctx context.Context, st interface{}) *Row { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return &Row{nil, errors.New("st should be a struct's pointer")} + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + + return s.QueryRowContext(ctx, args...) +} + +func (s *Stmt) QueryRowStruct(st interface{}) *Row { + return s.QueryRowStructContext(context.Background(), st) +} diff --git a/vendor/github.com/go-xorm/core/table.go b/vendor/github.com/go-xorm/core/table.go index 88199bedd61..d129e60f8b9 100644 --- a/vendor/github.com/go-xorm/core/table.go +++ b/vendor/github.com/go-xorm/core/table.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -49,7 +53,6 @@ func NewTable(name string, t reflect.Type) *Table { } func (table *Table) columnsByName(name string) []*Column { - n := len(name) for k := range table.columnsMap { @@ -75,7 +78,6 @@ func (table *Table) GetColumn(name string) *Column { } func (table *Table) GetColumnIdx(name string, idx int) *Column { - cols := table.columnsByName(name) if cols != nil && idx < len(cols) { diff --git a/vendor/github.com/go-xorm/core/tx.go b/vendor/github.com/go-xorm/core/tx.go new file mode 100644 index 00000000000..a56b70063eb --- /dev/null +++ b/vendor/github.com/go-xorm/core/tx.go @@ -0,0 +1,153 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package core + +import ( + "context" + "database/sql" +) + +type Tx struct { + *sql.Tx + db *DB +} + +func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { + tx, err := db.DB.BeginTx(ctx, opts) + if err != nil { + return nil, err + } + return &Tx{tx, db}, nil +} + +func (db *DB) Begin() (*Tx, error) { + tx, err := db.DB.Begin() + if err != nil { + return nil, err + } + return &Tx{tx, db}, nil +} + +func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) { + names := make(map[string]int) + var i int + query = re.ReplaceAllStringFunc(query, func(src string) string { + names[src[1:]] = i + i += 1 + return "?" + }) + + stmt, err := tx.Tx.PrepareContext(ctx, query) + if err != nil { + return nil, err + } + return &Stmt{stmt, tx.db, names}, nil +} + +func (tx *Tx) Prepare(query string) (*Stmt, error) { + return tx.PrepareContext(context.Background(), query) +} + +func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt { + stmt.Stmt = tx.Tx.StmtContext(ctx, stmt.Stmt) + return stmt +} + +func (tx *Tx) Stmt(stmt *Stmt) *Stmt { + return tx.StmtContext(context.Background(), stmt) +} + +func (tx *Tx) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) { + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err + } + return tx.Tx.ExecContext(ctx, query, args...) +} + +func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) { + return tx.ExecMapContext(context.Background(), query, mp) +} + +func (tx *Tx) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return tx.Tx.ExecContext(ctx, query, args...) +} + +func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) { + return tx.ExecStructContext(context.Background(), query, st) +} + +func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { + rows, err := tx.Tx.QueryContext(ctx, query, args...) + if err != nil { + return nil, err + } + return &Rows{rows, tx.db}, nil +} + +func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { + return tx.QueryContext(context.Background(), query, args...) +} + +func (tx *Tx) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) { + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err + } + return tx.QueryContext(ctx, query, args...) +} + +func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) { + return tx.QueryMapContext(context.Background(), query, mp) +} + +func (tx *Tx) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return tx.QueryContext(ctx, query, args...) +} + +func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) { + return tx.QueryStructContext(context.Background(), query, st) +} + +func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row { + rows, err := tx.QueryContext(ctx, query, args...) + return &Row{rows, err} +} + +func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { + return tx.QueryRowContext(context.Background(), query, args...) +} + +func (tx *Tx) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row { + query, args, err := MapToSlice(query, mp) + if err != nil { + return &Row{nil, err} + } + return tx.QueryRowContext(ctx, query, args...) +} + +func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { + return tx.QueryRowMapContext(context.Background(), query, mp) +} + +func (tx *Tx) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row { + query, args, err := StructToSlice(query, st) + if err != nil { + return &Row{nil, err} + } + return tx.QueryRowContext(ctx, query, args...) +} + +func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { + return tx.QueryRowStructContext(context.Background(), query, st) +} diff --git a/vendor/github.com/go-xorm/core/type.go b/vendor/github.com/go-xorm/core/type.go index 8010a2220fc..8164953602e 100644 --- a/vendor/github.com/go-xorm/core/type.go +++ b/vendor/github.com/go-xorm/core/type.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package core import ( @@ -69,24 +73,31 @@ var ( Enum = "ENUM" Set = "SET" - Char = "CHAR" - Varchar = "VARCHAR" - NVarchar = "NVARCHAR" - TinyText = "TINYTEXT" - Text = "TEXT" - Clob = "CLOB" - MediumText = "MEDIUMTEXT" - LongText = "LONGTEXT" - Uuid = "UUID" + Char = "CHAR" + Varchar = "VARCHAR" + NChar = "NCHAR" + NVarchar = "NVARCHAR" + TinyText = "TINYTEXT" + Text = "TEXT" + NText = "NTEXT" + Clob = "CLOB" + MediumText = "MEDIUMTEXT" + LongText = "LONGTEXT" + Uuid = "UUID" + UniqueIdentifier = "UNIQUEIDENTIFIER" + SysName = "SYSNAME" Date = "DATE" DateTime = "DATETIME" + SmallDateTime = "SMALLDATETIME" Time = "TIME" TimeStamp = "TIMESTAMP" TimeStampz = "TIMESTAMPZ" Decimal = "DECIMAL" Numeric = "NUMERIC" + Money = "MONEY" + SmallMoney = "SMALLMONEY" Real = "REAL" Float = "FLOAT" @@ -124,35 +135,42 @@ var ( Jsonb: TEXT_TYPE, Char: TEXT_TYPE, + NChar: TEXT_TYPE, Varchar: TEXT_TYPE, NVarchar: TEXT_TYPE, TinyText: TEXT_TYPE, Text: TEXT_TYPE, + NText: TEXT_TYPE, MediumText: TEXT_TYPE, LongText: TEXT_TYPE, Uuid: TEXT_TYPE, Clob: TEXT_TYPE, + SysName: TEXT_TYPE, Date: TIME_TYPE, DateTime: TIME_TYPE, Time: TIME_TYPE, TimeStamp: TIME_TYPE, TimeStampz: TIME_TYPE, + SmallDateTime: TIME_TYPE, Decimal: NUMERIC_TYPE, Numeric: NUMERIC_TYPE, Real: NUMERIC_TYPE, Float: NUMERIC_TYPE, Double: NUMERIC_TYPE, + Money: NUMERIC_TYPE, + SmallMoney: NUMERIC_TYPE, Binary: BLOB_TYPE, VarBinary: BLOB_TYPE, - TinyBlob: BLOB_TYPE, - Blob: BLOB_TYPE, - MediumBlob: BLOB_TYPE, - LongBlob: BLOB_TYPE, - Bytea: BLOB_TYPE, + TinyBlob: BLOB_TYPE, + Blob: BLOB_TYPE, + MediumBlob: BLOB_TYPE, + LongBlob: BLOB_TYPE, + Bytea: BLOB_TYPE, + UniqueIdentifier: BLOB_TYPE, Bool: NUMERIC_TYPE, @@ -289,15 +307,15 @@ func SQLType2Type(st SQLType) reflect.Type { return reflect.TypeOf(float32(1)) case Double: return reflect.TypeOf(float64(1)) - case Char, Varchar, NVarchar, TinyText, Text, MediumText, LongText, Enum, Set, Uuid, Clob: + case Char, NChar, Varchar, NVarchar, TinyText, Text, NText, MediumText, LongText, Enum, Set, Uuid, Clob, SysName: return reflect.TypeOf("") - case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary: + case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary, UniqueIdentifier: return reflect.TypeOf([]byte{}) case Bool: return reflect.TypeOf(true) - case DateTime, Date, Time, TimeStamp, TimeStampz: + case DateTime, Date, Time, TimeStamp, TimeStampz, SmallDateTime: return reflect.TypeOf(c_TIME_DEFAULT) - case Decimal, Numeric: + case Decimal, Numeric, Money, SmallMoney: return reflect.TypeOf("") default: return reflect.TypeOf("") diff --git a/vendor/github.com/go-xorm/xorm/context_cache.go b/vendor/github.com/go-xorm/xorm/context_cache.go new file mode 100644 index 00000000000..1bc22884968 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/context_cache.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +// ContextCache is the interface that operates the cache data. +type ContextCache interface { + // Put puts value into cache with key. + Put(key string, val interface{}) + // Get gets cached value by given key. + Get(key string) interface{} +} + +type memoryContextCache map[string]interface{} + +// NewMemoryContextCache return memoryContextCache +func NewMemoryContextCache() memoryContextCache { + return make(map[string]interface{}) +} + +// Put puts value into cache with key. +func (m memoryContextCache) Put(key string, val interface{}) { + m[key] = val +} + +// Get gets cached value by given key. +func (m memoryContextCache) Get(key string) interface{} { + return m[key] +} diff --git a/vendor/github.com/go-xorm/xorm/dialect_mysql.go b/vendor/github.com/go-xorm/xorm/dialect_mysql.go index 99100b23251..9f5ae3b2e50 100644 --- a/vendor/github.com/go-xorm/xorm/dialect_mysql.go +++ b/vendor/github.com/go-xorm/xorm/dialect_mysql.go @@ -172,12 +172,33 @@ type mysql struct { allowAllFiles bool allowOldPasswords bool clientFoundRows bool + rowFormat string } func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(d, db, uri, drivername, dataSourceName) } +func (db *mysql) SetParams(params map[string]string) { + rowFormat, ok := params["rowFormat"] + if ok { + var t = strings.ToUpper(rowFormat) + switch t { + case "COMPACT": + fallthrough + case "REDUNDANT": + fallthrough + case "DYNAMIC": + fallthrough + case "COMPRESSED": + db.rowFormat = t + break + default: + break + } + } +} + func (db *mysql) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { @@ -487,6 +508,62 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { return indexes, nil } +func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string { + var sql string + sql = "CREATE TABLE IF NOT EXISTS " + if tableName == "" { + tableName = table.Name + } + + sql += db.Quote(tableName) + sql += " (" + + if len(table.ColumnsSeq()) > 0 { + pkList := table.PrimaryKeys + + for _, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + if col.IsPrimaryKey && len(pkList) == 1 { + sql += col.String(db) + } else { + sql += col.StringNoPk(db) + } + sql = strings.TrimSpace(sql) + if len(col.Comment) > 0 { + sql += " COMMENT '" + col.Comment + "'" + } + sql += ", " + } + + if len(pkList) > 1 { + sql += "PRIMARY KEY ( " + sql += db.Quote(strings.Join(pkList, db.Quote(","))) + sql += " ), " + } + + sql = sql[:len(sql)-2] + } + sql += ")" + + if storeEngine != "" { + sql += " ENGINE=" + storeEngine + } + + if len(charset) == 0 { + charset = db.URI().Charset + } + if len(charset) != 0 { + sql += " DEFAULT CHARSET " + charset + } + + + + if db.rowFormat != "" { + sql += " ROW_FORMAT=" + db.rowFormat + } + return sql +} + func (db *mysql) Filters() []core.Filter { return []core.Filter{&core.IdFilter{}} } diff --git a/vendor/github.com/go-xorm/xorm/dialect_postgres.go b/vendor/github.com/go-xorm/xorm/dialect_postgres.go index 83e9a1015c4..1f74bd312e8 100644 --- a/vendor/github.com/go-xorm/xorm/dialect_postgres.go +++ b/vendor/github.com/go-xorm/xorm/dialect_postgres.go @@ -764,14 +764,26 @@ var ( "YES": true, "ZONE": true, } + + // DefaultPostgresSchema default postgres schema + DefaultPostgresSchema = "public" ) +const postgresPublicSchema = "public" + type postgres struct { core.Base } func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(d, db, uri, drivername, dataSourceName) + err := db.Base.Init(d, db, uri, drivername, dataSourceName) + if err != nil { + return err + } + if db.Schema == "" { + db.Schema = DefaultPostgresSchema + } + return nil } func (db *postgres) SqlType(c *core.Column) string { @@ -868,32 +880,42 @@ func (db *postgres) IndexOnTable() bool { } func (db *postgres) IndexCheckSql(tableName, idxName string) (string, []interface{}) { - args := []interface{}{tableName, idxName} + if len(db.Schema) == 0 { + args := []interface{}{tableName, idxName} + return `SELECT indexname FROM pg_indexes WHERE tablename = ? AND indexname = ?`, args + } + + args := []interface{}{db.Schema, tableName, idxName} return `SELECT indexname FROM pg_indexes ` + - `WHERE tablename = ? AND indexname = ?`, args + `WHERE schemaname = ? AND tablename = ? AND indexname = ?`, args } func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) { - args := []interface{}{tableName} - return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args + if len(db.Schema) == 0 { + args := []interface{}{tableName} + return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args + } + + args := []interface{}{db.Schema, tableName} + return `SELECT tablename FROM pg_tables WHERE schemaname = ? AND tablename = ?`, args } -/*func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interface{}) { - args := []interface{}{tableName, colName} - return "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = ?" + - " AND column_name = ?", args -}*/ - func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string { - return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", - tableName, col.Name, db.SqlType(col)) + if len(db.Schema) == 0 { + return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", + tableName, col.Name, db.SqlType(col)) + } + return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s", + db.Schema, tableName, col.Name, db.SqlType(col)) } func (db *postgres) DropIndexSql(tableName string, index *core.Index) string { - //var unique string quote := db.Quote idxName := index.Name + tableName = strings.Replace(tableName, `"`, "", -1) + tableName = strings.Replace(tableName, `.`, "_", -1) + if !strings.HasPrefix(idxName, "UQE_") && !strings.HasPrefix(idxName, "IDX_") { if index.Type == core.UniqueType { @@ -902,13 +924,21 @@ func (db *postgres) DropIndexSql(tableName string, index *core.Index) string { idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name) } } + if db.Uri.Schema != "" { + idxName = db.Uri.Schema + "." + idxName + } return fmt.Sprintf("DROP INDEX %v", quote(idxName)) } func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { - args := []interface{}{tableName, colName} - query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" + - " AND column_name = $2" + args := []interface{}{db.Schema, tableName, colName} + query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = $1 AND table_name = $2" + + " AND column_name = $3" + if len(db.Schema) == 0 { + args = []interface{}{tableName, colName} + query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" + + " AND column_name = $2" + } db.LogSQL(query, args) rows, err := db.DB().Query(query, args...) @@ -921,8 +951,7 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { } func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { - // FIXME: the schema should be replaced by user custom's - args := []interface{}{tableName, "public"} + args := []interface{}{tableName} s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey @@ -933,7 +962,15 @@ FROM pg_attribute f LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey) LEFT JOIN pg_class AS g ON p.confrelid = g.oid LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name -WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;` +WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;` + + var f string + if len(db.Schema) != 0 { + args = append(args, db.Schema) + f = " AND s.table_schema = $2" + } + s = fmt.Sprintf(s, f) + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) @@ -1023,9 +1060,13 @@ WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.att } func (db *postgres) GetTables() ([]*core.Table, error) { - // FIXME: replace public to user customrize schema - args := []interface{}{"public"} - s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1") + args := []interface{}{} + s := "SELECT tablename FROM pg_tables" + if len(db.Schema) != 0 { + args = append(args, db.Schema) + s = s + " WHERE schemaname = $1" + } + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) @@ -1049,9 +1090,12 @@ func (db *postgres) GetTables() ([]*core.Table, error) { } func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { - // FIXME: replace the public schema to user specify schema - args := []interface{}{"public", tableName} - s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2") + args := []interface{}{tableName} + s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1") + if len(db.Schema) != 0 { + args = append(args, db.Schema) + s = s + " AND schemaname=$2" + } db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) @@ -1179,3 +1223,15 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { return db, nil } + +type pqDriverPgx struct { + pqDriver +} + +func (pgx *pqDriverPgx) Parse(driverName, dataSourceName string) (*core.Uri, error) { + // Remove the leading characters for driver to work + if len(dataSourceName) >= 9 && dataSourceName[0] == 0 { + dataSourceName = dataSourceName[9:] + } + return pgx.pqDriver.Parse(driverName, dataSourceName) +} diff --git a/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go b/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go index a55b1615e71..e129481466e 100644 --- a/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go +++ b/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go @@ -233,7 +233,7 @@ func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) { } func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string { - //var unique string + // var unique string quote := db.Quote idxName := index.Name @@ -452,5 +452,9 @@ type sqlite3Driver struct { } func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + if strings.Contains(dataSourceName, "?") { + dataSourceName = dataSourceName[:strings.Index(dataSourceName, "?")] + } + return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil } diff --git a/vendor/github.com/go-xorm/xorm/engine.go b/vendor/github.com/go-xorm/xorm/engine.go index 444611afb16..89a96d9fdb5 100644 --- a/vendor/github.com/go-xorm/xorm/engine.go +++ b/vendor/github.com/go-xorm/xorm/engine.go @@ -49,6 +49,35 @@ type Engine struct { tagHandlers map[string]tagHandler engineGroup *EngineGroup + + cachers map[string]core.Cacher + cacherLock sync.RWMutex +} + +func (engine *Engine) setCacher(tableName string, cacher core.Cacher) { + engine.cacherLock.Lock() + engine.cachers[tableName] = cacher + engine.cacherLock.Unlock() +} + +func (engine *Engine) SetCacher(tableName string, cacher core.Cacher) { + engine.setCacher(tableName, cacher) +} + +func (engine *Engine) getCacher(tableName string) core.Cacher { + var cacher core.Cacher + var ok bool + engine.cacherLock.RLock() + cacher, ok = engine.cachers[tableName] + engine.cacherLock.RUnlock() + if !ok && !engine.disableGlobalCache { + cacher = engine.Cacher + } + return cacher +} + +func (engine *Engine) GetCacher(tableName string) core.Cacher { + return engine.getCacher(tableName) } // BufferSize sets buffer size for iterate @@ -148,6 +177,14 @@ func (engine *Engine) QuoteStr() string { return engine.dialect.QuoteStr() } +func (engine *Engine) quoteColumns(columnStr string) string { + columns := strings.Split(columnStr, ",") + for i := 0; i < len(columns); i++ { + columns[i] = engine.Quote(strings.TrimSpace(columns[i])) + } + return strings.Join(columns, ",") +} + // Quote Use QuoteStr quote the string sql func (engine *Engine) Quote(value string) string { value = strings.TrimSpace(value) @@ -165,7 +202,7 @@ func (engine *Engine) Quote(value string) string { } // QuoteTo quotes string and writes into the buffer -func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) { +func (engine *Engine) QuoteTo(buf *builder.StringBuilder, value string) { if buf == nil { return } @@ -208,6 +245,11 @@ func (engine *Engine) AutoIncrStr() string { return engine.dialect.AutoIncrStr() } +// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. +func (engine *Engine) SetConnMaxLifetime(d time.Duration) { + engine.db.SetConnMaxLifetime(d) +} + // SetMaxOpenConns is only available for go 1.2+ func (engine *Engine) SetMaxOpenConns(conns int) { engine.db.SetMaxOpenConns(conns) @@ -245,13 +287,7 @@ func (engine *Engine) NoCascade() *Session { // MapCacher Set a table use a special cacher func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error { - v := rValue(bean) - tb, err := engine.autoMapType(v) - if err != nil { - return err - } - - tb.Cacher = cacher + engine.setCacher(engine.TableName(bean, true), cacher) return nil } @@ -536,33 +572,6 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D return nil } -func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) { - v := rValue(beanOrTableName) - if v.Type().Kind() == reflect.String { - return beanOrTableName.(string), nil - } else if v.Type().Kind() == reflect.Struct { - return engine.tbName(v), nil - } - return "", errors.New("bean should be a struct or struct's point") -} - -func (engine *Engine) tbName(v reflect.Value) string { - if tb, ok := v.Interface().(TableName); ok { - return tb.TableName() - } - - if v.Type().Kind() == reflect.Ptr { - if tb, ok := reflect.Indirect(v).Interface().(TableName); ok { - return tb.TableName() - } - } else if v.CanAddr() { - if tb, ok := v.Addr().Interface().(TableName); ok { - return tb.TableName() - } - } - return engine.TableMapper.Obj2Table(reflect.Indirect(v).Type().Name()) -} - // Cascade use cascade or not func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { session := engine.NewSession() @@ -846,7 +855,7 @@ func (engine *Engine) TableInfo(bean interface{}) *Table { if err != nil { engine.logger.Error(err) } - return &Table{tb, engine.tbName(v)} + return &Table{tb, engine.TableName(bean)} } func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { @@ -861,15 +870,6 @@ func addIndex(indexName string, table *core.Table, col *core.Column, indexType i } } -func (engine *Engine) newTable() *core.Table { - table := core.NewEmptyTable() - - if !engine.disableGlobalCache { - table.Cacher = engine.Cacher - } - return table -} - // TableName table name interface to define customerize table name type TableName interface { TableName() string @@ -881,21 +881,9 @@ var ( func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { t := v.Type() - table := engine.newTable() - if tb, ok := v.Interface().(TableName); ok { - table.Name = tb.TableName() - } else { - if v.CanAddr() { - if tb, ok = v.Addr().Interface().(TableName); ok { - table.Name = tb.TableName() - } - } - if table.Name == "" { - table.Name = engine.TableMapper.Obj2Table(t.Name()) - } - } - + table := core.NewEmptyTable() table.Type = t + table.Name = engine.tbNameForMap(v) var idFieldColName string var hasCacheTag, hasNoCacheTag bool @@ -1049,15 +1037,15 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { if hasCacheTag { if engine.Cacher != nil { // !nash! use engine's cacher if provided engine.logger.Info("enable cache on table:", table.Name) - table.Cacher = engine.Cacher + engine.setCacher(table.Name, engine.Cacher) } else { engine.logger.Info("enable LRU cache on table:", table.Name) - table.Cacher = NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) // !nashtsai! HACK use LRU cacher for now + engine.setCacher(table.Name, NewLRUCacher2(NewMemoryStore(), time.Hour, 10000)) } } if hasNoCacheTag { - engine.logger.Info("no cache on table:", table.Name) - table.Cacher = nil + engine.logger.Info("disable cache on table:", table.Name) + engine.setCacher(table.Name, nil) } return table, nil @@ -1116,7 +1104,25 @@ func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) { pk := make([]interface{}, len(table.PrimaryKeys)) for i, col := range table.PKColumns() { var err error - pkField := v.FieldByName(col.FieldName) + + fieldName := col.FieldName + for { + parts := strings.SplitN(fieldName, ".", 2) + if len(parts) == 1 { + break + } + + v = v.FieldByName(parts[0]) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return nil, ErrUnSupportedType + } + fieldName = parts[1] + } + + pkField := v.FieldByName(fieldName) switch pkField.Kind() { case reflect.String: pk[i], err = engine.idTypeAssertion(col, pkField.String()) @@ -1162,26 +1168,10 @@ func (engine *Engine) CreateUniques(bean interface{}) error { return session.CreateUniques(bean) } -func (engine *Engine) getCacher2(table *core.Table) core.Cacher { - return table.Cacher -} - // ClearCacheBean if enabled cache, clear the cache bean func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { - v := rValue(bean) - t := v.Type() - if t.Kind() != reflect.Struct { - return errors.New("error params") - } - tableName := engine.tbName(v) - table, err := engine.autoMapType(v) - if err != nil { - return err - } - cacher := table.Cacher - if cacher == nil { - cacher = engine.Cacher - } + tableName := engine.TableName(bean) + cacher := engine.getCacher(tableName) if cacher != nil { cacher.ClearIds(tableName) cacher.DelBean(tableName, id) @@ -1192,21 +1182,8 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { // ClearCache if enabled cache, clear some tables' cache func (engine *Engine) ClearCache(beans ...interface{}) error { for _, bean := range beans { - v := rValue(bean) - t := v.Type() - if t.Kind() != reflect.Struct { - return errors.New("error params") - } - tableName := engine.tbName(v) - table, err := engine.autoMapType(v) - if err != nil { - return err - } - - cacher := table.Cacher - if cacher == nil { - cacher = engine.Cacher - } + tableName := engine.TableName(bean) + cacher := engine.getCacher(tableName) if cacher != nil { cacher.ClearIds(tableName) cacher.ClearBeans(tableName) @@ -1224,13 +1201,13 @@ func (engine *Engine) Sync(beans ...interface{}) error { for _, bean := range beans { v := rValue(bean) - tableName := engine.tbName(v) + tableNameNoSchema := engine.TableName(bean) table, err := engine.autoMapType(v) if err != nil { return err } - isExist, err := session.Table(bean).isTableExist(tableName) + isExist, err := session.Table(bean).isTableExist(tableNameNoSchema) if err != nil { return err } @@ -1256,12 +1233,12 @@ func (engine *Engine) Sync(beans ...interface{}) error { } } else { for _, col := range table.Columns() { - isExist, err := engine.dialect.IsColumnExist(tableName, col.Name) + isExist, err := engine.dialect.IsColumnExist(tableNameNoSchema, col.Name) if err != nil { return err } if !isExist { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } err = session.addColumn(col.Name) @@ -1272,35 +1249,35 @@ func (engine *Engine) Sync(beans ...interface{}) error { } for name, index := range table.Indexes { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } if index.Type == core.UniqueType { - isExist, err := session.isIndexExist2(tableName, index.Cols, true) + isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, true) if err != nil { return err } if !isExist { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } - err = session.addUnique(tableName, name) + err = session.addUnique(tableNameNoSchema, name) if err != nil { return err } } } else if index.Type == core.IndexType { - isExist, err := session.isIndexExist2(tableName, index.Cols, false) + isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, false) if err != nil { return err } if !isExist { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } - err = session.addIndex(tableName, name) + err = session.addIndex(tableNameNoSchema, name) if err != nil { return err } @@ -1369,10 +1346,10 @@ func (engine *Engine) DropIndexes(bean interface{}) error { } // Exec raw sql -func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error) { +func (engine *Engine) Exec(sqlorArgs ...interface{}) (sql.Result, error) { session := engine.NewSession() defer session.Close() - return session.Exec(sql, args...) + return session.Exec(sqlorArgs...) } // Query a raw sql and return records as []map[string][]byte @@ -1453,6 +1430,13 @@ func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error { return session.Find(beans, condiBeans...) } +// FindAndCount find the results and also return the counts +func (engine *Engine) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) { + session := engine.NewSession() + defer session.Close() + return session.FindAndCount(rowsSlicePtr, condiBean...) +} + // Iterate record by record handle records from table, bean's non-empty fields // are conditions. func (engine *Engine) Iterate(bean interface{}, fun IterFunc) error { @@ -1629,6 +1613,11 @@ func (engine *Engine) SetTZDatabase(tz *time.Location) { engine.DatabaseTZ = tz } +// SetSchema sets the schema of database +func (engine *Engine) SetSchema(schema string) { + engine.dialect.URI().Schema = schema +} + // Unscoped always disable struct tag "deleted" func (engine *Engine) Unscoped() *Session { session := engine.NewSession() diff --git a/vendor/github.com/go-xorm/xorm/engine_cond.go b/vendor/github.com/go-xorm/xorm/engine_cond.go index 6c8e3879cee..4dde8662e13 100644 --- a/vendor/github.com/go-xorm/xorm/engine_cond.go +++ b/vendor/github.com/go-xorm/xorm/engine_cond.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" "github.com/go-xorm/builder" @@ -51,7 +52,9 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{}, fieldValuePtr, err := col.ValueOf(bean) if err != nil { - engine.logger.Error(err) + if !strings.Contains(err.Error(), "is not valid") { + engine.logger.Warn(err) + } continue } diff --git a/vendor/github.com/go-xorm/xorm/engine_group.go b/vendor/github.com/go-xorm/xorm/engine_group.go index 1de425f372c..5eee3e61833 100644 --- a/vendor/github.com/go-xorm/xorm/engine_group.go +++ b/vendor/github.com/go-xorm/xorm/engine_group.go @@ -5,6 +5,8 @@ package xorm import ( + "time" + "github.com/go-xorm/core" ) @@ -99,6 +101,14 @@ func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { } } +// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. +func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { + eg.Engine.SetConnMaxLifetime(d) + for i := 0; i < len(eg.slaves); i++ { + eg.slaves[i].SetConnMaxLifetime(d) + } +} + // SetDefaultCacher set the default cacher func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) { eg.Engine.SetDefaultCacher(cacher) diff --git a/vendor/github.com/go-xorm/xorm/engine_maxlife.go b/vendor/github.com/go-xorm/xorm/engine_maxlife.go deleted file mode 100644 index 22666c5f44c..00000000000 --- a/vendor/github.com/go-xorm/xorm/engine_maxlife.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.6 - -package xorm - -import "time" - -// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. -func (engine *Engine) SetConnMaxLifetime(d time.Duration) { - engine.db.SetConnMaxLifetime(d) -} - -// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. -func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { - eg.Engine.SetConnMaxLifetime(d) - for i := 0; i < len(eg.slaves); i++ { - eg.slaves[i].SetConnMaxLifetime(d) - } -} diff --git a/vendor/github.com/go-xorm/xorm/engine_table.go b/vendor/github.com/go-xorm/xorm/engine_table.go new file mode 100644 index 00000000000..94871a4bce5 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/engine_table.go @@ -0,0 +1,113 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "fmt" + "reflect" + "strings" + + "github.com/go-xorm/core" +) + +// TableNameWithSchema will automatically add schema prefix on table name +func (engine *Engine) tbNameWithSchema(v string) string { + // Add schema name as prefix of table name. + // Only for postgres database. + if engine.dialect.DBType() == core.POSTGRES && + engine.dialect.URI().Schema != "" && + engine.dialect.URI().Schema != postgresPublicSchema && + strings.Index(v, ".") == -1 { + return engine.dialect.URI().Schema + "." + v + } + return v +} + +// TableName returns table name with schema prefix if has +func (engine *Engine) TableName(bean interface{}, includeSchema ...bool) string { + tbName := engine.tbNameNoSchema(bean) + if len(includeSchema) > 0 && includeSchema[0] { + tbName = engine.tbNameWithSchema(tbName) + } + + return tbName +} + +// tbName get some table's table name +func (session *Session) tbNameNoSchema(table *core.Table) string { + if len(session.statement.AltTableName) > 0 { + return session.statement.AltTableName + } + + return table.Name +} + +func (engine *Engine) tbNameForMap(v reflect.Value) string { + if v.Type().Implements(tpTableName) { + return v.Interface().(TableName).TableName() + } + if v.Kind() == reflect.Ptr { + v = v.Elem() + if v.Type().Implements(tpTableName) { + return v.Interface().(TableName).TableName() + } + } + + return engine.TableMapper.Obj2Table(v.Type().Name()) +} + +func (engine *Engine) tbNameNoSchema(tablename interface{}) string { + switch tablename.(type) { + case []string: + t := tablename.([]string) + if len(t) > 1 { + return fmt.Sprintf("%v AS %v", engine.Quote(t[0]), engine.Quote(t[1])) + } else if len(t) == 1 { + return engine.Quote(t[0]) + } + case []interface{}: + t := tablename.([]interface{}) + l := len(t) + var table string + if l > 0 { + f := t[0] + switch f.(type) { + case string: + table = f.(string) + case TableName: + table = f.(TableName).TableName() + default: + v := rValue(f) + t := v.Type() + if t.Kind() == reflect.Struct { + table = engine.tbNameForMap(v) + } else { + table = engine.Quote(fmt.Sprintf("%v", f)) + } + } + } + if l > 1 { + return fmt.Sprintf("%v AS %v", engine.Quote(table), + engine.Quote(fmt.Sprintf("%v", t[1]))) + } else if l == 1 { + return engine.Quote(table) + } + case TableName: + return tablename.(TableName).TableName() + case string: + return tablename.(string) + case reflect.Value: + v := tablename.(reflect.Value) + return engine.tbNameForMap(v) + default: + v := rValue(tablename) + t := v.Type() + if t.Kind() == reflect.Struct { + return engine.tbNameForMap(v) + } + return engine.Quote(fmt.Sprintf("%v", tablename)) + } + return "" +} diff --git a/vendor/github.com/go-xorm/xorm/error.go b/vendor/github.com/go-xorm/xorm/error.go index cfeefc31e8e..a223fc4a861 100644 --- a/vendor/github.com/go-xorm/xorm/error.go +++ b/vendor/github.com/go-xorm/xorm/error.go @@ -6,23 +6,44 @@ package xorm import ( "errors" + "fmt" ) var ( // ErrParamsType params error ErrParamsType = errors.New("Params type error") // ErrTableNotFound table not found error - ErrTableNotFound = errors.New("Not found table") + ErrTableNotFound = errors.New("Table not found") // ErrUnSupportedType unsupported error ErrUnSupportedType = errors.New("Unsupported type error") - // ErrNotExist record is not exist error - ErrNotExist = errors.New("Not exist error") + // ErrNotExist record does not exist error + ErrNotExist = errors.New("Record does not exist") // ErrCacheFailed cache failed error ErrCacheFailed = errors.New("Cache failed") // ErrNeedDeletedCond delete needs less one condition error - ErrNeedDeletedCond = errors.New("Delete need at least one condition") + ErrNeedDeletedCond = errors.New("Delete action needs at least one condition") // ErrNotImplemented not implemented ErrNotImplemented = errors.New("Not implemented") // ErrConditionType condition type unsupported - ErrConditionType = errors.New("Unsupported conditon type") + ErrConditionType = errors.New("Unsupported condition type") ) + +// ErrFieldIsNotExist columns does not exist +type ErrFieldIsNotExist struct { + FieldName string + TableName string +} + +func (e ErrFieldIsNotExist) Error() string { + return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) +} + +// ErrFieldIsNotValid is not valid +type ErrFieldIsNotValid struct { + FieldName string + TableName string +} + +func (e ErrFieldIsNotValid) Error() string { + return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) +} diff --git a/vendor/github.com/go-xorm/xorm/helpers.go b/vendor/github.com/go-xorm/xorm/helpers.go index f39ed472560..f1705782e3d 100644 --- a/vendor/github.com/go-xorm/xorm/helpers.go +++ b/vendor/github.com/go-xorm/xorm/helpers.go @@ -11,7 +11,6 @@ import ( "sort" "strconv" "strings" - "time" "github.com/go-xorm/core" ) @@ -293,19 +292,6 @@ func structName(v reflect.Type) string { return v.Name() } -func col2NewCols(columns ...string) []string { - newColumns := make([]string, 0, len(columns)) - for _, col := range columns { - col = strings.Replace(col, "`", "", -1) - col = strings.Replace(col, `"`, "", -1) - ccols := strings.Split(col, ",") - for _, c := range ccols { - newColumns = append(newColumns, strings.TrimSpace(c)) - } - } - return newColumns -} - func sliceEq(left, right []string) bool { if len(left) != len(right) { return false @@ -320,154 +306,6 @@ func sliceEq(left, right []string) bool { return true } -func setColumnInt(bean interface{}, col *core.Column, t int64) { - v, err := col.ValueOf(bean) - if err != nil { - return - } - if v.CanSet() { - switch v.Type().Kind() { - case reflect.Int, reflect.Int64, reflect.Int32: - v.SetInt(t) - case reflect.Uint, reflect.Uint64, reflect.Uint32: - v.SetUint(uint64(t)) - } - } -} - -func setColumnTime(bean interface{}, col *core.Column, t time.Time) { - v, err := col.ValueOf(bean) - if err != nil { - return - } - if v.CanSet() { - switch v.Type().Kind() { - case reflect.Struct: - v.Set(reflect.ValueOf(t).Convert(v.Type())) - case reflect.Int, reflect.Int64, reflect.Int32: - v.SetInt(t.Unix()) - case reflect.Uint, reflect.Uint64, reflect.Uint32: - v.SetUint(uint64(t.Unix())) - } - } -} - -func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { - colNames := make([]string, 0, len(table.ColumnsSeq())) - args := make([]interface{}, 0, len(table.ColumnsSeq())) - - for _, col := range table.Columns() { - if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } - } - if col.MapType == core.ONLYFROMDB { - continue - } - - fieldValuePtr, err := col.ValueOf(bean) - if err != nil { - return nil, nil, err - } - fieldValue := *fieldValuePtr - - if col.IsAutoIncrement { - switch fieldValue.Type().Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: - if fieldValue.Int() == 0 { - continue - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: - if fieldValue.Uint() == 0 { - continue - } - case reflect.String: - if len(fieldValue.String()) == 0 { - continue - } - case reflect.Ptr: - if fieldValue.Pointer() == 0 { - continue - } - } - } - - if col.IsDeleted { - continue - } - - if session.statement.ColumnStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } else if _, ok := session.statement.incrColumns[col.Name]; ok { - continue - } else if _, ok := session.statement.decrColumns[col.Name]; ok { - continue - } - } - if session.statement.OmitStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); ok { - continue - } - } - - // !evalphobia! set fieldValue as nil when column is nullable and zero-value - if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { - if col.Nullable && isZero(fieldValue.Interface()) { - var nilValue *int - fieldValue = reflect.ValueOf(nilValue) - } - } - - if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { - // if time is non-empty, then set to auto time - val, t := session.engine.nowTime(col) - args = append(args, val) - - var colName = col.Name - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) - } else if col.IsVersion && session.statement.checkVersion { - args = append(args, 1) - } else { - arg, err := session.value2Interface(col, fieldValue) - if err != nil { - return colNames, args, err - } - args = append(args, arg) - } - - if includeQuote { - colNames = append(colNames, session.engine.Quote(col.Name)+" = ?") - } else { - colNames = append(colNames, col.Name) - } - } - return colNames, args, nil -} - func indexName(tableName, idxName string) string { return fmt.Sprintf("IDX_%v_%v", tableName, idxName) } - -func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) { - if len(m) == 0 { - return false, false - } - - n := len(col.Name) - - for mk := range m { - if len(mk) != n { - continue - } - if strings.EqualFold(mk, col.Name) { - return m[mk], true - } - } - - return false, false -} diff --git a/vendor/github.com/go-xorm/xorm/interface.go b/vendor/github.com/go-xorm/xorm/interface.go index 9a3b6da0b2b..33d2078e44e 100644 --- a/vendor/github.com/go-xorm/xorm/interface.go +++ b/vendor/github.com/go-xorm/xorm/interface.go @@ -27,9 +27,10 @@ type Interface interface { Delete(interface{}) (int64, error) Distinct(columns ...string) *Session DropIndexes(bean interface{}) error - Exec(string, ...interface{}) (sql.Result, error) + Exec(sqlOrAgrs ...interface{}) (sql.Result, error) Exist(bean ...interface{}) (bool, error) Find(interface{}, ...interface{}) error + FindAndCount(interface{}, ...interface{}) (int64, error) Get(interface{}) (bool, error) GroupBy(keys string) *Session ID(interface{}) *Session @@ -41,6 +42,7 @@ type Interface interface { IsTableExist(beanOrTableName interface{}) (bool, error) Iterate(interface{}, IterFunc) error Limit(int, ...int) *Session + MustCols(columns ...string) *Session NoAutoCondition(...bool) *Session NotIn(string, ...interface{}) *Session Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session @@ -70,29 +72,40 @@ type EngineInterface interface { Before(func(interface{})) *Session Charset(charset string) *Session + ClearCache(...interface{}) error CreateTables(...interface{}) error DBMetas() ([]*core.Table, error) Dialect() core.Dialect DropTables(...interface{}) error DumpAllToFile(fp string, tp ...core.DbType) error + GetCacher(string) core.Cacher GetColumnMapper() core.IMapper GetDefaultCacher() core.Cacher GetTableMapper() core.IMapper GetTZDatabase() *time.Location GetTZLocation() *time.Location + MapCacher(interface{}, core.Cacher) error NewSession() *Session NoAutoTime() *Session Quote(string) string + SetCacher(string, core.Cacher) + SetConnMaxLifetime(time.Duration) SetDefaultCacher(core.Cacher) + SetLogger(logger core.ILogger) SetLogLevel(core.LogLevel) SetMapper(core.IMapper) + SetMaxOpenConns(int) + SetMaxIdleConns(int) + SetSchema(string) SetTZDatabase(tz *time.Location) SetTZLocation(tz *time.Location) + ShowExecTime(...bool) ShowSQL(show ...bool) Sync(...interface{}) error Sync2(...interface{}) error StoreEngine(storeEngine string) *Session TableInfo(bean interface{}) *Table + TableName(interface{}, ...bool) string UnMapType(reflect.Type) } diff --git a/vendor/github.com/go-xorm/xorm/rows.go b/vendor/github.com/go-xorm/xorm/rows.go index 31e29ae26f6..54ec7f37a28 100644 --- a/vendor/github.com/go-xorm/xorm/rows.go +++ b/vendor/github.com/go-xorm/xorm/rows.go @@ -32,7 +32,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { var args []interface{} var err error - if err = rows.session.statement.setRefValue(rValue(bean)); err != nil { + if err = rows.session.statement.setRefBean(bean); err != nil { return nil, err } @@ -94,8 +94,7 @@ func (rows *Rows) Scan(bean interface{}) error { return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) } - dataStruct := rValue(bean) - if err := rows.session.statement.setRefValue(dataStruct); err != nil { + if err := rows.session.statement.setRefBean(bean); err != nil { return err } @@ -104,6 +103,7 @@ func (rows *Rows) Scan(bean interface{}) error { return err } + dataStruct := rValue(bean) _, err = rows.session.slice2Bean(scanResults, rows.fields, bean, &dataStruct, rows.session.statement.RefTable) if err != nil { return err diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/github.com/go-xorm/xorm/session.go index 5c6cb5f9def..b475089191c 100644 --- a/vendor/github.com/go-xorm/xorm/session.go +++ b/vendor/github.com/go-xorm/xorm/session.go @@ -102,6 +102,12 @@ func (session *Session) Close() { } } +// ContextCache enable context cache or not +func (session *Session) ContextCache(context ContextCache) *Session { + session.statement.context = context + return session +} + // IsClosed returns if session is closed func (session *Session) IsClosed() bool { return session.db == nil @@ -278,24 +284,22 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, return } -func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) *reflect.Value { +func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) (*reflect.Value, error) { var col *core.Column if col = table.GetColumnIdx(key, idx); col == nil { - //session.engine.logger.Warnf("table %v has no column %v. %v", table.Name, key, table.ColumnsSeq()) - return nil + return nil, ErrFieldIsNotExist{key, table.Name} } fieldValue, err := col.ValueOfV(dataStruct) if err != nil { - session.engine.logger.Error(err) - return nil + return nil, err } if !fieldValue.IsValid() || !fieldValue.CanSet() { - session.engine.logger.Warnf("table %v's column %v is not valid or cannot set", table.Name, key) - return nil + return nil, ErrFieldIsNotValid{key, table.Name} } - return fieldValue + + return fieldValue, nil } // Cell cell is a result of one column field @@ -407,409 +411,417 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b } tempMap[lKey] = idx - if fieldValue := session.getField(dataStruct, key, table, idx); fieldValue != nil { - rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) - - // if row is null then ignore - if rawValue.Interface() == nil { - continue + fieldValue, err := session.getField(dataStruct, key, table, idx) + if err != nil { + if !strings.Contains(err.Error(), "is not valid") { + session.engine.logger.Warn(err) } + continue + } + if fieldValue == nil { + continue + } + rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) - if fieldValue.CanAddr() { - if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { - if data, err := value2Bytes(&rawValue); err == nil { - if err := structConvert.FromDB(data); err != nil { - return nil, err - } - } else { + // if row is null then ignore + if rawValue.Interface() == nil { + continue + } + + if fieldValue.CanAddr() { + if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { + if data, err := value2Bytes(&rawValue); err == nil { + if err := structConvert.FromDB(data); err != nil { return nil, err } - continue - } - } - - if _, ok := fieldValue.Interface().(core.Conversion); ok { - if data, err := value2Bytes(&rawValue); err == nil { - if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { - fieldValue.Set(reflect.New(fieldValue.Type().Elem())) - } - fieldValue.Interface().(core.Conversion).FromDB(data) } else { return nil, err } continue } + } - rawValueType := reflect.TypeOf(rawValue.Interface()) - vv := reflect.ValueOf(rawValue.Interface()) - col := table.GetColumnIdx(key, idx) - if col.IsPrimaryKey { - pk = append(pk, rawValue.Interface()) + if _, ok := fieldValue.Interface().(core.Conversion); ok { + if data, err := value2Bytes(&rawValue); err == nil { + if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { + fieldValue.Set(reflect.New(fieldValue.Type().Elem())) + } + fieldValue.Interface().(core.Conversion).FromDB(data) + } else { + return nil, err } - fieldType := fieldValue.Type() - hasAssigned := false + continue + } - if col.SQLType.IsJson() { - var bs []byte - if rawValueType.Kind() == reflect.String { - bs = []byte(vv.String()) - } else if rawValueType.ConvertibleTo(core.BytesType) { - bs = vv.Bytes() + rawValueType := reflect.TypeOf(rawValue.Interface()) + vv := reflect.ValueOf(rawValue.Interface()) + col := table.GetColumnIdx(key, idx) + if col.IsPrimaryKey { + pk = append(pk, rawValue.Interface()) + } + fieldType := fieldValue.Type() + hasAssigned := false + + if col.SQLType.IsJson() { + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(core.BytesType) { + bs = vv.Bytes() + } else { + return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind()) + } + + hasAssigned = true + + if len(bs) > 0 { + if fieldType.Kind() == reflect.String { + fieldValue.SetString(string(bs)) + continue + } + if fieldValue.CanAddr() { + err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + return nil, err + } } else { - return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind()) - } - - hasAssigned = true - - if len(bs) > 0 { - if fieldType.Kind() == reflect.String { - fieldValue.SetString(string(bs)) - continue - } - if fieldValue.CanAddr() { - err := json.Unmarshal(bs, fieldValue.Addr().Interface()) - if err != nil { - return nil, err - } - } else { - x := reflect.New(fieldType) - err := json.Unmarshal(bs, x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) + x := reflect.New(fieldType) + err := json.Unmarshal(bs, x.Interface()) + if err != nil { + return nil, err } + fieldValue.Set(x.Elem()) } - - continue } - switch fieldType.Kind() { - case reflect.Complex64, reflect.Complex128: - // TODO: reimplement this - var bs []byte - if rawValueType.Kind() == reflect.String { - bs = []byte(vv.String()) - } else if rawValueType.ConvertibleTo(core.BytesType) { - bs = vv.Bytes() - } + continue + } - hasAssigned = true - if len(bs) > 0 { - if fieldValue.CanAddr() { - err := json.Unmarshal(bs, fieldValue.Addr().Interface()) - if err != nil { - return nil, err - } - } else { - x := reflect.New(fieldType) - err := json.Unmarshal(bs, x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) + switch fieldType.Kind() { + case reflect.Complex64, reflect.Complex128: + // TODO: reimplement this + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(core.BytesType) { + bs = vv.Bytes() + } + + hasAssigned = true + if len(bs) > 0 { + if fieldValue.CanAddr() { + err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + return nil, err } + } else { + x := reflect.New(fieldType) + err := json.Unmarshal(bs, x.Interface()) + if err != nil { + return nil, err + } + fieldValue.Set(x.Elem()) } + } + case reflect.Slice, reflect.Array: + switch rawValueType.Kind() { case reflect.Slice, reflect.Array: - switch rawValueType.Kind() { - case reflect.Slice, reflect.Array: - switch rawValueType.Elem().Kind() { - case reflect.Uint8: - if fieldType.Elem().Kind() == reflect.Uint8 { - hasAssigned = true - if col.SQLType.IsText() { - x := reflect.New(fieldType) - err := json.Unmarshal(vv.Bytes(), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } else { - if fieldValue.Len() > 0 { - for i := 0; i < fieldValue.Len(); i++ { - if i < vv.Len() { - fieldValue.Index(i).Set(vv.Index(i)) - } - } - } else { - for i := 0; i < vv.Len(); i++ { - fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) - } - } - } - } - } - } - case reflect.String: - if rawValueType.Kind() == reflect.String { - hasAssigned = true - fieldValue.SetString(vv.String()) - } - case reflect.Bool: - if rawValueType.Kind() == reflect.Bool { - hasAssigned = true - fieldValue.SetBool(vv.Bool()) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch rawValueType.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true - fieldValue.SetInt(vv.Int()) - } - case reflect.Float32, reflect.Float64: - switch rawValueType.Kind() { - case reflect.Float32, reflect.Float64: - hasAssigned = true - fieldValue.SetFloat(vv.Float()) - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - switch rawValueType.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - hasAssigned = true - fieldValue.SetUint(vv.Uint()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true - fieldValue.SetUint(uint64(vv.Int())) - } - case reflect.Struct: - if fieldType.ConvertibleTo(core.TimeType) { - dbTZ := session.engine.DatabaseTZ - if col.TimeZone != nil { - dbTZ = col.TimeZone - } - - if rawValueType == core.TimeType { + switch rawValueType.Elem().Kind() { + case reflect.Uint8: + if fieldType.Elem().Kind() == reflect.Uint8 { hasAssigned = true - - t := vv.Convert(core.TimeType).Interface().(time.Time) - - z, _ := t.Zone() - // set new location if database don't save timezone or give an incorrect timezone - if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location - session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), - t.Minute(), t.Second(), t.Nanosecond(), dbTZ) - } - - t = t.In(session.engine.TZLocation) - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } else if rawValueType == core.IntType || rawValueType == core.Int64Type || - rawValueType == core.Int32Type { - hasAssigned = true - - t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } else { - if d, ok := vv.Interface().([]uint8); ok { - hasAssigned = true - t, err := session.byte2Time(col, d) - if err != nil { - session.engine.logger.Error("byte2Time error:", err.Error()) - hasAssigned = false - } else { - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } - } else if d, ok := vv.Interface().(string); ok { - hasAssigned = true - t, err := session.str2Time(col, d) - if err != nil { - session.engine.logger.Error("byte2Time error:", err.Error()) - hasAssigned = false - } else { - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } - } else { - return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) - } - } - } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { - // !! 增加支持sql.Scanner接口的结构,如sql.NullString - hasAssigned = true - if err := nulVal.Scan(vv.Interface()); err != nil { - session.engine.logger.Error("sql.Sanner error:", err.Error()) - hasAssigned = false - } - } else if col.SQLType.IsJson() { - if rawValueType.Kind() == reflect.String { - hasAssigned = true - x := reflect.New(fieldType) - if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } - } else if rawValueType.Kind() == reflect.Slice { - hasAssigned = true - x := reflect.New(fieldType) - if len(vv.Bytes()) > 0 { + if col.SQLType.IsText() { + x := reflect.New(fieldType) err := json.Unmarshal(vv.Bytes(), x.Interface()) if err != nil { return nil, err } fieldValue.Set(x.Elem()) - } - } - } else if session.statement.UseCascade { - table, err := session.engine.autoMapType(*fieldValue) - if err != nil { - return nil, err - } - - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - return nil, errors.New("unsupported non or composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - pk[0], err = asKind(vv, rawValueType) - if err != nil { - return nil, err - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - fieldValue.Set(structInter.Elem()) } else { - return nil, errors.New("cascade obj is not exist") + if fieldValue.Len() > 0 { + for i := 0; i < fieldValue.Len(); i++ { + if i < vv.Len() { + fieldValue.Index(i).Set(vv.Index(i)) + } + } + } else { + for i := 0; i < vv.Len(); i++ { + fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) + } + } } } } - case reflect.Ptr: - // !nashtsai! TODO merge duplicated codes above - switch fieldType { - // following types case matching ptr's native type, therefore assign ptr directly - case core.PtrStringType: - if rawValueType.Kind() == reflect.String { - x := vv.String() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrBoolType: - if rawValueType.Kind() == reflect.Bool { - x := vv.Bool() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrTimeType: - if rawValueType == core.PtrTimeType { - hasAssigned = true - var x = rawValue.Interface().(time.Time) - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrFloat64Type: - if rawValueType.Kind() == reflect.Float64 { - x := vv.Float() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrUint64Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint64(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt64Type: - if rawValueType.Kind() == reflect.Int64 { - x := vv.Int() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrFloat32Type: - if rawValueType.Kind() == reflect.Float64 { - var x = float32(vv.Float()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrIntType: - if rawValueType.Kind() == reflect.Int64 { - var x = int(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt32Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int32(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt8Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int8(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrInt16Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int16(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrUintType: - if rawValueType.Kind() == reflect.Int64 { - var x = uint(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.PtrUint32Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint32(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.Uint8Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint8(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.Uint16Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint16(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case core.Complex64Type: - var x complex64 - if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), &x) - if err != nil { - return nil, err - } - fieldValue.Set(reflect.ValueOf(&x)) - } - hasAssigned = true - case core.Complex128Type: - var x complex128 - if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), &x) - if err != nil { - return nil, err - } - fieldValue.Set(reflect.ValueOf(&x)) - } - hasAssigned = true - } // switch fieldType - } // switch fieldType.Kind() + } + case reflect.String: + if rawValueType.Kind() == reflect.String { + hasAssigned = true + fieldValue.SetString(vv.String()) + } + case reflect.Bool: + if rawValueType.Kind() == reflect.Bool { + hasAssigned = true + fieldValue.SetBool(vv.Bool()) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch rawValueType.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + hasAssigned = true + fieldValue.SetInt(vv.Int()) + } + case reflect.Float32, reflect.Float64: + switch rawValueType.Kind() { + case reflect.Float32, reflect.Float64: + hasAssigned = true + fieldValue.SetFloat(vv.Float()) + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + switch rawValueType.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + hasAssigned = true + fieldValue.SetUint(vv.Uint()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + hasAssigned = true + fieldValue.SetUint(uint64(vv.Int())) + } + case reflect.Struct: + if fieldType.ConvertibleTo(core.TimeType) { + dbTZ := session.engine.DatabaseTZ + if col.TimeZone != nil { + dbTZ = col.TimeZone + } - // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value - if !hasAssigned { - data, err := value2Bytes(&rawValue) + if rawValueType == core.TimeType { + hasAssigned = true + + t := vv.Convert(core.TimeType).Interface().(time.Time) + + z, _ := t.Zone() + // set new location if database don't save timezone or give an incorrect timezone + if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location + session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) + t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), + t.Minute(), t.Second(), t.Nanosecond(), dbTZ) + } + + t = t.In(session.engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else if rawValueType == core.IntType || rawValueType == core.Int64Type || + rawValueType == core.Int32Type { + hasAssigned = true + + t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else { + if d, ok := vv.Interface().([]uint8); ok { + hasAssigned = true + t, err := session.byte2Time(col, d) + if err != nil { + session.engine.logger.Error("byte2Time error:", err.Error()) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else if d, ok := vv.Interface().(string); ok { + hasAssigned = true + t, err := session.str2Time(col, d) + if err != nil { + session.engine.logger.Error("byte2Time error:", err.Error()) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else { + return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) + } + } + } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { + // !! 增加支持sql.Scanner接口的结构,如sql.NullString + hasAssigned = true + if err := nulVal.Scan(vv.Interface()); err != nil { + session.engine.logger.Error("sql.Sanner error:", err.Error()) + hasAssigned = false + } + } else if col.SQLType.IsJson() { + if rawValueType.Kind() == reflect.String { + hasAssigned = true + x := reflect.New(fieldType) + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), x.Interface()) + if err != nil { + return nil, err + } + fieldValue.Set(x.Elem()) + } + } else if rawValueType.Kind() == reflect.Slice { + hasAssigned = true + x := reflect.New(fieldType) + if len(vv.Bytes()) > 0 { + err := json.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + return nil, err + } + fieldValue.Set(x.Elem()) + } + } + } else if session.statement.UseCascade { + table, err := session.engine.autoMapType(*fieldValue) if err != nil { return nil, err } - if err = session.bytes2Value(col, fieldValue, data); err != nil { + hasAssigned = true + if len(table.PrimaryKeys) != 1 { + return nil, errors.New("unsupported non or composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + pk[0], err = asKind(vv, rawValueType) + if err != nil { return nil, err } + + if !isPKZero(pk) { + // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch + // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne + // property to be fetched lazily + structInter := reflect.New(fieldValue.Type()) + has, err := session.ID(pk).NoCascade().get(structInter.Interface()) + if err != nil { + return nil, err + } + if has { + fieldValue.Set(structInter.Elem()) + } else { + return nil, errors.New("cascade obj is not exist") + } + } + } + case reflect.Ptr: + // !nashtsai! TODO merge duplicated codes above + switch fieldType { + // following types case matching ptr's native type, therefore assign ptr directly + case core.PtrStringType: + if rawValueType.Kind() == reflect.String { + x := vv.String() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrBoolType: + if rawValueType.Kind() == reflect.Bool { + x := vv.Bool() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrTimeType: + if rawValueType == core.PtrTimeType { + hasAssigned = true + var x = rawValue.Interface().(time.Time) + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrFloat64Type: + if rawValueType.Kind() == reflect.Float64 { + x := vv.Float() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrUint64Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint64(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt64Type: + if rawValueType.Kind() == reflect.Int64 { + x := vv.Int() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrFloat32Type: + if rawValueType.Kind() == reflect.Float64 { + var x = float32(vv.Float()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrIntType: + if rawValueType.Kind() == reflect.Int64 { + var x = int(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt32Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int32(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt8Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int8(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrInt16Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int16(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrUintType: + if rawValueType.Kind() == reflect.Int64 { + var x = uint(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.PtrUint32Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint32(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.Uint8Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint8(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.Uint16Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint16(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case core.Complex64Type: + var x complex64 + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), &x) + if err != nil { + return nil, err + } + fieldValue.Set(reflect.ValueOf(&x)) + } + hasAssigned = true + case core.Complex128Type: + var x complex128 + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), &x) + if err != nil { + return nil, err + } + fieldValue.Set(reflect.ValueOf(&x)) + } + hasAssigned = true + } // switch fieldType + } // switch fieldType.Kind() + + // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value + if !hasAssigned { + data, err := value2Bytes(&rawValue) + if err != nil { + return nil, err + } + + if err = session.bytes2Value(col, fieldValue, data); err != nil { + return nil, err } } } @@ -828,15 +840,6 @@ func (session *Session) LastSQL() (string, []interface{}) { return session.lastSQL, session.lastSQLArgs } -// tbName get some table's table name -func (session *Session) tbNameNoSchema(table *core.Table) string { - if len(session.statement.AltTableName) > 0 { - return session.statement.AltTableName - } - - return table.Name -} - // Unscoped always disable struct tag "deleted" func (session *Session) Unscoped() *Session { session.statement.Unscoped() diff --git a/vendor/github.com/go-xorm/xorm/session_cols.go b/vendor/github.com/go-xorm/xorm/session_cols.go index 9972cb0ae4b..47d109c6cbb 100644 --- a/vendor/github.com/go-xorm/xorm/session_cols.go +++ b/vendor/github.com/go-xorm/xorm/session_cols.go @@ -4,6 +4,121 @@ package xorm +import ( + "reflect" + "strings" + "time" + + "github.com/go-xorm/core" +) + +type incrParam struct { + colName string + arg interface{} +} + +type decrParam struct { + colName string + arg interface{} +} + +type exprParam struct { + colName string + expr string +} + +type columnMap []string + +func (m columnMap) contain(colName string) bool { + if len(m) == 0 { + return false + } + + n := len(colName) + for _, mk := range m { + if len(mk) != n { + continue + } + if strings.EqualFold(mk, colName) { + return true + } + } + + return false +} + +func (m *columnMap) add(colName string) bool { + if m.contain(colName) { + return false + } + *m = append(*m, colName) + return true +} + +func setColumnInt(bean interface{}, col *core.Column, t int64) { + v, err := col.ValueOf(bean) + if err != nil { + return + } + if v.CanSet() { + switch v.Type().Kind() { + case reflect.Int, reflect.Int64, reflect.Int32: + v.SetInt(t) + case reflect.Uint, reflect.Uint64, reflect.Uint32: + v.SetUint(uint64(t)) + } + } +} + +func setColumnTime(bean interface{}, col *core.Column, t time.Time) { + v, err := col.ValueOf(bean) + if err != nil { + return + } + if v.CanSet() { + switch v.Type().Kind() { + case reflect.Struct: + v.Set(reflect.ValueOf(t).Convert(v.Type())) + case reflect.Int, reflect.Int64, reflect.Int32: + v.SetInt(t.Unix()) + case reflect.Uint, reflect.Uint64, reflect.Uint32: + v.SetUint(uint64(t.Unix())) + } + } +} + +func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) { + if len(m) == 0 { + return false, false + } + + n := len(col.Name) + + for mk := range m { + if len(mk) != n { + continue + } + if strings.EqualFold(mk, col.Name) { + return m[mk], true + } + } + + return false, false +} + +func col2NewCols(columns ...string) []string { + newColumns := make([]string, 0, len(columns)) + for _, col := range columns { + col = strings.Replace(col, "`", "", -1) + col = strings.Replace(col, `"`, "", -1) + ccols := strings.Split(col, ",") + for _, c := range ccols { + newColumns = append(newColumns, strings.TrimSpace(c)) + } + } + return newColumns +} + // Incr provides a query string like "count = count + 1" func (session *Session) Incr(column string, arg ...interface{}) *Session { session.statement.Incr(column, arg...) diff --git a/vendor/github.com/go-xorm/xorm/session_delete.go b/vendor/github.com/go-xorm/xorm/session_delete.go index 688b122ca6d..d9cf3ea9373 100644 --- a/vendor/github.com/go-xorm/xorm/session_delete.go +++ b/vendor/github.com/go-xorm/xorm/session_delete.go @@ -27,7 +27,7 @@ func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, return ErrCacheFailed } - cacher := session.engine.getCacher2(table) + cacher := session.engine.getCacher(tableName) pkColumns := table.PKColumns() ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { @@ -79,7 +79,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { defer session.Close() } - if err := session.statement.setRefValue(rValue(bean)); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return 0, err } @@ -199,7 +199,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { }) } - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { + if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache { session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) } diff --git a/vendor/github.com/go-xorm/xorm/session_exist.go b/vendor/github.com/go-xorm/xorm/session_exist.go index 049c1ddff14..74a660e852b 100644 --- a/vendor/github.com/go-xorm/xorm/session_exist.go +++ b/vendor/github.com/go-xorm/xorm/session_exist.go @@ -10,6 +10,7 @@ import ( "reflect" "github.com/go-xorm/builder" + "github.com/go-xorm/core" ) // Exist returns true if the record exist otherwise return false @@ -35,10 +36,18 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { return false, err } - sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) + if session.engine.dialect.DBType() == core.MSSQL { + sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s WHERE %s", tableName, condSQL) + } else { + sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) + } args = condArgs } else { - sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) + if session.engine.dialect.DBType() == core.MSSQL { + sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s", tableName) + } else { + sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) + } args = []interface{}{} } } else { @@ -48,7 +57,7 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { } if beanValue.Elem().Kind() == reflect.Struct { - if err := session.statement.setRefValue(beanValue.Elem()); err != nil { + if err := session.statement.setRefBean(bean[0]); err != nil { return false, err } } diff --git a/vendor/github.com/go-xorm/xorm/session_find.go b/vendor/github.com/go-xorm/xorm/session_find.go index f95dcfef2cb..b75f83479f1 100644 --- a/vendor/github.com/go-xorm/xorm/session_find.go +++ b/vendor/github.com/go-xorm/xorm/session_find.go @@ -29,6 +29,39 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) return session.find(rowsSlicePtr, condiBean...) } +// FindAndCount find the results and also return the counts +func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) { + if session.isAutoClose { + defer session.Close() + } + + session.autoResetStatement = false + err := session.find(rowsSlicePtr, condiBean...) + if err != nil { + return 0, err + } + + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { + return 0, errors.New("needs a pointer to a slice or a map") + } + + sliceElementType := sliceValue.Type().Elem() + if sliceElementType.Kind() == reflect.Ptr { + sliceElementType = sliceElementType.Elem() + } + session.autoResetStatement = true + + if session.statement.selectStr != "" { + session.statement.selectStr = "" + } + if session.statement.OrderStr != "" { + session.statement.OrderStr = "" + } + + return session.Count(reflect.New(sliceElementType).Interface()) +} + func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { @@ -42,7 +75,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if sliceElementType.Kind() == reflect.Ptr { if sliceElementType.Elem().Kind() == reflect.Struct { pv := reflect.New(sliceElementType.Elem()) - if err := session.statement.setRefValue(pv.Elem()); err != nil { + if err := session.statement.setRefValue(pv); err != nil { return err } } else { @@ -50,7 +83,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } } else if sliceElementType.Kind() == reflect.Struct { pv := reflect.New(sliceElementType) - if err := session.statement.setRefValue(pv.Elem()); err != nil { + if err := session.statement.setRefValue(pv); err != nil { return err } } else { @@ -102,7 +135,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if session.statement.JoinStr == "" { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = session.statement.genColumnStr() } @@ -110,7 +143,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } else { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = "*" } @@ -128,7 +161,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } args = append(session.statement.joinArgs, condArgs...) - sqlStr, err = session.statement.genSelectSQL(columnStr, condSQL) + sqlStr, err = session.statement.genSelectSQL(columnStr, condSQL, true, true) if err != nil { return err } @@ -143,7 +176,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } if session.canCache() { - if cacher := session.engine.getCacher2(table); cacher != nil && + if cacher := session.engine.getCacher(table.Name); cacher != nil && !session.statement.IsDistinct && !session.statement.unscoped { err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) @@ -288,6 +321,12 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return ErrCacheFailed } + tableName := session.statement.TableName() + cacher := session.engine.getCacher(tableName) + if cacher == nil { + return nil + } + for _, filter := range session.engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.engine.dialect, session.statement.RefTable) } @@ -297,9 +336,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return ErrCacheFailed } - tableName := session.statement.TableName() table := session.statement.RefTable - cacher := session.engine.getCacher2(table) ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { rows, err := session.queryRows(newsql, args...) diff --git a/vendor/github.com/go-xorm/xorm/session_get.go b/vendor/github.com/go-xorm/xorm/session_get.go index 8faf53c02c7..887a0aebdc8 100644 --- a/vendor/github.com/go-xorm/xorm/session_get.go +++ b/vendor/github.com/go-xorm/xorm/session_get.go @@ -5,7 +5,9 @@ package xorm import ( + "database/sql" "errors" + "fmt" "reflect" "strconv" @@ -30,7 +32,7 @@ func (session *Session) get(bean interface{}) (bool, error) { } if beanValue.Elem().Kind() == reflect.Struct { - if err := session.statement.setRefValue(beanValue.Elem()); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return false, err } } @@ -56,7 +58,7 @@ func (session *Session) get(bean interface{}) (bool, error) { table := session.statement.RefTable if session.canCache() && beanValue.Elem().Kind() == reflect.Struct { - if cacher := session.engine.getCacher2(table); cacher != nil && + if cacher := session.engine.getCacher(table.Name); cacher != nil && !session.statement.unscoped { has, err := session.cacheGet(bean, sqlStr, args...) if err != ErrCacheFailed { @@ -65,7 +67,28 @@ func (session *Session) get(bean interface{}) (bool, error) { } } - return session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...) + context := session.statement.context + if context != nil { + res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args)) + if res != nil { + structValue := reflect.Indirect(reflect.ValueOf(bean)) + structValue.Set(reflect.Indirect(reflect.ValueOf(res))) + session.lastSQL = "" + session.lastSQLArgs = nil + return true, nil + } + } + + has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...) + if err != nil || !has { + return has, err + } + + if context != nil { + context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean) + } + + return true, nil } func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) { @@ -76,9 +99,19 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea defer rows.Close() if !rows.Next() { + if rows.Err() != nil { + return false, rows.Err() + } return false, nil } + switch bean.(type) { + case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString: + return true, rows.Scan(&bean) + case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString: + return true, rows.Scan(bean) + } + switch beanKind { case reflect.Struct: fields, err := rows.Columns() @@ -126,8 +159,9 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, ErrCacheFailed } - cacher := session.engine.getCacher2(session.statement.RefTable) tableName := session.statement.TableName() + cacher := session.engine.getCacher(tableName) + session.engine.logger.Debug("[cacheGet] find sql:", newsql, args) table := session.statement.RefTable ids, err := core.GetCacheSql(cacher, tableName, newsql, args) diff --git a/vendor/github.com/go-xorm/xorm/session_insert.go b/vendor/github.com/go-xorm/xorm/session_insert.go index 129ee23098a..2ea58fdaf95 100644 --- a/vendor/github.com/go-xorm/xorm/session_insert.go +++ b/vendor/github.com/go-xorm/xorm/session_insert.go @@ -66,11 +66,12 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error return 0, errors.New("could not insert a empty slice") } - if err := session.statement.setRefValue(reflect.ValueOf(sliceValue.Index(0).Interface())); err != nil { + if err := session.statement.setRefBean(sliceValue.Index(0).Interface()); err != nil { return 0, err } - if len(session.statement.TableName()) <= 0 { + tableName := session.statement.TableName() + if len(tableName) <= 0 { return 0, ErrTableNotFound } @@ -115,15 +116,11 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.IsDeleted { continue } - if session.statement.ColumnStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } + if session.statement.omitColumnMap.contain(col.Name) { + continue } - if session.statement.OmitStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); ok { - continue - } + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { val, t := session.engine.nowTime(col) @@ -170,15 +167,11 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.IsDeleted { continue } - if session.statement.ColumnStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok { - continue - } + if session.statement.omitColumnMap.contain(col.Name) { + continue } - if session.statement.OmitStr != "" { - if _, ok := getFlagForColumn(session.statement.columnMap, col); ok { - continue - } + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { val, t := session.engine.nowTime(col) @@ -211,38 +204,33 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } cleanupProcessorsClosures(&session.beforeClosures) - var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)" - var statement string - var tableName = session.statement.TableName() + var sql string if session.engine.dialect.DBType() == core.ORACLE { - sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL" temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (", session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr()) - statement = fmt.Sprintf(sql, + sql = fmt.Sprintf("INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL", session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr(), strings.Join(colMultiPlaces, temp)) } else { - statement = fmt.Sprintf(sql, + sql = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr(), strings.Join(colMultiPlaces, "),(")) } - res, err := session.exec(statement, args...) + res, err := session.exec(sql, args...) if err != nil { return 0, err } - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) lenAfterClosures := len(session.afterClosures) for i := 0; i < size; i++ { @@ -298,7 +286,7 @@ func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { } func (session *Session) innerInsert(bean interface{}) (int64, error) { - if err := session.statement.setRefValue(rValue(bean)); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return 0, err } if len(session.statement.TableName()) <= 0 { @@ -316,8 +304,8 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok { processor.BeforeInsert() } - // -- - colNames, args, err := genCols(session.statement.RefTable, session, bean, false, false) + + colNames, args, err := session.genInsertColumns(bean) if err != nil { return 0, err } @@ -402,9 +390,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) if table.Version != "" && session.statement.checkVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -447,9 +433,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { } defer handleAfterInsertProcessorFunc(bean) - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) if table.Version != "" && session.statement.checkVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -490,9 +474,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(table, tableName) - } + session.cacheInsert(tableName) if table.Version != "" && session.statement.checkVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -539,16 +521,104 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) { return session.innerInsert(bean) } -func (session *Session) cacheInsert(table *core.Table, tables ...string) error { - if table == nil { - return ErrCacheFailed +func (session *Session) cacheInsert(table string) error { + if !session.statement.UseCache { + return nil } - - cacher := session.engine.getCacher2(table) - for _, t := range tables { - session.engine.logger.Debug("[cache] clear sql:", t) - cacher.ClearIds(t) + cacher := session.engine.getCacher(table) + if cacher == nil { + return nil } - + session.engine.logger.Debug("[cache] clear sql:", table) + cacher.ClearIds(table) return nil } + +// genInsertColumns generates insert needed columns +func (session *Session) genInsertColumns(bean interface{}) ([]string, []interface{}, error) { + table := session.statement.RefTable + colNames := make([]string, 0, len(table.ColumnsSeq())) + args := make([]interface{}, 0, len(table.ColumnsSeq())) + + for _, col := range table.Columns() { + if col.MapType == core.ONLYFROMDB { + continue + } + + if col.IsDeleted { + continue + } + + if session.statement.omitColumnMap.contain(col.Name) { + continue + } + + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue + } + + if _, ok := session.statement.incrColumns[col.Name]; ok { + continue + } else if _, ok := session.statement.decrColumns[col.Name]; ok { + continue + } + + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + return nil, nil, err + } + fieldValue := *fieldValuePtr + + if col.IsAutoIncrement { + switch fieldValue.Type().Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + case reflect.String: + if len(fieldValue.String()) == 0 { + continue + } + case reflect.Ptr: + if fieldValue.Pointer() == 0 { + continue + } + } + } + + // !evalphobia! set fieldValue as nil when column is nullable and zero-value + if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { + if col.Nullable && isZero(fieldValue.Interface()) { + var nilValue *int + fieldValue = reflect.ValueOf(nilValue) + } + } + + if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { + // if time is non-empty, then set to auto time + val, t := session.engine.nowTime(col) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.statement.checkVersion { + args = append(args, 1) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return colNames, args, err + } + args = append(args, arg) + } + + colNames = append(colNames, col.Name) + } + return colNames, args, nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_query.go b/vendor/github.com/go-xorm/xorm/session_query.go index 5b4e0dc45d0..6d597cc4592 100644 --- a/vendor/github.com/go-xorm/xorm/session_query.go +++ b/vendor/github.com/go-xorm/xorm/session_query.go @@ -17,7 +17,7 @@ import ( func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interface{}, error) { if len(sqlorArgs) > 0 { - return sqlorArgs[0].(string), sqlorArgs[1:], nil + return convertSQLOrArgs(sqlorArgs...) } if session.statement.RawSQL != "" { @@ -35,7 +35,7 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa if session.statement.JoinStr == "" { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = session.statement.genColumnStr() } @@ -43,7 +43,7 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa } else { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) + columnStr = session.engine.quoteColumns(session.statement.GroupByStr) } else { columnStr = "*" } @@ -54,13 +54,17 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa } } + if err := session.statement.processIDParam(); err != nil { + return "", nil, err + } + condSQL, condArgs, err := builder.ToSQL(session.statement.cond) if err != nil { return "", nil, err } args := append(session.statement.joinArgs, condArgs...) - sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL) + sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL, true, true) if err != nil { return "", nil, err } @@ -162,6 +166,34 @@ func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, return result, nil } +func row2sliceStr(rows *core.Rows, fields []string) (results []string, err error) { + result := make([]string, 0, len(fields)) + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var scanResultContainer interface{} + scanResultContainers[i] = &scanResultContainer + } + if err := rows.Scan(scanResultContainers...); err != nil { + return nil, err + } + + for i := 0; i < len(fields); i++ { + rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[i])) + // if row is null then as empty string + if rawValue.Interface() == nil { + result = append(result, "") + continue + } + + if data, err := value2String(&rawValue); err == nil { + result = append(result, data) + } else { + return nil, err + } + } + return result, nil +} + func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { fields, err := rows.Columns() if err != nil { @@ -178,6 +210,22 @@ func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) return resultsSlice, nil } +func rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + for rows.Next() { + record, err := row2sliceStr(rows, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, record) + } + + return resultsSlice, nil +} + // QueryString runs a raw sql and return records as []map[string]string func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { if session.isAutoClose { @@ -198,6 +246,26 @@ func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]stri return rows2Strings(rows) } +// QuerySliceString runs a raw sql and return records as [][]string +func (session *Session) QuerySliceString(sqlorArgs ...interface{}) ([][]string, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.genQuerySQL(sqlorArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return rows2SliceString(rows) +} + func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) { resultsMap = make(map[string]interface{}, len(fields)) scanResultContainers := make([]interface{}, len(fields)) diff --git a/vendor/github.com/go-xorm/xorm/session_raw.go b/vendor/github.com/go-xorm/xorm/session_raw.go index 69bf9b3c6bf..47823d67063 100644 --- a/vendor/github.com/go-xorm/xorm/session_raw.go +++ b/vendor/github.com/go-xorm/xorm/session_raw.go @@ -9,6 +9,7 @@ import ( "reflect" "time" + "github.com/go-xorm/builder" "github.com/go-xorm/core" ) @@ -193,11 +194,34 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er return session.DB().Exec(sqlStr, args...) } +func convertSQLOrArgs(sqlorArgs ...interface{}) (string, []interface{}, error) { + switch sqlorArgs[0].(type) { + case string: + return sqlorArgs[0].(string), sqlorArgs[1:], nil + case *builder.Builder: + return sqlorArgs[0].(*builder.Builder).ToSQL() + case builder.Builder: + bd := sqlorArgs[0].(builder.Builder) + return bd.ToSQL() + } + + return "", nil, ErrUnSupportedType +} + // Exec raw sql -func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { +func (session *Session) Exec(sqlorArgs ...interface{}) (sql.Result, error) { if session.isAutoClose { defer session.Close() } + if len(sqlorArgs) == 0 { + return nil, ErrUnSupportedType + } + + sqlStr, args, err := convertSQLOrArgs(sqlorArgs...) + if err != nil { + return nil, err + } + return session.exec(sqlStr, args...) } diff --git a/vendor/github.com/go-xorm/xorm/session_schema.go b/vendor/github.com/go-xorm/xorm/session_schema.go index a2708b736c0..369ec72a4d8 100644 --- a/vendor/github.com/go-xorm/xorm/session_schema.go +++ b/vendor/github.com/go-xorm/xorm/session_schema.go @@ -6,9 +6,7 @@ package xorm import ( "database/sql" - "errors" "fmt" - "reflect" "strings" "github.com/go-xorm/core" @@ -34,8 +32,7 @@ func (session *Session) CreateTable(bean interface{}) error { } func (session *Session) createTable(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -54,8 +51,7 @@ func (session *Session) CreateIndexes(bean interface{}) error { } func (session *Session) createIndexes(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -78,8 +74,7 @@ func (session *Session) CreateUniques(bean interface{}) error { } func (session *Session) createUniques(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -103,8 +98,7 @@ func (session *Session) DropIndexes(bean interface{}) error { } func (session *Session) dropIndexes(bean interface{}) error { - v := rValue(bean) - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return err } @@ -128,11 +122,7 @@ func (session *Session) DropTable(beanOrTableName interface{}) error { } func (session *Session) dropTable(beanOrTableName interface{}) error { - tableName, err := session.engine.tableName(beanOrTableName) - if err != nil { - return err - } - + tableName := session.engine.TableName(beanOrTableName) var needDrop = true if !session.engine.dialect.SupportDropIfExists() { sqlStr, args := session.engine.dialect.TableCheckSql(tableName) @@ -144,8 +134,8 @@ func (session *Session) dropTable(beanOrTableName interface{}) error { } if needDrop { - sqlStr := session.engine.Dialect().DropTableSql(tableName) - _, err = session.exec(sqlStr) + sqlStr := session.engine.Dialect().DropTableSql(session.engine.TableName(tableName, true)) + _, err := session.exec(sqlStr) return err } return nil @@ -157,10 +147,7 @@ func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) defer session.Close() } - tableName, err := session.engine.tableName(beanOrTableName) - if err != nil { - return false, err - } + tableName := session.engine.TableName(beanOrTableName) return session.isTableExist(tableName) } @@ -173,24 +160,15 @@ func (session *Session) isTableExist(tableName string) (bool, error) { // IsTableEmpty if table have any records func (session *Session) IsTableEmpty(bean interface{}) (bool, error) { - v := rValue(bean) - t := v.Type() - - if t.Kind() == reflect.String { - if session.isAutoClose { - defer session.Close() - } - return session.isTableEmpty(bean.(string)) - } else if t.Kind() == reflect.Struct { - rows, err := session.Count(bean) - return rows == 0, err + if session.isAutoClose { + defer session.Close() } - return false, errors.New("bean should be a struct or struct's point") + return session.isTableEmpty(session.engine.TableName(bean)) } func (session *Session) isTableEmpty(tableName string) (bool, error) { var total int64 - sqlStr := fmt.Sprintf("select count(*) from %s", session.engine.Quote(tableName)) + sqlStr := fmt.Sprintf("select count(*) from %s", session.engine.Quote(session.engine.TableName(tableName, true))) err := session.queryRow(sqlStr).Scan(&total) if err != nil { if err == sql.ErrNoRows { @@ -255,6 +233,12 @@ func (session *Session) Sync2(beans ...interface{}) error { return err } + session.autoResetStatement = false + defer func() { + session.autoResetStatement = true + session.resetStatement() + }() + var structTables []*core.Table for _, bean := range beans { @@ -264,7 +248,8 @@ func (session *Session) Sync2(beans ...interface{}) error { return err } structTables = append(structTables, table) - var tbName = session.tbNameNoSchema(table) + tbName := engine.TableName(bean) + tbNameWithSchema := engine.TableName(tbName, true) var oriTable *core.Table for _, tb := range tables { @@ -309,32 +294,32 @@ func (session *Session) Sync2(beans ...interface{}) error { if engine.dialect.DBType() == core.MYSQL || engine.dialect.DBType() == core.POSTGRES { engine.logger.Infof("Table %s column %s change type from %s to %s\n", - tbName, col.Name, curType, expectedType) - _, err = session.exec(engine.dialect.ModifyColumnSql(table.Name, col)) + tbNameWithSchema, col.Name, curType, expectedType) + _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) } else { engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", - tbName, col.Name, curType, expectedType) + tbNameWithSchema, col.Name, curType, expectedType) } } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { if engine.dialect.DBType() == core.MYSQL { if oriCol.Length < col.Length { engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - tbName, col.Name, oriCol.Length, col.Length) - _, err = session.exec(engine.dialect.ModifyColumnSql(table.Name, col)) + tbNameWithSchema, col.Name, oriCol.Length, col.Length) + _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) } } } else { if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", - tbName, col.Name, curType, expectedType) + tbNameWithSchema, col.Name, curType, expectedType) } } } else if expectedType == core.Varchar { if engine.dialect.DBType() == core.MYSQL { if oriCol.Length < col.Length { engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - tbName, col.Name, oriCol.Length, col.Length) - _, err = session.exec(engine.dialect.ModifyColumnSql(table.Name, col)) + tbNameWithSchema, col.Name, oriCol.Length, col.Length) + _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) } } } @@ -348,7 +333,7 @@ func (session *Session) Sync2(beans ...interface{}) error { } } else { session.statement.RefTable = table - session.statement.tableName = tbName + session.statement.tableName = tbNameWithSchema err = session.addColumn(col.Name) } if err != nil { @@ -371,7 +356,7 @@ func (session *Session) Sync2(beans ...interface{}) error { if oriIndex != nil { if oriIndex.Type != index.Type { - sql := engine.dialect.DropIndexSql(tbName, oriIndex) + sql := engine.dialect.DropIndexSql(tbNameWithSchema, oriIndex) _, err = session.exec(sql) if err != nil { return err @@ -387,7 +372,7 @@ func (session *Session) Sync2(beans ...interface{}) error { for name2, index2 := range oriTable.Indexes { if _, ok := foundIndexNames[name2]; !ok { - sql := engine.dialect.DropIndexSql(tbName, index2) + sql := engine.dialect.DropIndexSql(tbNameWithSchema, index2) _, err = session.exec(sql) if err != nil { return err @@ -398,12 +383,12 @@ func (session *Session) Sync2(beans ...interface{}) error { for name, index := range addedNames { if index.Type == core.UniqueType { session.statement.RefTable = table - session.statement.tableName = tbName - err = session.addUnique(tbName, name) + session.statement.tableName = tbNameWithSchema + err = session.addUnique(tbNameWithSchema, name) } else if index.Type == core.IndexType { session.statement.RefTable = table - session.statement.tableName = tbName - err = session.addIndex(tbName, name) + session.statement.tableName = tbNameWithSchema + err = session.addIndex(tbNameWithSchema, name) } if err != nil { return err @@ -428,7 +413,7 @@ func (session *Session) Sync2(beans ...interface{}) error { for _, colName := range table.ColumnsSeq() { if oriTable.GetColumn(colName) == nil { - engine.logger.Warnf("Table %s has column %s but struct has not related field", table.Name, colName) + engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(table.Name, true), colName) } } } diff --git a/vendor/github.com/go-xorm/xorm/session_tx.go b/vendor/github.com/go-xorm/xorm/session_tx.go index 84d2f7f9dcf..c8d759a31ac 100644 --- a/vendor/github.com/go-xorm/xorm/session_tx.go +++ b/vendor/github.com/go-xorm/xorm/session_tx.go @@ -24,6 +24,7 @@ func (session *Session) Rollback() error { if !session.isAutoCommit && !session.isCommitedOrRollbacked { session.saveLastSQL(session.engine.dialect.RollBackStr()) session.isCommitedOrRollbacked = true + session.isAutoCommit = true return session.tx.Rollback() } return nil @@ -34,6 +35,7 @@ func (session *Session) Commit() error { if !session.isAutoCommit && !session.isCommitedOrRollbacked { session.saveLastSQL("COMMIT") session.isCommitedOrRollbacked = true + session.isAutoCommit = true var err error if err = session.tx.Commit(); err == nil { // handle processors after tx committed diff --git a/vendor/github.com/go-xorm/xorm/session_update.go b/vendor/github.com/go-xorm/xorm/session_update.go index f558745667f..42dfaacd0c9 100644 --- a/vendor/github.com/go-xorm/xorm/session_update.go +++ b/vendor/github.com/go-xorm/xorm/session_update.go @@ -40,7 +40,7 @@ func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, } } - cacher := session.engine.getCacher2(table) + cacher := session.engine.getCacher(tableName) session.engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:]) ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) if err != nil { @@ -167,7 +167,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 var isMap = t.Kind() == reflect.Map var isStruct = t.Kind() == reflect.Struct if isStruct { - if err := session.statement.setRefValue(v); err != nil { + if err := session.statement.setRefBean(bean); err != nil { return 0, err } @@ -176,12 +176,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } if session.statement.ColumnStr == "" { - colNames, args = buildUpdates(session.engine, session.statement.RefTable, bean, false, false, - false, false, session.statement.allUseBool, session.statement.useAllCols, - session.statement.mustColumnMap, session.statement.nullableMap, - session.statement.columnMap, true, session.statement.unscoped) + colNames, args = session.statement.buildUpdates(bean, false, false, + false, false, true) } else { - colNames, args, err = genCols(session.statement.RefTable, session, bean, true, true) + colNames, args, err = session.genUpdateColumns(bean) if err != nil { return 0, err } @@ -202,7 +200,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 table := session.statement.RefTable if session.statement.UseAutoTime && table != nil && table.Updated != "" { - if _, ok := session.statement.columnMap[strings.ToLower(table.Updated)]; !ok { + if !session.statement.columnMap.contain(table.Updated) && + !session.statement.omitColumnMap.contain(table.Updated) { colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?") col := table.UpdatedColumn() val, t := session.engine.nowTime(col) @@ -362,12 +361,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - if table != nil { - if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - //session.cacheUpdate(table, tableName, sqlStr, args...) - cacher.ClearIds(tableName) - cacher.ClearBeans(tableName) - } + if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache { + //session.cacheUpdate(table, tableName, sqlStr, args...) + session.engine.logger.Debug("[cacheUpdate] clear table ", tableName) + cacher.ClearIds(tableName) + cacher.ClearBeans(tableName) } // handle after update processors @@ -402,3 +400,92 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return res.RowsAffected() } + +func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interface{}, error) { + table := session.statement.RefTable + colNames := make([]string, 0, len(table.ColumnsSeq())) + args := make([]interface{}, 0, len(table.ColumnsSeq())) + + for _, col := range table.Columns() { + if !col.IsVersion && !col.IsCreated && !col.IsUpdated { + if session.statement.omitColumnMap.contain(col.Name) { + continue + } + } + if col.MapType == core.ONLYFROMDB { + continue + } + + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + return nil, nil, err + } + fieldValue := *fieldValuePtr + + if col.IsAutoIncrement { + switch fieldValue.Type().Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + case reflect.String: + if len(fieldValue.String()) == 0 { + continue + } + case reflect.Ptr: + if fieldValue.Pointer() == 0 { + continue + } + } + } + + if (col.IsDeleted && !session.statement.unscoped) || col.IsCreated { + continue + } + + if len(session.statement.columnMap) > 0 { + if !session.statement.columnMap.contain(col.Name) { + continue + } else if _, ok := session.statement.incrColumns[col.Name]; ok { + continue + } else if _, ok := session.statement.decrColumns[col.Name]; ok { + continue + } + } + + // !evalphobia! set fieldValue as nil when column is nullable and zero-value + if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { + if col.Nullable && isZero(fieldValue.Interface()) { + var nilValue *int + fieldValue = reflect.ValueOf(nilValue) + } + } + + if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { + // if time is non-empty, then set to auto time + val, t := session.engine.nowTime(col) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.statement.checkVersion { + args = append(args, 1) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return colNames, args, err + } + args = append(args, arg) + } + + colNames = append(colNames, session.engine.Quote(col.Name)+" = ?") + } + return colNames, args, nil +} diff --git a/vendor/github.com/go-xorm/xorm/statement.go b/vendor/github.com/go-xorm/xorm/statement.go index 6400425b20e..a7f7010ad2b 100644 --- a/vendor/github.com/go-xorm/xorm/statement.go +++ b/vendor/github.com/go-xorm/xorm/statement.go @@ -5,7 +5,6 @@ package xorm import ( - "bytes" "database/sql/driver" "encoding/json" "errors" @@ -18,21 +17,6 @@ import ( "github.com/go-xorm/core" ) -type incrParam struct { - colName string - arg interface{} -} - -type decrParam struct { - colName string - arg interface{} -} - -type exprParam struct { - colName string - expr string -} - // Statement save all the sql info for executing SQL type Statement struct { RefTable *core.Table @@ -47,7 +31,6 @@ type Statement struct { HavingStr string ColumnStr string selectStr string - columnMap map[string]bool useAllCols bool OmitStr string AltTableName string @@ -67,6 +50,8 @@ type Statement struct { allUseBool bool checkVersion bool unscoped bool + columnMap columnMap + omitColumnMap columnMap mustColumnMap map[string]bool nullableMap map[string]bool incrColumns map[string]incrParam @@ -74,6 +59,7 @@ type Statement struct { exprColumns map[string]exprParam cond builder.Cond bufferSize int + context ContextCache } // Init reset all the statement's fields @@ -89,7 +75,8 @@ func (statement *Statement) Init() { statement.HavingStr = "" statement.ColumnStr = "" statement.OmitStr = "" - statement.columnMap = make(map[string]bool) + statement.columnMap = columnMap{} + statement.omitColumnMap = columnMap{} statement.AltTableName = "" statement.tableName = "" statement.idParam = nil @@ -113,6 +100,7 @@ func (statement *Statement) Init() { statement.exprColumns = make(map[string]exprParam) statement.cond = builder.NewCond() statement.bufferSize = 0 + statement.context = nil } // NoAutoCondition if you do not want convert bean's field as query condition, then use this function @@ -221,34 +209,33 @@ func (statement *Statement) setRefValue(v reflect.Value) error { if err != nil { return err } - statement.tableName = statement.Engine.tbName(v) + statement.tableName = statement.Engine.TableName(v, true) return nil } -// Table tempororily set table name, the parameter could be a string or a pointer of struct -func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { - v := rValue(tableNameOrBean) - t := v.Type() - if t.Kind() == reflect.String { - statement.AltTableName = tableNameOrBean.(string) - } else if t.Kind() == reflect.Struct { - var err error - statement.RefTable, err = statement.Engine.autoMapType(v) - if err != nil { - statement.Engine.logger.Error(err) - return statement - } - statement.AltTableName = statement.Engine.tbName(v) +func (statement *Statement) setRefBean(bean interface{}) error { + var err error + statement.RefTable, err = statement.Engine.autoMapType(rValue(bean)) + if err != nil { + return err } - return statement + statement.tableName = statement.Engine.TableName(bean, true) + return nil } // Auto generating update columnes and values according a struct -func buildUpdates(engine *Engine, table *core.Table, bean interface{}, - includeVersion bool, includeUpdated bool, includeNil bool, - includeAutoIncr bool, allUseBool bool, useAllCols bool, - mustColumnMap map[string]bool, nullableMap map[string]bool, - columnMap map[string]bool, update, unscoped bool) ([]string, []interface{}) { +func (statement *Statement) buildUpdates(bean interface{}, + includeVersion, includeUpdated, includeNil, + includeAutoIncr, update bool) ([]string, []interface{}) { + engine := statement.Engine + table := statement.RefTable + allUseBool := statement.allUseBool + useAllCols := statement.useAllCols + mustColumnMap := statement.mustColumnMap + nullableMap := statement.nullableMap + columnMap := statement.columnMap + omitColumnMap := statement.omitColumnMap + unscoped := statement.unscoped var colNames = make([]string, 0) var args = make([]interface{}, 0) @@ -268,7 +255,14 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if col.IsDeleted && !unscoped { continue } - if use, ok := columnMap[strings.ToLower(col.Name)]; ok && !use { + if omitColumnMap.contain(col.Name) { + continue + } + if len(columnMap) > 0 && !columnMap.contain(col.Name) { + continue + } + + if col.MapType == core.ONLYFROMDB { continue } @@ -604,17 +598,10 @@ func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { } func (statement *Statement) colmap2NewColsWithQuote() []string { - newColumns := make([]string, 0, len(statement.columnMap)) - for col := range statement.columnMap { - fields := strings.Split(strings.TrimSpace(col), ".") - if len(fields) == 1 { - newColumns = append(newColumns, statement.Engine.quote(fields[0])) - } else if len(fields) == 2 { - newColumns = append(newColumns, statement.Engine.quote(fields[0])+"."+ - statement.Engine.quote(fields[1])) - } else { - panic(errors.New("unwanted colnames")) - } + newColumns := make([]string, len(statement.columnMap), len(statement.columnMap)) + copy(newColumns, statement.columnMap) + for i := 0; i < len(statement.columnMap); i++ { + newColumns[i] = statement.Engine.Quote(newColumns[i]) } return newColumns } @@ -642,10 +629,11 @@ func (statement *Statement) Select(str string) *Statement { func (statement *Statement) Cols(columns ...string) *Statement { cols := col2NewCols(columns...) for _, nc := range cols { - statement.columnMap[strings.ToLower(nc)] = true + statement.columnMap.add(nc) } newColumns := statement.colmap2NewColsWithQuote() + statement.ColumnStr = strings.Join(newColumns, ", ") statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1) return statement @@ -680,7 +668,7 @@ func (statement *Statement) UseBool(columns ...string) *Statement { func (statement *Statement) Omit(columns ...string) { newColumns := col2NewCols(columns...) for _, nc := range newColumns { - statement.columnMap[strings.ToLower(nc)] = false + statement.omitColumnMap = append(statement.omitColumnMap, nc) } statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) } @@ -719,10 +707,9 @@ func (statement *Statement) OrderBy(order string) *Statement { // Desc generate `ORDER BY xx DESC` func (statement *Statement) Desc(colNames ...string) *Statement { - var buf bytes.Buffer - fmt.Fprintf(&buf, statement.OrderStr) + var buf builder.StringBuilder if len(statement.OrderStr) > 0 { - fmt.Fprint(&buf, ", ") + fmt.Fprint(&buf, statement.OrderStr, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, ")) @@ -732,10 +719,9 @@ func (statement *Statement) Desc(colNames ...string) *Statement { // Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { - var buf bytes.Buffer - fmt.Fprintf(&buf, statement.OrderStr) + var buf builder.StringBuilder if len(statement.OrderStr) > 0 { - fmt.Fprint(&buf, ", ") + fmt.Fprint(&buf, statement.OrderStr, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, ")) @@ -743,48 +729,35 @@ func (statement *Statement) Asc(colNames ...string) *Statement { return statement } +// Table tempororily set table name, the parameter could be a string or a pointer of struct +func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { + v := rValue(tableNameOrBean) + t := v.Type() + if t.Kind() == reflect.Struct { + var err error + statement.RefTable, err = statement.Engine.autoMapType(v) + if err != nil { + statement.Engine.logger.Error(err) + return statement + } + } + + statement.AltTableName = statement.Engine.TableName(tableNameOrBean, true) + return statement +} + // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { - var buf bytes.Buffer + var buf builder.StringBuilder if len(statement.JoinStr) > 0 { fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) } else { fmt.Fprintf(&buf, "%v JOIN ", joinOP) } - switch tablename.(type) { - case []string: - t := tablename.([]string) - if len(t) > 1 { - fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1])) - } else if len(t) == 1 { - fmt.Fprintf(&buf, statement.Engine.Quote(t[0])) - } - case []interface{}: - t := tablename.([]interface{}) - l := len(t) - var table string - if l > 0 { - f := t[0] - v := rValue(f) - t := v.Type() - if t.Kind() == reflect.String { - table = f.(string) - } else if t.Kind() == reflect.Struct { - table = statement.Engine.tbName(v) - } - } - if l > 1 { - fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(table), - statement.Engine.Quote(fmt.Sprintf("%v", t[1]))) - } else if l == 1 { - fmt.Fprintf(&buf, statement.Engine.Quote(table)) - } - default: - fmt.Fprintf(&buf, statement.Engine.Quote(fmt.Sprintf("%v", tablename))) - } + tbName := statement.Engine.TableName(tablename, true) - fmt.Fprintf(&buf, " ON %v", condition) + fmt.Fprintf(&buf, "%s ON %v", tbName, condition) statement.JoinStr = buf.String() statement.joinArgs = append(statement.joinArgs, args...) return statement @@ -809,18 +782,20 @@ func (statement *Statement) Unscoped() *Statement { } func (statement *Statement) genColumnStr() string { - var buf bytes.Buffer if statement.RefTable == nil { return "" } + var buf builder.StringBuilder columns := statement.RefTable.Columns() for _, col := range columns { - if statement.OmitStr != "" { - if _, ok := getFlagForColumn(statement.columnMap, col); ok { - continue - } + if statement.omitColumnMap.contain(col.Name) { + continue + } + + if len(statement.columnMap) > 0 && !statement.columnMap.contain(col.Name) { + continue } if col.MapType == core.ONLYTODB { @@ -831,10 +806,6 @@ func (statement *Statement) genColumnStr() string { buf.WriteString(", ") } - if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" { - buf.WriteString("id() AS ") - } - if statement.JoinStr != "" { if statement.TableAlias != "" { buf.WriteString(statement.TableAlias) @@ -859,11 +830,13 @@ func (statement *Statement) genCreateTableSQL() string { func (statement *Statement) genIndexSQL() []string { var sqls []string tbName := statement.TableName() - quote := statement.Engine.Quote - for idxName, index := range statement.RefTable.Indexes { + for _, index := range statement.RefTable.Indexes { if index.Type == core.IndexType { - sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(tbName, idxName)), - quote(tbName), quote(strings.Join(index.Cols, quote(",")))) + sql := statement.Engine.dialect.CreateIndexSql(tbName, index) + /*idxTBName := strings.Replace(tbName, ".", "_", -1) + idxTBName = strings.Replace(idxTBName, `"`, "", -1) + sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(idxTBName, idxName)), + quote(tbName), quote(strings.Join(index.Cols, quote(","))))*/ sqls = append(sqls, sql) } } @@ -889,16 +862,18 @@ func (statement *Statement) genUniqueSQL() []string { func (statement *Statement) genDelIndexSQL() []string { var sqls []string tbName := statement.TableName() + idxPrefixName := strings.Replace(tbName, `"`, "", -1) + idxPrefixName = strings.Replace(idxPrefixName, `.`, "_", -1) for idxName, index := range statement.RefTable.Indexes { var rIdxName string if index.Type == core.UniqueType { - rIdxName = uniqueName(tbName, idxName) + rIdxName = uniqueName(idxPrefixName, idxName) } else if index.Type == core.IndexType { - rIdxName = indexName(tbName, idxName) + rIdxName = indexName(idxPrefixName, idxName) } - sql := fmt.Sprintf("DROP INDEX %v", statement.Engine.Quote(rIdxName)) + sql := fmt.Sprintf("DROP INDEX %v", statement.Engine.Quote(statement.Engine.TableName(rIdxName, true))) if statement.Engine.dialect.IndexOnTable() { - sql += fmt.Sprintf(" ON %v", statement.Engine.Quote(statement.TableName())) + sql += fmt.Sprintf(" ON %v", statement.Engine.Quote(tbName)) } sqls = append(sqls, sql) } @@ -949,7 +924,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, v := rValue(bean) isStruct := v.Kind() == reflect.Struct if isStruct { - statement.setRefValue(v) + statement.setRefBean(bean) } var columnStr = statement.ColumnStr @@ -960,7 +935,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, if len(statement.JoinStr) == 0 { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { - columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + columnStr = statement.Engine.quoteColumns(statement.GroupByStr) } else { columnStr = statement.genColumnStr() } @@ -968,7 +943,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, } else { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { - columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + columnStr = statement.Engine.quoteColumns(statement.GroupByStr) } } } @@ -982,13 +957,17 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, if err := statement.mergeConds(bean); err != nil { return "", nil, err } + } else { + if err := statement.processIDParam(); err != nil { + return "", nil, err + } } condSQL, condArgs, err := builder.ToSQL(statement.cond) if err != nil { return "", nil, err } - sqlStr, err := statement.genSelectSQL(columnStr, condSQL) + sqlStr, err := statement.genSelectSQL(columnStr, condSQL, true, true) if err != nil { return "", nil, err } @@ -1001,7 +980,7 @@ func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interfa var condArgs []interface{} var err error if len(beans) > 0 { - statement.setRefValue(rValue(beans[0])) + statement.setRefBean(beans[0]) condSQL, condArgs, err = statement.genConds(beans[0]) } else { condSQL, condArgs, err = builder.ToSQL(statement.cond) @@ -1018,7 +997,7 @@ func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interfa selectSQL = "count(*)" } } - sqlStr, err := statement.genSelectSQL(selectSQL, condSQL) + sqlStr, err := statement.genSelectSQL(selectSQL, condSQL, false, false) if err != nil { return "", nil, err } @@ -1027,7 +1006,7 @@ func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interfa } func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (string, []interface{}, error) { - statement.setRefValue(rValue(bean)) + statement.setRefBean(bean) var sumStrs = make([]string, 0, len(columns)) for _, colName := range columns { @@ -1043,7 +1022,7 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri return "", nil, err } - sqlStr, err := statement.genSelectSQL(sumSelect, condSQL) + sqlStr, err := statement.genSelectSQL(sumSelect, condSQL, true, true) if err != nil { return "", nil, err } @@ -1051,27 +1030,20 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri return sqlStr, append(statement.joinArgs, condArgs...), nil } -func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string, err error) { - var distinct string +func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, needOrderBy bool) (string, error) { + var ( + distinct string + dialect = statement.Engine.Dialect() + quote = statement.Engine.Quote + fromStr = " FROM " + top, mssqlCondi, whereStr string + ) if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { distinct = "DISTINCT " } - - var dialect = statement.Engine.Dialect() - var quote = statement.Engine.Quote - var top string - var mssqlCondi string - - if err := statement.processIDParam(); err != nil { - return "", err - } - - var buf bytes.Buffer if len(condSQL) > 0 { - fmt.Fprintf(&buf, " WHERE %v", condSQL) + whereStr = " WHERE " + condSQL } - var whereStr = buf.String() - var fromStr = " FROM " if dialect.DBType() == core.MSSQL && strings.Contains(statement.TableName(), "..") { fromStr += statement.TableName() @@ -1118,9 +1090,10 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string, e } var orderStr string - if len(statement.OrderStr) > 0 { + if needOrderBy && len(statement.OrderStr) > 0 { orderStr = " ORDER BY " + statement.OrderStr } + var groupStr string if len(statement.GroupByStr) > 0 { groupStr = " GROUP BY " + statement.GroupByStr @@ -1130,45 +1103,50 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string, e } } - // !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern - a = fmt.Sprintf("SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) + var buf builder.StringBuilder + fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) if len(mssqlCondi) > 0 { if len(whereStr) > 0 { - a += " AND " + mssqlCondi + fmt.Fprint(&buf, " AND ", mssqlCondi) } else { - a += " WHERE " + mssqlCondi + fmt.Fprint(&buf, " WHERE ", mssqlCondi) } } if statement.GroupByStr != "" { - a = fmt.Sprintf("%v GROUP BY %v", a, statement.GroupByStr) + fmt.Fprint(&buf, " GROUP BY ", statement.GroupByStr) } if statement.HavingStr != "" { - a = fmt.Sprintf("%v %v", a, statement.HavingStr) + fmt.Fprint(&buf, " ", statement.HavingStr) } - if statement.OrderStr != "" { - a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) + if needOrderBy && statement.OrderStr != "" { + fmt.Fprint(&buf, " ORDER BY ", statement.OrderStr) } - if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE { - if statement.Start > 0 { - a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) - } else if statement.LimitN > 0 { - a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) - } - } else if dialect.DBType() == core.ORACLE { - if statement.Start != 0 || statement.LimitN != 0 { - a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) + if needLimit { + if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE { + if statement.Start > 0 { + fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", statement.LimitN, statement.Start) + } else if statement.LimitN > 0 { + fmt.Fprint(&buf, " LIMIT ", statement.LimitN) + } + } else if dialect.DBType() == core.ORACLE { + if statement.Start != 0 || statement.LimitN != 0 { + oldString := buf.String() + buf.Reset() + fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", + columnStr, columnStr, oldString, statement.Start+statement.LimitN, statement.Start) + } } } if statement.IsForUpdate { - a = dialect.ForUpdateSql(a) + return dialect.ForUpdateSql(buf.String()), nil } - return + return buf.String(), nil } func (statement *Statement) processIDParam() error { - if statement.idParam == nil { + if statement.idParam == nil || statement.RefTable == nil { return nil } diff --git a/vendor/github.com/go-xorm/xorm/transaction.go b/vendor/github.com/go-xorm/xorm/transaction.go new file mode 100644 index 00000000000..4104103fd53 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/transaction.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +// Transaction Execute sql wrapped in a transaction(abbr as tx), tx will automatic commit if no errors occurred +func (engine *Engine) Transaction(f func(*Session) (interface{}, error)) (interface{}, error) { + session := engine.NewSession() + defer session.Close() + + if err := session.Begin(); err != nil { + return nil, err + } + + result, err := f(session) + if err != nil { + return nil, err + } + + if err := session.Commit(); err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/go-xorm/xorm/xorm.go b/vendor/github.com/go-xorm/xorm/xorm.go index 4fdadf2fade..739de8d4292 100644 --- a/vendor/github.com/go-xorm/xorm/xorm.go +++ b/vendor/github.com/go-xorm/xorm/xorm.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.8 + package xorm import ( @@ -17,7 +19,7 @@ import ( const ( // Version show the xorm's version - Version string = "0.6.4.0910" + Version string = "0.7.0.0504" ) func regDrvsNDialects() bool { @@ -31,7 +33,7 @@ func regDrvsNDialects() bool { "mysql": {"mysql", func() core.Driver { return &mysqlDriver{} }, func() core.Dialect { return &mysql{} }}, "mymysql": {"mysql", func() core.Driver { return &mymysqlDriver{} }, func() core.Dialect { return &mysql{} }}, "postgres": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, - "pgx": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, + "pgx": {"postgres", func() core.Driver { return &pqDriverPgx{} }, func() core.Dialect { return &postgres{} }}, "sqlite3": {"sqlite3", func() core.Driver { return &sqlite3Driver{} }, func() core.Dialect { return &sqlite3{} }}, "oci8": {"oracle", func() core.Driver { return &oci8Driver{} }, func() core.Dialect { return &oracle{} }}, "goracle": {"oracle", func() core.Driver { return &goracleDriver{} }, func() core.Dialect { return &oracle{} }}, @@ -90,6 +92,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { TagIdentifier: "xorm", TZLocation: time.Local, tagHandlers: defaultTagHandlers, + cachers: make(map[string]core.Cacher), } if uri.DbType == core.SQLITE { @@ -108,6 +111,13 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return engine, nil } +// NewEngineWithParams new a db manager with params. The params will be passed to dialect. +func NewEngineWithParams(driverName string, dataSourceName string, params map[string]string) (*Engine, error) { + engine, err := NewEngine(driverName, dataSourceName) + engine.dialect.SetParams(params) + return engine, err +} + // Clone clone an engine func (engine *Engine) Clone() (*Engine, error) { return NewEngine(engine.DriverName(), engine.DataSourceName()) diff --git a/vendor/github.com/jmespath/go-jmespath/api.go b/vendor/github.com/jmespath/go-jmespath/api.go index 9cfa988bc5b..8e26ffeecff 100644 --- a/vendor/github.com/jmespath/go-jmespath/api.go +++ b/vendor/github.com/jmespath/go-jmespath/api.go @@ -2,7 +2,7 @@ package jmespath import "strconv" -// JmesPath is the epresentation of a compiled JMES path query. A JmesPath is +// JMESPath is the epresentation of a compiled JMES path query. A JMESPath is // safe for concurrent use by multiple goroutines. type JMESPath struct { ast ASTNode From 18999df716df44337406bb6c47157be4527e61cb Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 21:12:21 +0100 Subject: [PATCH 09/40] Ensuring master branch when performing release --- scripts/cli/tasks/grafanaui.release.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index b363ace35e0..0a36a00bca7 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -83,7 +83,19 @@ const publishPackage = (name: string, version: string) => await execa('npm', ['publish', '--access', 'public']); })(); +const ensureMasterBranch = async () => { + const currentBranch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']); + const status = await execa.stdout('git', ['status', '--porcelain']); + console.log(status === ''); + + if (currentBranch !== 'master' && status !== '') { + console.error(chalk.red.bold('You need to be on clean master branch to release @grafana/ui')); + process.exit(1); + } +}; + const releaseTaskRunner: TaskRunner = async ({ publishToNpm }) => { + await ensureMasterBranch(); await execTask(buildTask)(); let releaseConfirmed = false; From b816b4e2596b3e612d6bc4837ddb7f09f95eea2d Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 21:16:34 +0100 Subject: [PATCH 10/40] Remove log --- scripts/cli/tasks/grafanaui.release.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index 0a36a00bca7..2ebd2162e5b 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -86,7 +86,6 @@ const publishPackage = (name: string, version: string) => const ensureMasterBranch = async () => { const currentBranch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']); const status = await execa.stdout('git', ['status', '--porcelain']); - console.log(status === ''); if (currentBranch !== 'master' && status !== '') { console.error(chalk.red.bold('You need to be on clean master branch to release @grafana/ui')); From 358f9cbeaeebc5a52ee3a770bd8fcd4a2b40ecdd Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Mar 2019 21:18:26 +0100 Subject: [PATCH 11/40] Ensure clean master only when publishing package to npm --- scripts/cli/tasks/grafanaui.release.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/cli/tasks/grafanaui.release.ts b/scripts/cli/tasks/grafanaui.release.ts index 2ebd2162e5b..ef3430eaf9e 100644 --- a/scripts/cli/tasks/grafanaui.release.ts +++ b/scripts/cli/tasks/grafanaui.release.ts @@ -94,7 +94,10 @@ const ensureMasterBranch = async () => { }; const releaseTaskRunner: TaskRunner = async ({ publishToNpm }) => { - await ensureMasterBranch(); + if (publishToNpm) { + await ensureMasterBranch(); + } + await execTask(buildTask)(); let releaseConfirmed = false; From f5aba3681485c0b56294fb3a75a7b1bd4537efae Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 21:56:52 -0800 Subject: [PATCH 12/40] add ScopedVars to replace function --- packages/grafana-ui/src/types/panel.ts | 3 +- .../dashboard/dashgrid/PanelChrome.test.tsx | 35 +++++++++++++++++++ .../dashboard/dashgrid/PanelChrome.tsx | 10 ++++-- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 public/app/features/dashboard/dashgrid/PanelChrome.test.tsx diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index ae205100c13..260ff78df76 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -1,8 +1,9 @@ import { ComponentClass } from 'react'; import { TimeSeries, LoadingState, TableData } from './data'; import { TimeRange } from './time'; +import { ScopedVars } from './datasource'; -export type InterpolateFunction = (value: string, format?: string | Function) => string; +export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string; export interface PanelProps { panelData: PanelData; diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx new file mode 100644 index 00000000000..d6242b9db22 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx @@ -0,0 +1,35 @@ +import { PanelChrome } from './PanelChrome'; + +jest.mock('sass/_variables.generated.scss', () => ({ + panelhorizontalpadding: 10, + panelVerticalPadding: 10, +})); + +describe('PanelChrome', () => { + let chrome: PanelChrome; + + beforeEach(() => { + chrome = new PanelChrome({ + panel: { + scopedVars: { + aaa: { value: 'AAA', text: 'upperA' }, + bbb: { value: 'BBB', text: 'upperB' }, + }, + }, + dashboard: {}, + plugin: {}, + isFullscreen: false, + }); + }); + + it('Should replace a panel variable', () => { + const out = chrome.replaceVariables('hello $aaa'); + expect(out).toBe('hello AAA'); + }); + + it('It should prefer the diret variables', () => { + const extra = { aaa: { text: '???', value: 'XXX' } }; + const out = chrome.replaceVariables('hello $aaa and $bbb', extra); + expect(out).toBe('hello XXX and BBB'); + }); +}); diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 80ce2f39b70..149d0f3deee 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -19,6 +19,7 @@ import { profiler } from 'app/core/profiler'; import { DashboardModel, PanelModel } from '../state'; import { PanelPlugin } from 'app/types'; import { DataQueryResponse, TimeRange, LoadingState, PanelData, DataQueryError } from '@grafana/ui'; +import { ScopedVars } from '@grafana/ui'; import variables from 'sass/_variables.generated.scss'; import templateSrv from 'app/features/templating/template_srv'; @@ -85,8 +86,13 @@ export class PanelChrome extends PureComponent { }); }; - replaceVariables = (value: string, format?: string) => { - return templateSrv.replace(value, this.props.panel.scopedVars, format); + replaceVariables = (value: string, extraVars?: ScopedVars, format?: string) => { + let vars = this.props.panel.scopedVars; + if (extraVars) { + vars = vars ? { ...vars, ...extraVars } : extraVars; + } + console.log('VARiables', vars); + return templateSrv.replace(value, vars, format); }; onDataResponse = (dataQueryResponse: DataQueryResponse) => { From 948729e951978d7ecae1970b7ece2712bb689e77 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 22:00:12 -0800 Subject: [PATCH 13/40] remove console.log --- public/app/features/dashboard/dashgrid/PanelChrome.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 149d0f3deee..0a9d1d44ceb 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -91,7 +91,6 @@ export class PanelChrome extends PureComponent { if (extraVars) { vars = vars ? { ...vars, ...extraVars } : extraVars; } - console.log('VARiables', vars); return templateSrv.replace(value, vars, format); }; From aa38a9e0b49c17e37b0b246a558fe791457dd89e Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 22:14:28 -0800 Subject: [PATCH 14/40] typescript functions on replace --- public/app/features/templating/template_srv.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index 11e90cbb5f7..e0d35295556 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -1,7 +1,7 @@ import kbn from 'app/core/utils/kbn'; import _ from 'lodash'; import { variableRegex } from 'app/features/templating/variable'; -import { TimeRange } from '@grafana/ui/src'; +import { TimeRange, ScopedVars } from '@grafana/ui/src'; function luceneEscape(value) { return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1'); @@ -220,7 +220,7 @@ export class TemplateSrv { return values; } - replace(target, scopedVars?, format?) { + replace(target: string, scopedVars?: ScopedVars, format?: string | Function) { if (!target) { return target; } From 3dd7d407183c5d0e574d96d7f46a2290e983976b Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 22:18:42 -0800 Subject: [PATCH 15/40] fix comments --- public/app/features/dashboard/dashgrid/PanelChrome.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx index d6242b9db22..7136a14a907 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.test.tsx @@ -27,7 +27,7 @@ describe('PanelChrome', () => { expect(out).toBe('hello AAA'); }); - it('It should prefer the diret variables', () => { + it('But it should prefer the local variable value', () => { const extra = { aaa: { text: '???', value: 'XXX' } }; const out = chrome.replaceVariables('hello $aaa and $bbb', extra); expect(out).toBe('hello XXX and BBB'); From 5524dacc9d28a601745f9b44a12cf60347e3f07e Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 23:00:43 -0800 Subject: [PATCH 16/40] use explore icon --- public/app/features/panel/metrics_panel_ctrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index ceebfd82335..028585ae21e 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -224,7 +224,7 @@ class MetricsPanelCtrl extends PanelCtrl { items.push({ text: 'Explore', click: 'ctrl.explore();', - icon: 'fa fa-fw fa-rocket', + icon: 'gicon gicon-explore', shortcut: 'x', }); } From 909d425008a74c93ea2fb58f5a4a69a7205b4e40 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 5 Mar 2019 23:40:03 -0800 Subject: [PATCH 17/40] cleanup plugin versions --- public/app/features/plugins/partials/plugin_edit.html | 4 ++-- public/app/plugins/datasource/cloudwatch/plugin.json | 3 +-- public/app/plugins/datasource/elasticsearch/plugin.json | 5 +---- public/app/plugins/datasource/graphite/plugin.json | 3 +-- public/app/plugins/datasource/influxdb/plugin.json | 3 +-- public/app/plugins/datasource/mysql/plugin.json | 3 +-- public/app/plugins/datasource/opentsdb/plugin.json | 3 +-- public/app/plugins/datasource/postgres/plugin.json | 3 +-- public/app/plugins/datasource/prometheus/plugin.json | 3 +-- public/app/plugins/panel/alertlist/plugin.json | 3 +-- public/app/plugins/panel/dashlist/plugin.json | 3 +-- public/app/plugins/panel/graph/plugin.json | 3 +-- public/app/plugins/panel/heatmap/plugin.json | 3 +-- public/app/plugins/panel/pluginlist/plugin.json | 3 +-- public/app/plugins/panel/singlestat/plugin.json | 3 +-- public/app/plugins/panel/table/plugin.json | 3 +-- public/app/plugins/panel/text/plugin.json | 3 +-- 17 files changed, 18 insertions(+), 36 deletions(-) diff --git a/public/app/features/plugins/partials/plugin_edit.html b/public/app/features/plugins/partials/plugin_edit.html index 16cdfc1d1b2..d84196c47b0 100644 --- a/public/app/features/plugins/partials/plugin_edit.html +++ b/public/app/features/plugins/partials/plugin_edit.html @@ -25,7 +25,7 @@