Selaa lähdekoodia

Merge branch 'master' into 1271_share_zero

Marcus Efraimsson 7 vuotta sitten
vanhempi
commit
2e8aeffc13
100 muutettua tiedostoa jossa 2738 lisäystä ja 1786 poistoa
  1. 5 1
      CHANGELOG.md
  2. 5 4
      Gopkg.lock
  3. 2 4
      Gopkg.toml
  4. 6 0
      conf/defaults.ini
  5. 3 0
      conf/sample.ini
  6. 8 2
      docs/sources/administration/provisioning.md
  7. 5 0
      docs/sources/installation/configuration.md
  8. 68 1
      docs/sources/reference/templating.md
  9. 1 1
      emails/templates/layouts/default.html
  10. 7 7
      pkg/api/admin_users.go
  11. 3 3
      pkg/api/alerting.go
  12. 17 17
      pkg/api/annotations.go
  13. 19 19
      pkg/api/api.go
  14. 3 3
      pkg/api/apikey.go
  15. 2 2
      pkg/api/app_routes.go
  16. 1 1
      pkg/api/common_test.go
  17. 18 21
      pkg/api/dashboard.go
  18. 8 8
      pkg/api/dashboard_permission.go
  19. 2 2
      pkg/api/dashboard_snapshot.go
  20. 10 10
      pkg/api/dashboard_test.go
  21. 4 4
      pkg/api/dataproxy.go
  22. 12 13
      pkg/api/datasources.go
  23. 2 2
      pkg/api/folder.go
  24. 2 2
      pkg/api/folder_test.go
  25. 2 2
      pkg/api/http_server.go
  26. 15 14
      pkg/api/index.go
  27. 3 3
      pkg/api/login.go
  28. 3 3
      pkg/api/metrics.go
  29. 6 6
      pkg/api/org.go
  30. 15 16
      pkg/api/org_invite.go
  31. 9 9
      pkg/api/org_users.go
  32. 2 2
      pkg/api/playlist.go
  33. 27 29
      pkg/api/playlist_play.go
  34. 43 41
      pkg/api/plugins.go
  35. 5 5
      pkg/api/preferences.go
  36. 8 8
      pkg/api/search.go
  37. 2 2
      pkg/api/team.go
  38. 18 18
      pkg/api/user.go
  39. 1 1
      pkg/cmd/grafana-server/server.go
  40. 3 3
      pkg/middleware/auth.go
  41. 1 1
      pkg/middleware/dashboard_redirect.go
  42. 9 9
      pkg/middleware/dashboard_redirect_test.go
  43. 1 1
      pkg/middleware/middleware_test.go
  44. 7 7
      pkg/middleware/recovery_test.go
  45. 2 2
      pkg/middleware/session.go
  46. 26 3
      pkg/services/session/mysql.go
  47. 3 2
      pkg/services/session/session.go
  48. 1 1
      pkg/services/sqlstore/alert.go
  49. 1 0
      pkg/services/sqlstore/dashboard_folder_test.go
  50. 1 1
      pkg/services/sqlstore/migrator/migrator.go
  51. 3 0
      pkg/services/sqlstore/org_test.go
  52. 9 4
      pkg/services/sqlstore/quota.go
  53. 2 2
      pkg/services/sqlstore/quota_test.go
  54. 20 12
      pkg/services/sqlstore/sqlstore.go
  55. 4 1
      pkg/setting/setting.go
  56. 5 1
      public/app/core/components/code_editor/code_editor.ts
  57. 8 0
      public/app/core/services/keybindingSrv.ts
  58. 4 1
      public/app/features/dashboard/specs/viewstate_srv_specs.ts
  59. 21 1
      public/app/features/dashboard/view_state_srv.ts
  60. 5 2
      public/app/features/panel/metrics_panel_ctrl.ts
  61. 0 155
      public/app/plugins/datasource/graphite/add_graphite_func.js
  62. 159 0
      public/app/plugins/datasource/graphite/add_graphite_func.ts
  63. 0 309
      public/app/plugins/datasource/graphite/func_editor.js
  64. 317 0
      public/app/plugins/datasource/graphite/func_editor.ts
  65. 64 67
      public/app/plugins/datasource/mssql/specs/datasource_specs.ts
  66. 3 6
      public/app/plugins/datasource/prometheus/specs/completer_specs.ts
  67. 6 2
      public/app/stores/ViewStore/ViewStore.ts
  68. BIN
      public/img/kibana.png
  69. BIN
      public/img/small.png
  70. 4 1
      vendor/github.com/go-xorm/core/column.go
  71. 3 0
      vendor/github.com/go-xorm/core/dialect.go
  72. 12 0
      vendor/github.com/go-xorm/core/rows.go
  73. 1 0
      vendor/github.com/go-xorm/core/table.go
  74. 3 2
      vendor/github.com/go-xorm/core/type.go
  75. 12 22
      vendor/github.com/go-xorm/xorm/cache_lru.go
  76. 0 0
      vendor/github.com/go-xorm/xorm/cache_memory_store.go
  77. 26 0
      vendor/github.com/go-xorm/xorm/context.go
  78. 102 3
      vendor/github.com/go-xorm/xorm/convert.go
  79. 21 10
      vendor/github.com/go-xorm/xorm/dialect_mssql.go
  80. 8 6
      vendor/github.com/go-xorm/xorm/dialect_mysql.go
  81. 7 0
      vendor/github.com/go-xorm/xorm/dialect_oracle.go
  82. 24 47
      vendor/github.com/go-xorm/xorm/dialect_postgres.go
  83. 17 4
      vendor/github.com/go-xorm/xorm/dialect_sqlite3.go
  84. 10 3
      vendor/github.com/go-xorm/xorm/doc.go
  85. 284 210
      vendor/github.com/go-xorm/xorm/engine.go
  86. 230 0
      vendor/github.com/go-xorm/xorm/engine_cond.go
  87. 194 0
      vendor/github.com/go-xorm/xorm/engine_group.go
  88. 116 0
      vendor/github.com/go-xorm/xorm/engine_group_policy.go
  89. 22 0
      vendor/github.com/go-xorm/xorm/engine_maxlife.go
  90. 2 0
      vendor/github.com/go-xorm/xorm/error.go
  91. 46 195
      vendor/github.com/go-xorm/xorm/helpers.go
  92. 21 0
      vendor/github.com/go-xorm/xorm/helpler_time.go
  93. 103 0
      vendor/github.com/go-xorm/xorm/interface.go
  94. 33 7
      vendor/github.com/go-xorm/xorm/processors.go
  95. 31 45
      vendor/github.com/go-xorm/xorm/rows.go
  96. 187 184
      vendor/github.com/go-xorm/xorm/session.go
  97. 12 12
      vendor/github.com/go-xorm/xorm/session_cols.go
  98. 9 9
      vendor/github.com/go-xorm/xorm/session_cond.go
  99. 101 109
      vendor/github.com/go-xorm/xorm/session_convert.go
  100. 40 38
      vendor/github.com/go-xorm/xorm/session_delete.go

+ 5 - 1
CHANGELOG.md

@@ -1,5 +1,6 @@
 # 5.1.0 (unreleased)
 # 5.1.0 (unreleased)
 
 
+* **MSSQL**: New Microsoft SQL Server data source [#10093](https://github.com/grafana/grafana/pull/10093), [#11298](https://github.com/grafana/grafana/pull/11298), thx [@linuxchips](https://github.com/linuxchips)
 * **Prometheus**: The heatmap panel now support Prometheus histograms [#10009](https://github.com/grafana/grafana/issues/10009)
 * **Prometheus**: The heatmap panel now support Prometheus histograms [#10009](https://github.com/grafana/grafana/issues/10009)
 * **Postgres/MySQL**: Ability to insert 0s or nulls for missing intervals [#9487](https://github.com/grafana/grafana/issues/9487), thanks [@svenklemm](https://github.com/svenklemm)
 * **Postgres/MySQL**: Ability to insert 0s or nulls for missing intervals [#9487](https://github.com/grafana/grafana/issues/9487), thanks [@svenklemm](https://github.com/svenklemm)
 * **Graph**: Thresholds for Right Y axis [#7107](https://github.com/grafana/grafana/issues/7107), thx [@ilgizar](https://github.com/ilgizar)
 * **Graph**: Thresholds for Right Y axis [#7107](https://github.com/grafana/grafana/issues/7107), thx [@ilgizar](https://github.com/ilgizar)
@@ -13,9 +14,12 @@
 * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes)
 * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes)
 * **Cloudwatch**: Support high resolution metrics [#10925](https://github.com/grafana/grafana/pull/10925), thx [@mtanda](https://github.com/mtanda)
 * **Cloudwatch**: Support high resolution metrics [#10925](https://github.com/grafana/grafana/pull/10925), thx [@mtanda](https://github.com/mtanda)
 * **Cloudwatch**: Add dimension filtering to CloudWatch `dimension_values()` [#10029](https://github.com/grafana/grafana/issues/10029), thx [@willyhutw](https://github.com/willyhutw)
 * **Cloudwatch**: Add dimension filtering to CloudWatch `dimension_values()` [#10029](https://github.com/grafana/grafana/issues/10029), thx [@willyhutw](https://github.com/willyhutw)
-* **Units**: Second to HH:mm:ss formatter [#11107](https://github.com/grafana/grafana/issues/11107), thx [@gladdiologist](https://github.com/gladdiologist) 
+* **Units**: Second to HH:mm:ss formatter [#11107](https://github.com/grafana/grafana/issues/11107), thx [@gladdiologist](https://github.com/gladdiologist)
 * **Singlestat**: Add color to prefix and postfix in singlestat panel [#11143](https://github.com/grafana/grafana/pull/11143), thx [@ApsOps](https://github.com/ApsOps)
 * **Singlestat**: Add color to prefix and postfix in singlestat panel [#11143](https://github.com/grafana/grafana/pull/11143), thx [@ApsOps](https://github.com/ApsOps)
 
 
+# 5.0.4 (unreleased)
+* **Dashboard** Fixed bug where collapsed panels could not be directly linked to/renderer [#11114](https://github.com/grafana/grafana/issues/11114) & [#11086](https://github.com/grafana/grafana/issues/11086)
+
 # 5.0.3 (2018-03-16)
 # 5.0.3 (2018-03-16)
 * **Mysql**: Mysql panic occurring occasionally upon Grafana dashboard access (a bigger patch than the one in 5.0.2) [#11155](https://github.com/grafana/grafana/issues/11155)
 * **Mysql**: Mysql panic occurring occasionally upon Grafana dashboard access (a bigger patch than the one in 5.0.2) [#11155](https://github.com/grafana/grafana/issues/11155)
 
 

+ 5 - 4
Gopkg.lock

@@ -153,7 +153,6 @@
   packages = [
   packages = [
     ".",
     ".",
     "memcache",
     "memcache",
-    "mysql",
     "postgres",
     "postgres",
     "redis"
     "redis"
   ]
   ]
@@ -179,12 +178,14 @@
 [[projects]]
 [[projects]]
   name = "github.com/go-xorm/core"
   name = "github.com/go-xorm/core"
   packages = ["."]
   packages = ["."]
-  revision = "e8409d73255791843585964791443dbad877058c"
+  revision = "da1adaf7a28ca792961721a34e6e04945200c890"
+  version = "v0.5.7"
 
 
 [[projects]]
 [[projects]]
   name = "github.com/go-xorm/xorm"
   name = "github.com/go-xorm/xorm"
   packages = ["."]
   packages = ["."]
-  revision = "6687a2b4e824f4d87f2d65060ec5cb0d896dff1e"
+  revision = "1933dd69e294c0a26c0266637067f24dbb25770c"
+  version = "v0.6.4"
 
 
 [[projects]]
 [[projects]]
   branch = "master"
   branch = "master"
@@ -642,6 +643,6 @@
 [solve-meta]
 [solve-meta]
   analyzer-name = "dep"
   analyzer-name = "dep"
   analyzer-version = 1
   analyzer-version = 1
-  inputs-digest = "5e65aeace832f1b4be17e7ff5d5714513c40f31b94b885f64f98f2332968d7c6"
+  inputs-digest = "8a9e651fb8ea49dfd3c6ddc99bd3242b39e453ea9edd11321da79bd2c865e9d1"
   solver-name = "gps-cdcl"
   solver-name = "gps-cdcl"
   solver-version = 1
   solver-version = 1

+ 2 - 4
Gopkg.toml

@@ -85,13 +85,11 @@ ignored = [
 
 
 [[constraint]]
 [[constraint]]
   name = "github.com/go-xorm/core"
   name = "github.com/go-xorm/core"
-  revision = "e8409d73255791843585964791443dbad877058c"
-  #version = "0.5.7" //keeping this since we would rather depend on version then commit
+  version = "0.5.7"
 
 
 [[constraint]]
 [[constraint]]
   name = "github.com/go-xorm/xorm"
   name = "github.com/go-xorm/xorm"
-  revision = "6687a2b4e824f4d87f2d65060ec5cb0d896dff1e"
-  #version = "0.6.4" //keeping this since we would rather depend on version then commit
+  version = "0.6.4"
 
 
 [[constraint]]
 [[constraint]]
   name = "github.com/gorilla/websocket"
   name = "github.com/gorilla/websocket"

+ 6 - 0
conf/defaults.ini

@@ -82,6 +82,9 @@ max_idle_conn = 2
 # Max conn setting default is 0 (mean not set)
 # Max conn setting default is 0 (mean not set)
 max_open_conn =
 max_open_conn =
 
 
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+conn_max_lifetime = 14400
+
 # Set to true to log the sql calls and execution times.
 # Set to true to log the sql calls and execution times.
 log_queries =
 log_queries =
 
 
@@ -125,6 +128,9 @@ cookie_secure = false
 session_life_time = 86400
 session_life_time = 86400
 gc_interval_time = 86400
 gc_interval_time = 86400
 
 
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+conn_max_lifetime = 14400
+
 #################################### Data proxy ###########################
 #################################### Data proxy ###########################
 [dataproxy]
 [dataproxy]
 
 

+ 3 - 0
conf/sample.ini

@@ -90,6 +90,9 @@
 # Max conn setting default is 0 (mean not set)
 # Max conn setting default is 0 (mean not set)
 ;max_open_conn =
 ;max_open_conn =
 
 
+# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
+;conn_max_lifetime = 14400
+
 # Set to true to log the sql calls and execution times.
 # Set to true to log the sql calls and execution times.
 log_queries =
 log_queries =
 
 

+ 8 - 2
docs/sources/administration/provisioning.md

@@ -133,12 +133,18 @@ datasources:
   editable: false
   editable: false
 ```
 ```
 
 
+#### Extra info per datasource
+
+| Datasource | Misc |
+| ---- | ---- |
+| Elasticserach | Elasticsearch uses the `database` property to configure the index for a datasource |
+
 #### Json data
 #### Json data
 
 
 Since not all datasources have the same configuration settings we only have the most common ones as fields. The rest should be stored as a json blob in the `json_data` field. Here are the most common settings that the core datasources use.
 Since not all datasources have the same configuration settings we only have the most common ones as fields. The rest should be stored as a json blob in the `json_data` field. Here are the most common settings that the core datasources use.
 
 
-| Name | Type | Datasource |Description |
-| ----| ---- | ---- | --- |
+| Name | Type | Datasource | Description |
+| ---- | ---- | ---- | ---- |
 | tlsAuth | boolean | *All* |  Enable TLS authentication using client cert configured in secure json data |
 | tlsAuth | boolean | *All* |  Enable TLS authentication using client cert configured in secure json data |
 | tlsAuthWithCACert | boolean | *All* | Enable TLS authtication using CA cert |
 | tlsAuthWithCACert | boolean | *All* | Enable TLS authtication using CA cert |
 | tlsSkipVerify | boolean | *All* | Controls whether a client verifies the server's certificate chain and host name. |
 | tlsSkipVerify | boolean | *All* | Controls whether a client verifies the server's certificate chain and host name. |

+ 5 - 0
docs/sources/installation/configuration.md

@@ -234,7 +234,12 @@ The maximum number of connections in the idle connection pool.
 ### max_open_conn
 ### max_open_conn
 The maximum number of open connections to the database.
 The maximum number of open connections to the database.
 
 
+### conn_max_lifetime
+
+Sets the maximum amount of time a connection may be reused. The default is 14400 (which means 14400 seconds or 4 hours). For MySQL, this setting should be shorter than the [`wait_timeout`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout) variable.
+
 ### log_queries
 ### log_queries
+
 Set to `true` to log the sql calls and execution times.
 Set to `true` to log the sql calls and execution times.
 
 
 <hr />
 <hr />

+ 68 - 1
docs/sources/reference/templating.md

@@ -1,6 +1,6 @@
 +++
 +++
 title = "Variables"
 title = "Variables"
-keywords = ["grafana", "templating", "documentation", "guide"]
+keywords = ["grafana", "templating", "documentation", "guide", "template", "variable"]
 type = "docs"
 type = "docs"
 [menu.docs]
 [menu.docs]
 name = "Variables"
 name = "Variables"
@@ -80,6 +80,73 @@ Option | Description
 *Regex* | Regex to filter or capture specific parts of the names return by your data source query. Optional.
 *Regex* | Regex to filter or capture specific parts of the names return by your data source query. Optional.
 *Sort* | Define sort order for options in dropdown. **Disabled** means that the order of options returned by your data source query will be used.
 *Sort* | Define sort order for options in dropdown. **Disabled** means that the order of options returned by your data source query will be used.
 
 
+#### Using regex to filter/modify values in the Variable dropdown
+
+Using the Regex Query Option, you filter the list of options returned by the Variable query or modify the options returned.
+
+Examples of filtering on the following list of options:
+
+```text
+backend_01
+backend_02
+backend_03
+backend_04
+```
+
+##### Filter so that only the options that end with `01` or `02` are returned:
+
+Regex:
+
+```regex
+/.*[01|02]/
+```
+
+Result:
+
+```text
+backend_01
+backend_02
+```
+
+##### Filter and modify the options using a regex capture group to return part of the text:
+
+Regex:
+
+```regex
+/.*(01|02)/
+```
+
+Result:
+
+```text
+01
+02
+```
+
+#### Filter and modify - Prometheus Example
+
+List of options:
+
+```text
+up{instance="demo.robustperception.io:9090",job="prometheus"} 1 1521630638000
+up{instance="demo.robustperception.io:9093",job="alertmanager"} 1 1521630638000
+up{instance="demo.robustperception.io:9100",job="node"} 1 1521630638000
+```
+
+Regex:
+
+```regex
+/.*instance="([^"]*).*/
+```
+
+Result:
+
+```text
+demo.robustperception.io:9090
+demo.robustperception.io:9093
+demo.robustperception.io:9100
+```
+
 ### Query expressions
 ### Query expressions
 
 
 The query expressions are different for each data source.
 The query expressions are different for each data source.

+ 1 - 1
emails/templates/layouts/default.html

@@ -143,7 +143,7 @@ td[class="stack-column-center"] {
 											<center>
 											<center>
 												<p style="text-align: center; font-size: 12px; color: #999999;">
 												<p style="text-align: center; font-size: 12px; color: #999999;">
 													Sent by <a href="[[.AppUrl]]">Grafana v[[.BuildVersion]]</a>
 													Sent by <a href="[[.AppUrl]]">Grafana v[[.BuildVersion]]</a>
-													<br />&copy; 2016 Grafana and raintank
+													<br />&copy; 2018 Grafana Labs
 												</p>
 												</p>
 											</center>
 											</center>
 										</td>
 										</td>

+ 7 - 7
pkg/api/admin_users.go

@@ -47,14 +47,14 @@ func AdminCreateUser(c *m.ReqContext, form dtos.AdminCreateUserForm) {
 }
 }
 
 
 func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordForm) {
 func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordForm) {
-	userId := c.ParamsInt64(":id")
+	userID := c.ParamsInt64(":id")
 
 
 	if len(form.Password) < 4 {
 	if len(form.Password) < 4 {
 		c.JsonApiErr(400, "New password too short", nil)
 		c.JsonApiErr(400, "New password too short", nil)
 		return
 		return
 	}
 	}
 
 
-	userQuery := m.GetUserByIdQuery{Id: userId}
+	userQuery := m.GetUserByIdQuery{Id: userID}
 
 
 	if err := bus.Dispatch(&userQuery); err != nil {
 	if err := bus.Dispatch(&userQuery); err != nil {
 		c.JsonApiErr(500, "Could not read user from database", err)
 		c.JsonApiErr(500, "Could not read user from database", err)
@@ -64,7 +64,7 @@ func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordF
 	passwordHashed := util.EncodePassword(form.Password, userQuery.Result.Salt)
 	passwordHashed := util.EncodePassword(form.Password, userQuery.Result.Salt)
 
 
 	cmd := m.ChangeUserPasswordCommand{
 	cmd := m.ChangeUserPasswordCommand{
-		UserId:      userId,
+		UserId:      userID,
 		NewPassword: passwordHashed,
 		NewPassword: passwordHashed,
 	}
 	}
 
 
@@ -77,10 +77,10 @@ func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordF
 }
 }
 
 
 func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermissionsForm) {
 func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermissionsForm) {
-	userId := c.ParamsInt64(":id")
+	userID := c.ParamsInt64(":id")
 
 
 	cmd := m.UpdateUserPermissionsCommand{
 	cmd := m.UpdateUserPermissionsCommand{
-		UserId:         userId,
+		UserId:         userID,
 		IsGrafanaAdmin: form.IsGrafanaAdmin,
 		IsGrafanaAdmin: form.IsGrafanaAdmin,
 	}
 	}
 
 
@@ -93,9 +93,9 @@ func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermis
 }
 }
 
 
 func AdminDeleteUser(c *m.ReqContext) {
 func AdminDeleteUser(c *m.ReqContext) {
-	userId := c.ParamsInt64(":id")
+	userID := c.ParamsInt64(":id")
 
 
-	cmd := m.DeleteUserCommand{UserId: userId}
+	cmd := m.DeleteUserCommand{UserId: userID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		c.JsonApiErr(500, "Failed to delete user", err)
 		c.JsonApiErr(500, "Failed to delete user", err)

+ 3 - 3
pkg/api/alerting.go

@@ -26,9 +26,9 @@ func ValidateOrgAlert(c *m.ReqContext) {
 }
 }
 
 
 func GetAlertStatesForDashboard(c *m.ReqContext) Response {
 func GetAlertStatesForDashboard(c *m.ReqContext) Response {
-	dashboardId := c.QueryInt64("dashboardId")
+	dashboardID := c.QueryInt64("dashboardId")
 
 
-	if dashboardId == 0 {
+	if dashboardID == 0 {
 		return ApiError(400, "Missing query parameter dashboardId", nil)
 		return ApiError(400, "Missing query parameter dashboardId", nil)
 	}
 	}
 
 
@@ -151,7 +151,7 @@ func GetAlertNotifications(c *m.ReqContext) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func GetAlertNotificationById(c *m.ReqContext) Response {
+func GetAlertNotificationByID(c *m.ReqContext) Response {
 	query := &m.GetAlertNotificationsQuery{
 	query := &m.GetAlertNotificationsQuery{
 		OrgId: c.OrgId,
 		OrgId: c.OrgId,
 		Id:    c.ParamsInt64("notificationId"),
 		Id:    c.ParamsInt64("notificationId"),

+ 17 - 17
pkg/api/annotations.go

@@ -52,7 +52,7 @@ func (e *CreateAnnotationError) Error() string {
 }
 }
 
 
 func PostAnnotation(c *m.ReqContext, cmd dtos.PostAnnotationsCmd) Response {
 func PostAnnotation(c *m.ReqContext, cmd dtos.PostAnnotationsCmd) Response {
-	if canSave, err := canSaveByDashboardId(c, cmd.DashboardId); err != nil || !canSave {
+	if canSave, err := canSaveByDashboardID(c, cmd.DashboardId); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
@@ -179,18 +179,18 @@ func PostGraphiteAnnotation(c *m.ReqContext, cmd dtos.PostGraphiteAnnotationsCmd
 }
 }
 
 
 func UpdateAnnotation(c *m.ReqContext, cmd dtos.UpdateAnnotationsCmd) Response {
 func UpdateAnnotation(c *m.ReqContext, cmd dtos.UpdateAnnotationsCmd) Response {
-	annotationId := c.ParamsInt64(":annotationId")
+	annotationID := c.ParamsInt64(":annotationId")
 
 
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
 
 
-	if resp := canSave(c, repo, annotationId); resp != nil {
+	if resp := canSave(c, repo, annotationID); resp != nil {
 		return resp
 		return resp
 	}
 	}
 
 
 	item := annotations.Item{
 	item := annotations.Item{
 		OrgId:  c.OrgId,
 		OrgId:  c.OrgId,
 		UserId: c.UserId,
 		UserId: c.UserId,
-		Id:     annotationId,
+		Id:     annotationID,
 		Epoch:  cmd.Time / 1000,
 		Epoch:  cmd.Time / 1000,
 		Text:   cmd.Text,
 		Text:   cmd.Text,
 		Tags:   cmd.Tags,
 		Tags:   cmd.Tags,
@@ -254,14 +254,14 @@ func DeleteAnnotationById(c *m.ReqContext) Response {
 
 
 func DeleteAnnotationRegion(c *m.ReqContext) Response {
 func DeleteAnnotationRegion(c *m.ReqContext) Response {
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()
-	regionId := c.ParamsInt64(":regionId")
+	regionID := c.ParamsInt64(":regionId")
 
 
-	if resp := canSave(c, repo, regionId); resp != nil {
+	if resp := canSave(c, repo, regionID); resp != nil {
 		return resp
 		return resp
 	}
 	}
 
 
 	err := repo.Delete(&annotations.DeleteParams{
 	err := repo.Delete(&annotations.DeleteParams{
-		RegionId: regionId,
+		RegionId: regionID,
 	})
 	})
 
 
 	if err != nil {
 	if err != nil {
@@ -271,13 +271,13 @@ func DeleteAnnotationRegion(c *m.ReqContext) Response {
 	return ApiSuccess("Annotation region deleted")
 	return ApiSuccess("Annotation region deleted")
 }
 }
 
 
-func canSaveByDashboardId(c *m.ReqContext, dashboardId int64) (bool, error) {
-	if dashboardId == 0 && !c.SignedInUser.HasRole(m.ROLE_EDITOR) {
+func canSaveByDashboardID(c *m.ReqContext, dashboardID int64) (bool, error) {
+	if dashboardID == 0 && !c.SignedInUser.HasRole(m.ROLE_EDITOR) {
 		return false, nil
 		return false, nil
 	}
 	}
 
 
-	if dashboardId > 0 {
-		guardian := guardian.New(dashboardId, c.OrgId, c.SignedInUser)
+	if dashboardID > 0 {
+		guardian := guardian.New(dashboardID, c.OrgId, c.SignedInUser)
 		if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
 		if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
 			return false, err
 			return false, err
 		}
 		}
@@ -293,25 +293,25 @@ func canSave(c *m.ReqContext, repo annotations.Repository, annotationId int64) R
 		return ApiError(500, "Could not find annotation to update", err)
 		return ApiError(500, "Could not find annotation to update", err)
 	}
 	}
 
 
-	dashboardId := items[0].DashboardId
+	dashboardID := items[0].DashboardId
 
 
-	if canSave, err := canSaveByDashboardId(c, dashboardId); err != nil || !canSave {
+	if canSave, err := canSaveByDashboardID(c, dashboardID); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
-func canSaveByRegionId(c *m.ReqContext, repo annotations.Repository, regionId int64) Response {
-	items, err := repo.Find(&annotations.ItemQuery{RegionId: regionId, OrgId: c.OrgId})
+func canSaveByRegionID(c *m.ReqContext, repo annotations.Repository, regionID int64) Response {
+	items, err := repo.Find(&annotations.ItemQuery{RegionId: regionID, OrgId: c.OrgId})
 
 
 	if err != nil || len(items) == 0 {
 	if err != nil || len(items) == 0 {
 		return ApiError(500, "Could not find annotation to update", err)
 		return ApiError(500, "Could not find annotation to update", err)
 	}
 	}
 
 
-	dashboardId := items[0].DashboardId
+	dashboardID := items[0].DashboardId
 
 
-	if canSave, err := canSaveByDashboardId(c, dashboardId); err != nil || !canSave {
+	if canSave, err := canSaveByDashboardID(c, dashboardID); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 

+ 19 - 19
pkg/api/api.go

@@ -15,7 +15,7 @@ func (hs *HttpServer) registerRoutes() {
 	reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
 	reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
 	reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
 	reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
 	reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
 	reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
-	redirectFromLegacyDashboardUrl := middleware.RedirectFromLegacyDashboardUrl()
+	redirectFromLegacyDashboardUrl := middleware.RedirectFromLegacyDashboardURL()
 	redirectFromLegacyDashboardSoloUrl := middleware.RedirectFromLegacyDashboardSoloUrl()
 	redirectFromLegacyDashboardSoloUrl := middleware.RedirectFromLegacyDashboardSoloUrl()
 	quota := middleware.Quota
 	quota := middleware.Quota
 	bind := binding.Bind
 	bind := binding.Bind
@@ -110,7 +110,7 @@ func (hs *HttpServer) registerRoutes() {
 	r.Get("/api/snapshots-delete/:key", reqEditorRole, wrap(DeleteDashboardSnapshot))
 	r.Get("/api/snapshots-delete/:key", reqEditorRole, wrap(DeleteDashboardSnapshot))
 
 
 	// api renew session based on remember cookie
 	// api renew session based on remember cookie
-	r.Get("/api/login/ping", quota("session"), LoginApiPing)
+	r.Get("/api/login/ping", quota("session"), LoginAPIPing)
 
 
 	// authed api
 	// authed api
 	r.Group("/api", func(apiRoute RouteRegister) {
 	r.Group("/api", func(apiRoute RouteRegister) {
@@ -139,7 +139,7 @@ func (hs *HttpServer) registerRoutes() {
 		apiRoute.Group("/users", func(usersRoute RouteRegister) {
 		apiRoute.Group("/users", func(usersRoute RouteRegister) {
 			usersRoute.Get("/", wrap(SearchUsers))
 			usersRoute.Get("/", wrap(SearchUsers))
 			usersRoute.Get("/search", wrap(SearchUsersWithPaging))
 			usersRoute.Get("/search", wrap(SearchUsersWithPaging))
-			usersRoute.Get("/:id", wrap(GetUserById))
+			usersRoute.Get("/:id", wrap(GetUserByID))
 			usersRoute.Get("/:id/orgs", wrap(GetUserOrgList))
 			usersRoute.Get("/:id/orgs", wrap(GetUserOrgList))
 			// query parameters /users/lookup?loginOrEmail=admin@example.com
 			// query parameters /users/lookup?loginOrEmail=admin@example.com
 			usersRoute.Get("/lookup", wrap(GetUserByLoginOrEmail))
 			usersRoute.Get("/lookup", wrap(GetUserByLoginOrEmail))
@@ -149,11 +149,11 @@ func (hs *HttpServer) registerRoutes() {
 
 
 		// team (admin permission required)
 		// team (admin permission required)
 		apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
 		apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
-			teamsRoute.Get("/:teamId", wrap(GetTeamById))
+			teamsRoute.Get("/:teamId", wrap(GetTeamByID))
 			teamsRoute.Get("/search", wrap(SearchTeams))
 			teamsRoute.Get("/search", wrap(SearchTeams))
 			teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam))
 			teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam))
 			teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
 			teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
-			teamsRoute.Delete("/:teamId", wrap(DeleteTeamById))
+			teamsRoute.Delete("/:teamId", wrap(DeleteTeamByID))
 			teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers))
 			teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers))
 			teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember))
 			teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember))
 			teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
 			teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
@@ -192,10 +192,10 @@ func (hs *HttpServer) registerRoutes() {
 
 
 		// orgs (admin routes)
 		// orgs (admin routes)
 		apiRoute.Group("/orgs/:orgId", func(orgsRoute RouteRegister) {
 		apiRoute.Group("/orgs/:orgId", func(orgsRoute RouteRegister) {
-			orgsRoute.Get("/", wrap(GetOrgById))
+			orgsRoute.Get("/", wrap(GetOrgByID))
 			orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg))
 			orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg))
 			orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress))
 			orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress))
-			orgsRoute.Delete("/", wrap(DeleteOrgById))
+			orgsRoute.Delete("/", wrap(DeleteOrgByID))
 			orgsRoute.Get("/users", wrap(GetOrgUsers))
 			orgsRoute.Get("/users", wrap(GetOrgUsers))
 			orgsRoute.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
 			orgsRoute.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
 			orgsRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
 			orgsRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
@@ -211,9 +211,9 @@ func (hs *HttpServer) registerRoutes() {
 
 
 		// auth api keys
 		// auth api keys
 		apiRoute.Group("/auth/keys", func(keysRoute RouteRegister) {
 		apiRoute.Group("/auth/keys", func(keysRoute RouteRegister) {
-			keysRoute.Get("/", wrap(GetApiKeys))
-			keysRoute.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddApiKey))
-			keysRoute.Delete("/:id", wrap(DeleteApiKey))
+			keysRoute.Get("/", wrap(GetAPIKeys))
+			keysRoute.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddAPIKey))
+			keysRoute.Delete("/:id", wrap(DeleteAPIKey))
 		}, reqOrgAdmin)
 		}, reqOrgAdmin)
 
 
 		// Preferences
 		// Preferences
@@ -226,16 +226,16 @@ func (hs *HttpServer) registerRoutes() {
 			datasourceRoute.Get("/", wrap(GetDataSources))
 			datasourceRoute.Get("/", wrap(GetDataSources))
 			datasourceRoute.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), wrap(AddDataSource))
 			datasourceRoute.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), wrap(AddDataSource))
 			datasourceRoute.Put("/:id", bind(m.UpdateDataSourceCommand{}), wrap(UpdateDataSource))
 			datasourceRoute.Put("/:id", bind(m.UpdateDataSourceCommand{}), wrap(UpdateDataSource))
-			datasourceRoute.Delete("/:id", wrap(DeleteDataSourceById))
+			datasourceRoute.Delete("/:id", wrap(DeleteDataSourceByID))
 			datasourceRoute.Delete("/name/:name", wrap(DeleteDataSourceByName))
 			datasourceRoute.Delete("/name/:name", wrap(DeleteDataSourceByName))
-			datasourceRoute.Get("/:id", wrap(GetDataSourceById))
+			datasourceRoute.Get("/:id", wrap(GetDataSourceByID))
 			datasourceRoute.Get("/name/:name", wrap(GetDataSourceByName))
 			datasourceRoute.Get("/name/:name", wrap(GetDataSourceByName))
 		}, reqOrgAdmin)
 		}, reqOrgAdmin)
 
 
-		apiRoute.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)
+		apiRoute.Get("/datasources/id/:name", wrap(GetDataSourceIDByName), reqSignedIn)
 
 
 		apiRoute.Get("/plugins", wrap(GetPluginList))
 		apiRoute.Get("/plugins", wrap(GetPluginList))
-		apiRoute.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
+		apiRoute.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingByID))
 		apiRoute.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown))
 		apiRoute.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown))
 
 
 		apiRoute.Group("/plugins", func(pluginRoute RouteRegister) {
 		apiRoute.Group("/plugins", func(pluginRoute RouteRegister) {
@@ -250,11 +250,11 @@ func (hs *HttpServer) registerRoutes() {
 		// Folders
 		// Folders
 		apiRoute.Group("/folders", func(folderRoute RouteRegister) {
 		apiRoute.Group("/folders", func(folderRoute RouteRegister) {
 			folderRoute.Get("/", wrap(GetFolders))
 			folderRoute.Get("/", wrap(GetFolders))
-			folderRoute.Get("/id/:id", wrap(GetFolderById))
+			folderRoute.Get("/id/:id", wrap(GetFolderByID))
 			folderRoute.Post("/", bind(m.CreateFolderCommand{}), wrap(CreateFolder))
 			folderRoute.Post("/", bind(m.CreateFolderCommand{}), wrap(CreateFolder))
 
 
 			folderRoute.Group("/:uid", func(folderUidRoute RouteRegister) {
 			folderRoute.Group("/:uid", func(folderUidRoute RouteRegister) {
-				folderUidRoute.Get("/", wrap(GetFolderByUid))
+				folderUidRoute.Get("/", wrap(GetFolderByUID))
 				folderUidRoute.Put("/", bind(m.UpdateFolderCommand{}), wrap(UpdateFolder))
 				folderUidRoute.Put("/", bind(m.UpdateFolderCommand{}), wrap(UpdateFolder))
 				folderUidRoute.Delete("/", wrap(DeleteFolder))
 				folderUidRoute.Delete("/", wrap(DeleteFolder))
 
 
@@ -268,7 +268,7 @@ func (hs *HttpServer) registerRoutes() {
 		// Dashboard
 		// Dashboard
 		apiRoute.Group("/dashboards", func(dashboardRoute RouteRegister) {
 		apiRoute.Group("/dashboards", func(dashboardRoute RouteRegister) {
 			dashboardRoute.Get("/uid/:uid", wrap(GetDashboard))
 			dashboardRoute.Get("/uid/:uid", wrap(GetDashboard))
-			dashboardRoute.Delete("/uid/:uid", wrap(DeleteDashboardByUid))
+			dashboardRoute.Delete("/uid/:uid", wrap(DeleteDashboardByUID))
 
 
 			dashboardRoute.Get("/db/:slug", wrap(GetDashboard))
 			dashboardRoute.Get("/db/:slug", wrap(GetDashboard))
 			dashboardRoute.Delete("/db/:slug", wrap(DeleteDashboard))
 			dashboardRoute.Delete("/db/:slug", wrap(DeleteDashboard))
@@ -314,7 +314,7 @@ func (hs *HttpServer) registerRoutes() {
 		// metrics
 		// metrics
 		apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics))
 		apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics))
 		apiRoute.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios))
 		apiRoute.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios))
-		apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, wrap(GenerateSqlTestData))
+		apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, wrap(GenerateSQLTestData))
 		apiRoute.Get("/tsdb/testdata/random-walk", wrap(GetTestDataRandomWalk))
 		apiRoute.Get("/tsdb/testdata/random-walk", wrap(GetTestDataRandomWalk))
 
 
 		apiRoute.Group("/alerts", func(alertsRoute RouteRegister) {
 		apiRoute.Group("/alerts", func(alertsRoute RouteRegister) {
@@ -332,7 +332,7 @@ func (hs *HttpServer) registerRoutes() {
 			alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
 			alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
 			alertNotifications.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
 			alertNotifications.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
 			alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
 			alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
-			alertNotifications.Get("/:notificationId", wrap(GetAlertNotificationById))
+			alertNotifications.Get("/:notificationId", wrap(GetAlertNotificationByID))
 			alertNotifications.Delete("/:notificationId", wrap(DeleteAlertNotification))
 			alertNotifications.Delete("/:notificationId", wrap(DeleteAlertNotification))
 		}, reqEditorRole)
 		}, reqEditorRole)
 
 

+ 3 - 3
pkg/api/apikey.go

@@ -7,7 +7,7 @@ import (
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
-func GetApiKeys(c *m.ReqContext) Response {
+func GetAPIKeys(c *m.ReqContext) Response {
 	query := m.GetApiKeysQuery{OrgId: c.OrgId}
 	query := m.GetApiKeysQuery{OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -26,7 +26,7 @@ func GetApiKeys(c *m.ReqContext) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func DeleteApiKey(c *m.ReqContext) Response {
+func DeleteAPIKey(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 
 
 	cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
 	cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
@@ -39,7 +39,7 @@ func DeleteApiKey(c *m.ReqContext) Response {
 	return ApiSuccess("API key deleted")
 	return ApiSuccess("API key deleted")
 }
 }
 
 
-func AddApiKey(c *m.ReqContext, cmd m.AddApiKeyCommand) Response {
+func AddAPIKey(c *m.ReqContext, cmd m.AddApiKeyCommand) Response {
 	if !cmd.Role.IsValid() {
 	if !cmd.Role.IsValid() {
 		return ApiError(400, "Invalid role specified", nil)
 		return ApiError(400, "Invalid role specified", nil)
 	}
 	}

+ 2 - 2
pkg/api/app_routes.go

@@ -55,11 +55,11 @@ func InitAppPluginRoutes(r *macaron.Macaron) {
 	}
 	}
 }
 }
 
 
-func AppPluginRoute(route *plugins.AppPluginRoute, appId string) macaron.Handler {
+func AppPluginRoute(route *plugins.AppPluginRoute, appID string) macaron.Handler {
 	return func(c *m.ReqContext) {
 	return func(c *m.ReqContext) {
 		path := c.Params("*")
 		path := c.Params("*")
 
 
-		proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId)
+		proxy := pluginproxy.NewApiPluginProxy(c, path, route, appID)
 		proxy.Transport = pluginProxyTransport
 		proxy.Transport = pluginProxyTransport
 		proxy.ServeHTTP(c.Resp, c.Req.Request)
 		proxy.ServeHTTP(c.Resp, c.Req.Request)
 	}
 	}

+ 1 - 1
pkg/api/common_test.go

@@ -99,7 +99,7 @@ func setupScenarioContext(url string) *scenarioContext {
 	}))
 	}))
 
 
 	sc.m.Use(middleware.GetContextHandler())
 	sc.m.Use(middleware.GetContextHandler())
-	sc.m.Use(middleware.Sessioner(&session.Options{}))
+	sc.m.Use(middleware.Sessioner(&session.Options{}, 0))
 
 
 	return sc
 	return sc
 }
 }

+ 18 - 21
pkg/api/dashboard.go

@@ -22,12 +22,12 @@ import (
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )
 
 
-func isDashboardStarredByUser(c *m.ReqContext, dashId int64) (bool, error) {
+func isDashboardStarredByUser(c *m.ReqContext, dashID int64) (bool, error) {
 	if !c.IsSignedIn {
 	if !c.IsSignedIn {
 		return false, nil
 		return false, nil
 	}
 	}
 
 
-	query := m.IsStarredByUserQuery{UserId: c.UserId, DashboardId: dashId}
+	query := m.IsStarredByUserQuery{UserId: c.UserId, DashboardId: dashID}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return false, err
 		return false, err
 	}
 	}
@@ -114,24 +114,22 @@ func GetDashboard(c *m.ReqContext) Response {
 	return Json(200, dto)
 	return Json(200, dto)
 }
 }
 
 
-func getUserLogin(userId int64) string {
-	query := m.GetUserByIdQuery{Id: userId}
+func getUserLogin(userID int64) string {
+	query := m.GetUserByIdQuery{Id: userID}
 	err := bus.Dispatch(&query)
 	err := bus.Dispatch(&query)
 	if err != nil {
 	if err != nil {
 		return "Anonymous"
 		return "Anonymous"
-	} else {
-		user := query.Result
-		return user.Login
 	}
 	}
+	return query.Result.Login
 }
 }
 
 
-func getDashboardHelper(orgId int64, slug string, id int64, uid string) (*m.Dashboard, Response) {
+func getDashboardHelper(orgID int64, slug string, id int64, uid string) (*m.Dashboard, Response) {
 	var query m.GetDashboardQuery
 	var query m.GetDashboardQuery
 
 
 	if len(uid) > 0 {
 	if len(uid) > 0 {
-		query = m.GetDashboardQuery{Uid: uid, Id: id, OrgId: orgId}
+		query = m.GetDashboardQuery{Uid: uid, Id: id, OrgId: orgID}
 	} else {
 	} else {
-		query = m.GetDashboardQuery{Slug: slug, Id: id, OrgId: orgId}
+		query = m.GetDashboardQuery{Slug: slug, Id: id, OrgId: orgID}
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -173,7 +171,7 @@ func DeleteDashboard(c *m.ReqContext) Response {
 	})
 	})
 }
 }
 
 
-func DeleteDashboardByUid(c *m.ReqContext) Response {
+func DeleteDashboardByUID(c *m.ReqContext) Response {
 	dash, rsp := getDashboardHelper(c.OrgId, "", 0, c.Params(":uid"))
 	dash, rsp := getDashboardHelper(c.OrgId, "", 0, c.Params(":uid"))
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
@@ -291,9 +289,8 @@ func GetHomeDashboard(c *m.ReqContext) Response {
 			url := m.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
 			url := m.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
 			dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
 			dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
 			return Json(200, &dashRedirect)
 			return Json(200, &dashRedirect)
-		} else {
-			log.Warn("Failed to get slug from database, %s", err.Error())
 		}
 		}
+		log.Warn("Failed to get slug from database, %s", err.Error())
 	}
 	}
 
 
 	filePath := path.Join(setting.StaticRootPath, "dashboards/home.json")
 	filePath := path.Join(setting.StaticRootPath, "dashboards/home.json")
@@ -339,22 +336,22 @@ func addGettingStartedPanelToHomeDashboard(dash *simplejson.Json) {
 
 
 // GetDashboardVersions returns all dashboard versions as JSON
 // GetDashboardVersions returns all dashboard versions as JSON
 func GetDashboardVersions(c *m.ReqContext) Response {
 func GetDashboardVersions(c *m.ReqContext) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	guardian := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	guardian := guardian.New(dashID, c.OrgId, c.SignedInUser)
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	query := m.GetDashboardVersionsQuery{
 	query := m.GetDashboardVersionsQuery{
 		OrgId:       c.OrgId,
 		OrgId:       c.OrgId,
-		DashboardId: dashId,
+		DashboardId: dashID,
 		Limit:       c.QueryInt("limit"),
 		Limit:       c.QueryInt("limit"),
 		Start:       c.QueryInt("start"),
 		Start:       c.QueryInt("start"),
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
-		return ApiError(404, fmt.Sprintf("No versions found for dashboardId %d", dashId), err)
+		return ApiError(404, fmt.Sprintf("No versions found for dashboardId %d", dashID), err)
 	}
 	}
 
 
 	for _, version := range query.Result {
 	for _, version := range query.Result {
@@ -378,21 +375,21 @@ func GetDashboardVersions(c *m.ReqContext) Response {
 
 
 // GetDashboardVersion returns the dashboard version with the given ID.
 // GetDashboardVersion returns the dashboard version with the given ID.
 func GetDashboardVersion(c *m.ReqContext) Response {
 func GetDashboardVersion(c *m.ReqContext) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	guardian := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	guardian := guardian.New(dashID, c.OrgId, c.SignedInUser)
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 	if canSave, err := guardian.CanSave(); err != nil || !canSave {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	query := m.GetDashboardVersionQuery{
 	query := m.GetDashboardVersionQuery{
 		OrgId:       c.OrgId,
 		OrgId:       c.OrgId,
-		DashboardId: dashId,
+		DashboardId: dashID,
 		Version:     c.ParamsInt(":id"),
 		Version:     c.ParamsInt(":id"),
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
-		return ApiError(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashId), err)
+		return ApiError(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashID), err)
 	}
 	}
 
 
 	creator := "Anonymous"
 	creator := "Anonymous"

+ 8 - 8
pkg/api/dashboard_permission.go

@@ -10,14 +10,14 @@ import (
 )
 )
 
 
 func GetDashboardPermissionList(c *m.ReqContext) Response {
 func GetDashboardPermissionList(c *m.ReqContext) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	_, rsp := getDashboardHelper(c.OrgId, "", dashId, "")
+	_, rsp := getDashboardHelper(c.OrgId, "", dashID, "")
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
 	}
 	}
 
 
-	g := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	g := guardian.New(dashID, c.OrgId, c.SignedInUser)
 
 
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
@@ -38,25 +38,25 @@ func GetDashboardPermissionList(c *m.ReqContext) Response {
 }
 }
 
 
 func UpdateDashboardPermissions(c *m.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
 func UpdateDashboardPermissions(c *m.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
-	dashId := c.ParamsInt64(":dashboardId")
+	dashID := c.ParamsInt64(":dashboardId")
 
 
-	_, rsp := getDashboardHelper(c.OrgId, "", dashId, "")
+	_, rsp := getDashboardHelper(c.OrgId, "", dashID, "")
 	if rsp != nil {
 	if rsp != nil {
 		return rsp
 		return rsp
 	}
 	}
 
 
-	g := guardian.New(dashId, c.OrgId, c.SignedInUser)
+	g := guardian.New(dashID, c.OrgId, c.SignedInUser)
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 	if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
 		return dashboardGuardianResponse(err)
 		return dashboardGuardianResponse(err)
 	}
 	}
 
 
 	cmd := m.UpdateDashboardAclCommand{}
 	cmd := m.UpdateDashboardAclCommand{}
-	cmd.DashboardId = dashId
+	cmd.DashboardId = dashID
 
 
 	for _, item := range apiCmd.Items {
 	for _, item := range apiCmd.Items {
 		cmd.Items = append(cmd.Items, &m.DashboardAcl{
 		cmd.Items = append(cmd.Items, &m.DashboardAcl{
 			OrgId:       c.OrgId,
 			OrgId:       c.OrgId,
-			DashboardId: dashId,
+			DashboardId: dashID,
 			UserId:      item.UserId,
 			UserId:      item.UserId,
 			TeamId:      item.TeamId,
 			TeamId:      item.TeamId,
 			Role:        item.Role,
 			Role:        item.Role,

+ 2 - 2
pkg/api/dashboard_snapshot.go

@@ -106,9 +106,9 @@ func DeleteDashboardSnapshot(c *m.ReqContext) Response {
 		return ApiError(404, "Failed to get dashboard snapshot", nil)
 		return ApiError(404, "Failed to get dashboard snapshot", nil)
 	}
 	}
 	dashboard := query.Result.Dashboard
 	dashboard := query.Result.Dashboard
-	dashboardId := dashboard.Get("id").MustInt64()
+	dashboardID := dashboard.Get("id").MustInt64()
 
 
-	guardian := guardian.New(dashboardId, c.OrgId, c.SignedInUser)
+	guardian := guardian.New(dashboardID, c.OrgId, c.SignedInUser)
 	canEdit, err := guardian.CanEdit()
 	canEdit, err := guardian.CanEdit()
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Error while checking permissions for snapshot", err)
 		return ApiError(500, "Error while checking permissions for snapshot", err)

+ 10 - 10
pkg/api/dashboard_test.go

@@ -105,7 +105,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -165,7 +165,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 200)
 				So(sc.resp.Code, ShouldEqual, 200)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -271,7 +271,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -329,7 +329,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -398,7 +398,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 200)
 				So(sc.resp.Code, ShouldEqual, 200)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -468,7 +468,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -527,7 +527,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 200)
 				So(sc.resp.Code, ShouldEqual, 200)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -594,7 +594,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
 			})
 			})
 
 
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
 			loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
-				CallDeleteDashboardByUid(sc)
+				CallDeleteDashboardByUID(sc)
 				So(sc.resp.Code, ShouldEqual, 403)
 				So(sc.resp.Code, ShouldEqual, 403)
 
 
 				Convey("Should lookup dashboard by uid", func() {
 				Convey("Should lookup dashboard by uid", func() {
@@ -837,12 +837,12 @@ func CallDeleteDashboard(sc *scenarioContext) {
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 }
 }
 
 
-func CallDeleteDashboardByUid(sc *scenarioContext) {
+func CallDeleteDashboardByUID(sc *scenarioContext) {
 	bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
 	bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
 		return nil
 		return nil
 	})
 	})
 
 
-	sc.handlerFunc = DeleteDashboardByUid
+	sc.handlerFunc = DeleteDashboardByUID
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 	sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
 }
 }
 
 

+ 4 - 4
pkg/api/dataproxy.go

@@ -13,19 +13,19 @@ import (
 
 
 const HeaderNameNoBackendCache = "X-Grafana-NoCache"
 const HeaderNameNoBackendCache = "X-Grafana-NoCache"
 
 
-func (hs *HttpServer) getDatasourceById(id int64, orgId int64, nocache bool) (*m.DataSource, error) {
+func (hs *HttpServer) getDatasourceByID(id int64, orgID int64, nocache bool) (*m.DataSource, error) {
 	cacheKey := fmt.Sprintf("ds-%d", id)
 	cacheKey := fmt.Sprintf("ds-%d", id)
 
 
 	if !nocache {
 	if !nocache {
 		if cached, found := hs.cache.Get(cacheKey); found {
 		if cached, found := hs.cache.Get(cacheKey); found {
 			ds := cached.(*m.DataSource)
 			ds := cached.(*m.DataSource)
-			if ds.OrgId == orgId {
+			if ds.OrgId == orgID {
 				return ds, nil
 				return ds, nil
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
+	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgID}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -39,7 +39,7 @@ func (hs *HttpServer) ProxyDataSourceRequest(c *m.ReqContext) {
 
 
 	nocache := c.Req.Header.Get(HeaderNameNoBackendCache) == "true"
 	nocache := c.Req.Header.Get(HeaderNameNoBackendCache) == "true"
 
 
-	ds, err := hs.getDatasourceById(c.ParamsInt64(":id"), c.OrgId, nocache)
+	ds, err := hs.getDatasourceByID(c.ParamsInt64(":id"), c.OrgId, nocache)
 
 
 	if err != nil {
 	if err != nil {
 		c.JsonApiErr(500, "Unable to load datasource meta data", err)
 		c.JsonApiErr(500, "Unable to load datasource meta data", err)

+ 12 - 13
pkg/api/datasources.go

@@ -49,7 +49,7 @@ func GetDataSources(c *m.ReqContext) Response {
 	return Json(200, &result)
 	return Json(200, &result)
 }
 }
 
 
-func GetDataSourceById(c *m.ReqContext) Response {
+func GetDataSourceByID(c *m.ReqContext) Response {
 	query := m.GetDataSourceByIdQuery{
 	query := m.GetDataSourceByIdQuery{
 		Id:    c.ParamsInt64(":id"),
 		Id:    c.ParamsInt64(":id"),
 		OrgId: c.OrgId,
 		OrgId: c.OrgId,
@@ -68,14 +68,14 @@ func GetDataSourceById(c *m.ReqContext) Response {
 	return Json(200, &dtos)
 	return Json(200, &dtos)
 }
 }
 
 
-func DeleteDataSourceById(c *m.ReqContext) Response {
+func DeleteDataSourceByID(c *m.ReqContext) Response {
 	id := c.ParamsInt64(":id")
 	id := c.ParamsInt64(":id")
 
 
 	if id <= 0 {
 	if id <= 0 {
 		return ApiError(400, "Missing valid datasource id", nil)
 		return ApiError(400, "Missing valid datasource id", nil)
 	}
 	}
 
 
-	ds, err := getRawDataSourceById(id, c.OrgId)
+	ds, err := getRawDataSourceByID(id, c.OrgId)
 	if err != nil {
 	if err != nil {
 		return ApiError(400, "Failed to delete datasource", nil)
 		return ApiError(400, "Failed to delete datasource", nil)
 	}
 	}
@@ -143,7 +143,7 @@ func UpdateDataSource(c *m.ReqContext, cmd m.UpdateDataSourceCommand) Response {
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
 	cmd.Id = c.ParamsInt64(":id")
 	cmd.Id = c.ParamsInt64(":id")
 
 
-	err := fillWithSecureJsonData(&cmd)
+	err := fillWithSecureJSONData(&cmd)
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Failed to update datasource", err)
 		return ApiError(500, "Failed to update datasource", err)
 	}
 	}
@@ -152,9 +152,8 @@ func UpdateDataSource(c *m.ReqContext, cmd m.UpdateDataSourceCommand) Response {
 	if err != nil {
 	if err != nil {
 		if err == m.ErrDataSourceUpdatingOldVersion {
 		if err == m.ErrDataSourceUpdatingOldVersion {
 			return ApiError(500, "Failed to update datasource. Reload new version and try again", err)
 			return ApiError(500, "Failed to update datasource. Reload new version and try again", err)
-		} else {
-			return ApiError(500, "Failed to update datasource", err)
 		}
 		}
+		return ApiError(500, "Failed to update datasource", err)
 	}
 	}
 	ds := convertModelToDtos(cmd.Result)
 	ds := convertModelToDtos(cmd.Result)
 	return Json(200, util.DynMap{
 	return Json(200, util.DynMap{
@@ -165,12 +164,12 @@ func UpdateDataSource(c *m.ReqContext, cmd m.UpdateDataSourceCommand) Response {
 	})
 	})
 }
 }
 
 
-func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
+func fillWithSecureJSONData(cmd *m.UpdateDataSourceCommand) error {
 	if len(cmd.SecureJsonData) == 0 {
 	if len(cmd.SecureJsonData) == 0 {
 		return nil
 		return nil
 	}
 	}
 
 
-	ds, err := getRawDataSourceById(cmd.Id, cmd.OrgId)
+	ds, err := getRawDataSourceByID(cmd.Id, cmd.OrgId)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -179,8 +178,8 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
 		return m.ErrDatasourceIsReadOnly
 		return m.ErrDatasourceIsReadOnly
 	}
 	}
 
 
-	secureJsonData := ds.SecureJsonData.Decrypt()
-	for k, v := range secureJsonData {
+	secureJSONData := ds.SecureJsonData.Decrypt()
+	for k, v := range secureJSONData {
 
 
 		if _, ok := cmd.SecureJsonData[k]; !ok {
 		if _, ok := cmd.SecureJsonData[k]; !ok {
 			cmd.SecureJsonData[k] = v
 			cmd.SecureJsonData[k] = v
@@ -190,10 +189,10 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
 	return nil
 	return nil
 }
 }
 
 
-func getRawDataSourceById(id int64, orgId int64) (*m.DataSource, error) {
+func getRawDataSourceByID(id int64, orgID int64) (*m.DataSource, error) {
 	query := m.GetDataSourceByIdQuery{
 	query := m.GetDataSourceByIdQuery{
 		Id:    id,
 		Id:    id,
-		OrgId: orgId,
+		OrgId: orgID,
 	}
 	}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
@@ -220,7 +219,7 @@ func GetDataSourceByName(c *m.ReqContext) Response {
 }
 }
 
 
 // Get /api/datasources/id/:name
 // Get /api/datasources/id/:name
-func GetDataSourceIdByName(c *m.ReqContext) Response {
+func GetDataSourceIDByName(c *m.ReqContext) Response {
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {

+ 2 - 2
pkg/api/folder.go

@@ -31,7 +31,7 @@ func GetFolders(c *m.ReqContext) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func GetFolderByUid(c *m.ReqContext) Response {
+func GetFolderByUID(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 	folder, err := s.GetFolderByUid(c.Params(":uid"))
 
 
@@ -43,7 +43,7 @@ func GetFolderByUid(c *m.ReqContext) Response {
 	return Json(200, toFolderDto(g, folder))
 	return Json(200, toFolderDto(g, folder))
 }
 }
 
 
-func GetFolderById(c *m.ReqContext) Response {
+func GetFolderByID(c *m.ReqContext) Response {
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
 	folder, err := s.GetFolderById(c.ParamsInt64(":id"))
 	folder, err := s.GetFolderById(c.ParamsInt64(":id"))
 	if err != nil {
 	if err != nil {

+ 2 - 2
pkg/api/folder_test.go

@@ -133,8 +133,8 @@ func TestFoldersApiEndpoint(t *testing.T) {
 	})
 	})
 }
 }
 
 
-func callGetFolderByUid(sc *scenarioContext) {
-	sc.handlerFunc = GetFolderByUid
+func callGetFolderByUID(sc *scenarioContext) {
+	sc.handlerFunc = GetFolderByUID
 	sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
 	sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
 }
 }
 
 

+ 2 - 2
pkg/api/http_server.go

@@ -39,7 +39,7 @@ type HttpServer struct {
 	httpSrv *http.Server
 	httpSrv *http.Server
 }
 }
 
 
-func NewHttpServer() *HttpServer {
+func NewHTTPServer() *HttpServer {
 	return &HttpServer{
 	return &HttpServer{
 		log:   log.New("http.server"),
 		log:   log.New("http.server"),
 		cache: gocache.New(5*time.Minute, 10*time.Minute),
 		cache: gocache.New(5*time.Minute, 10*time.Minute),
@@ -175,7 +175,7 @@ func (hs *HttpServer) newMacaron() *macaron.Macaron {
 	m.Use(hs.healthHandler)
 	m.Use(hs.healthHandler)
 	m.Use(hs.metricsEndpoint)
 	m.Use(hs.metricsEndpoint)
 	m.Use(middleware.GetContextHandler())
 	m.Use(middleware.GetContextHandler())
-	m.Use(middleware.Sessioner(&setting.SessionOptions))
+	m.Use(middleware.Sessioner(&setting.SessionOptions, setting.SessionConnMaxLifetime))
 	m.Use(middleware.OrgRedirect())
 	m.Use(middleware.OrgRedirect())
 
 
 	// needs to be after context handler
 	// needs to be after context handler

+ 15 - 14
pkg/api/index.go

@@ -32,13 +32,13 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
 		locale = parts[0]
 		locale = parts[0]
 	}
 	}
 
 
-	appUrl := setting.AppUrl
-	appSubUrl := setting.AppSubUrl
+	appURL := setting.AppUrl
+	appSubURL := setting.AppSubUrl
 
 
 	// special case when doing localhost call from phantomjs
 	// special case when doing localhost call from phantomjs
 	if c.IsRenderCall {
 	if c.IsRenderCall {
-		appUrl = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
-		appSubUrl = ""
+		appURL = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
+		appSubURL = ""
 		settings["appSubUrl"] = ""
 		settings["appSubUrl"] = ""
 	}
 	}
 
 
@@ -62,8 +62,8 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
 		},
 		},
 		Settings:                settings,
 		Settings:                settings,
 		Theme:                   prefs.Theme,
 		Theme:                   prefs.Theme,
-		AppUrl:                  appUrl,
-		AppSubUrl:               appSubUrl,
+		AppUrl:                  appURL,
+		AppSubUrl:               appSubURL,
 		GoogleAnalyticsId:       setting.GoogleAnalyticsId,
 		GoogleAnalyticsId:       setting.GoogleAnalyticsId,
 		GoogleTagManagerId:      setting.GoogleTagManagerId,
 		GoogleTagManagerId:      setting.GoogleTagManagerId,
 		BuildVersion:            setting.BuildVersion,
 		BuildVersion:            setting.BuildVersion,
@@ -80,8 +80,8 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
 		data.User.Name = data.User.Login
 		data.User.Name = data.User.Login
 	}
 	}
 
 
-	themeUrlParam := c.Query("theme")
-	if themeUrlParam == "light" {
+	themeURLParam := c.Query("theme")
+	if themeURLParam == "light" {
 		data.User.LightTheme = true
 		data.User.LightTheme = true
 		data.Theme = "light"
 		data.Theme = "light"
 	}
 	}
@@ -299,12 +299,12 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
 }
 }
 
 
 func Index(c *m.ReqContext) {
 func Index(c *m.ReqContext) {
-	if data, err := setIndexViewData(c); err != nil {
+	data, err := setIndexViewData(c)
+	if err != nil {
 		c.Handle(500, "Failed to get settings", err)
 		c.Handle(500, "Failed to get settings", err)
 		return
 		return
-	} else {
-		c.HTML(200, "index", data)
 	}
 	}
+	c.HTML(200, "index", data)
 }
 }
 
 
 func NotFoundHandler(c *m.ReqContext) {
 func NotFoundHandler(c *m.ReqContext) {
@@ -313,10 +313,11 @@ func NotFoundHandler(c *m.ReqContext) {
 		return
 		return
 	}
 	}
 
 
-	if data, err := setIndexViewData(c); err != nil {
+	data, err := setIndexViewData(c)
+	if err != nil {
 		c.Handle(500, "Failed to get settings", err)
 		c.Handle(500, "Failed to get settings", err)
 		return
 		return
-	} else {
-		c.HTML(404, "index", data)
 	}
 	}
+
+	c.HTML(404, "index", data)
 }
 }

+ 3 - 3
pkg/api/login.go

@@ -14,7 +14,7 @@ import (
 )
 )
 
 
 const (
 const (
-	VIEW_INDEX = "index"
+	ViewIndex = "index"
 )
 )
 
 
 func LoginView(c *m.ReqContext) {
 func LoginView(c *m.ReqContext) {
@@ -40,7 +40,7 @@ func LoginView(c *m.ReqContext) {
 	}
 	}
 
 
 	if !tryLoginUsingRememberCookie(c) {
 	if !tryLoginUsingRememberCookie(c) {
-		c.HTML(200, VIEW_INDEX, viewData)
+		c.HTML(200, ViewIndex, viewData)
 		return
 		return
 	}
 	}
 
 
@@ -87,7 +87,7 @@ func tryLoginUsingRememberCookie(c *m.ReqContext) bool {
 	return true
 	return true
 }
 }
 
 
-func LoginApiPing(c *m.ReqContext) {
+func LoginAPIPing(c *m.ReqContext) {
 	if !tryLoginUsingRememberCookie(c) {
 	if !tryLoginUsingRememberCookie(c) {
 		c.JsonApiErr(401, "Unauthorized", nil)
 		c.JsonApiErr(401, "Unauthorized", nil)
 		return
 		return

+ 3 - 3
pkg/api/metrics.go

@@ -20,12 +20,12 @@ func QueryMetrics(c *m.ReqContext, reqDto dtos.MetricRequest) Response {
 		return ApiError(400, "No queries found in query", nil)
 		return ApiError(400, "No queries found in query", nil)
 	}
 	}
 
 
-	dsId, err := reqDto.Queries[0].Get("datasourceId").Int64()
+	dsID, err := reqDto.Queries[0].Get("datasourceId").Int64()
 	if err != nil {
 	if err != nil {
 		return ApiError(400, "Query missing datasourceId", nil)
 		return ApiError(400, "Query missing datasourceId", nil)
 	}
 	}
 
 
-	dsQuery := m.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
+	dsQuery := m.GetDataSourceByIdQuery{Id: dsID, OrgId: c.OrgId}
 	if err := bus.Dispatch(&dsQuery); err != nil {
 	if err := bus.Dispatch(&dsQuery); err != nil {
 		return ApiError(500, "failed to fetch data source", err)
 		return ApiError(500, "failed to fetch data source", err)
 	}
 	}
@@ -82,7 +82,7 @@ func GenerateError(c *m.ReqContext) Response {
 }
 }
 
 
 // GET /api/tsdb/testdata/gensql
 // GET /api/tsdb/testdata/gensql
-func GenerateSqlTestData(c *m.ReqContext) Response {
+func GenerateSQLTestData(c *m.ReqContext) Response {
 	if err := bus.Dispatch(&m.InsertSqlTestDataCommand{}); err != nil {
 	if err := bus.Dispatch(&m.InsertSqlTestDataCommand{}); err != nil {
 		return ApiError(500, "Failed to insert test data", err)
 		return ApiError(500, "Failed to insert test data", err)
 	}
 	}

+ 6 - 6
pkg/api/org.go

@@ -15,7 +15,7 @@ func GetOrgCurrent(c *m.ReqContext) Response {
 }
 }
 
 
 // GET /api/orgs/:orgId
 // GET /api/orgs/:orgId
-func GetOrgById(c *m.ReqContext) Response {
+func GetOrgByID(c *m.ReqContext) Response {
 	return getOrgHelper(c.ParamsInt64(":orgId"))
 	return getOrgHelper(c.ParamsInt64(":orgId"))
 }
 }
 
 
@@ -106,8 +106,8 @@ func UpdateOrg(c *m.ReqContext, form dtos.UpdateOrgForm) Response {
 	return updateOrgHelper(form, c.ParamsInt64(":orgId"))
 	return updateOrgHelper(form, c.ParamsInt64(":orgId"))
 }
 }
 
 
-func updateOrgHelper(form dtos.UpdateOrgForm, orgId int64) Response {
-	cmd := m.UpdateOrgCommand{Name: form.Name, OrgId: orgId}
+func updateOrgHelper(form dtos.UpdateOrgForm, orgID int64) Response {
+	cmd := m.UpdateOrgCommand{Name: form.Name, OrgId: orgID}
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		if err == m.ErrOrgNameTaken {
 		if err == m.ErrOrgNameTaken {
 			return ApiError(400, "Organization name taken", err)
 			return ApiError(400, "Organization name taken", err)
@@ -128,9 +128,9 @@ func UpdateOrgAddress(c *m.ReqContext, form dtos.UpdateOrgAddressForm) Response
 	return updateOrgAddressHelper(form, c.ParamsInt64(":orgId"))
 	return updateOrgAddressHelper(form, c.ParamsInt64(":orgId"))
 }
 }
 
 
-func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgId int64) Response {
+func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgID int64) Response {
 	cmd := m.UpdateOrgAddressCommand{
 	cmd := m.UpdateOrgAddressCommand{
-		OrgId: orgId,
+		OrgId: orgID,
 		Address: m.Address{
 		Address: m.Address{
 			Address1: form.Address1,
 			Address1: form.Address1,
 			Address2: form.Address2,
 			Address2: form.Address2,
@@ -149,7 +149,7 @@ func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgId int64) Respons
 }
 }
 
 
 // GET /api/orgs/:orgId
 // GET /api/orgs/:orgId
-func DeleteOrgById(c *m.ReqContext) Response {
+func DeleteOrgByID(c *m.ReqContext) Response {
 	if err := bus.Dispatch(&m.DeleteOrgCommand{Id: c.ParamsInt64(":orgId")}); err != nil {
 	if err := bus.Dispatch(&m.DeleteOrgCommand{Id: c.ParamsInt64(":orgId")}); err != nil {
 		if err == m.ErrOrgNotFound {
 		if err == m.ErrOrgNotFound {
 			return ApiError(404, "Failed to delete organization. ID not found", nil)
 			return ApiError(404, "Failed to delete organization. ID not found", nil)

+ 15 - 16
pkg/api/org_invite.go

@@ -96,26 +96,25 @@ func inviteExistingUserToOrg(c *m.ReqContext, user *m.User, inviteDto *dtos.AddI
 			return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err)
 			return ApiError(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err)
 		}
 		}
 		return ApiError(500, "Error while trying to create org user", err)
 		return ApiError(500, "Error while trying to create org user", err)
-	} else {
+	}
 
 
-		if inviteDto.SendEmail && util.IsEmail(user.Email) {
-			emailCmd := m.SendEmailCommand{
-				To:       []string{user.Email},
-				Template: "invited_to_org.html",
-				Data: map[string]interface{}{
-					"Name":      user.NameOrFallback(),
-					"OrgName":   c.OrgName,
-					"InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login),
-				},
-			}
-
-			if err := bus.Dispatch(&emailCmd); err != nil {
-				return ApiError(500, "Failed to send email invited_to_org", err)
-			}
+	if inviteDto.SendEmail && util.IsEmail(user.Email) {
+		emailCmd := m.SendEmailCommand{
+			To:       []string{user.Email},
+			Template: "invited_to_org.html",
+			Data: map[string]interface{}{
+				"Name":      user.NameOrFallback(),
+				"OrgName":   c.OrgName,
+				"InvitedBy": util.StringsFallback3(c.Name, c.Email, c.Login),
+			},
 		}
 		}
 
 
-		return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", user.NameOrFallback(), c.OrgName))
+		if err := bus.Dispatch(&emailCmd); err != nil {
+			return ApiError(500, "Failed to send email invited_to_org", err)
+		}
 	}
 	}
+
+	return ApiSuccess(fmt.Sprintf("Existing Grafana user %s added to org %s", user.NameOrFallback(), c.OrgName))
 }
 }
 
 
 func RevokeInvite(c *m.ReqContext) Response {
 func RevokeInvite(c *m.ReqContext) Response {

+ 9 - 9
pkg/api/org_users.go

@@ -53,9 +53,9 @@ func GetOrgUsers(c *m.ReqContext) Response {
 	return getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0)
 	return getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0)
 }
 }
 
 
-func getOrgUsersHelper(orgId int64, query string, limit int) Response {
+func getOrgUsersHelper(orgID int64, query string, limit int) Response {
 	q := m.GetOrgUsersQuery{
 	q := m.GetOrgUsersQuery{
-		OrgId: orgId,
+		OrgId: orgID,
 		Query: query,
 		Query: query,
 		Limit: limit,
 		Limit: limit,
 	}
 	}
@@ -102,19 +102,19 @@ func updateOrgUserHelper(cmd m.UpdateOrgUserCommand) Response {
 
 
 // DELETE /api/org/users/:userId
 // DELETE /api/org/users/:userId
 func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response {
 func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response {
-	userId := c.ParamsInt64(":userId")
-	return removeOrgUserHelper(c.OrgId, userId)
+	userID := c.ParamsInt64(":userId")
+	return removeOrgUserHelper(c.OrgId, userID)
 }
 }
 
 
 // DELETE /api/orgs/:orgId/users/:userId
 // DELETE /api/orgs/:orgId/users/:userId
 func RemoveOrgUser(c *m.ReqContext) Response {
 func RemoveOrgUser(c *m.ReqContext) Response {
-	userId := c.ParamsInt64(":userId")
-	orgId := c.ParamsInt64(":orgId")
-	return removeOrgUserHelper(orgId, userId)
+	userID := c.ParamsInt64(":userId")
+	orgID := c.ParamsInt64(":orgId")
+	return removeOrgUserHelper(orgID, userID)
 }
 }
 
 
-func removeOrgUserHelper(orgId int64, userId int64) Response {
-	cmd := m.RemoveOrgUserCommand{OrgId: orgId, UserId: userId}
+func removeOrgUserHelper(orgID int64, userID int64) Response {
+	cmd := m.RemoveOrgUserCommand{OrgId: orgID, UserId: userID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		if err == m.ErrLastOrgAdmin {
 		if err == m.ErrLastOrgAdmin {

+ 2 - 2
pkg/api/playlist.go

@@ -127,9 +127,9 @@ func GetPlaylistItems(c *m.ReqContext) Response {
 }
 }
 
 
 func GetPlaylistDashboards(c *m.ReqContext) Response {
 func GetPlaylistDashboards(c *m.ReqContext) Response {
-	playlistId := c.ParamsInt64(":id")
+	playlistID := c.ParamsInt64(":id")
 
 
-	playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistId)
+	playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistID)
 	if err != nil {
 	if err != nil {
 		return ApiError(500, "Could not load dashboards", err)
 		return ApiError(500, "Could not load dashboards", err)
 	}
 	}

+ 27 - 29
pkg/api/playlist_play.go

@@ -34,29 +34,27 @@ func populateDashboardsById(dashboardByIds []int64, dashboardIdOrder map[int64]i
 	return result, nil
 	return result, nil
 }
 }
 
 
-func populateDashboardsByTag(orgId int64, signedInUser *m.SignedInUser, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice {
+func populateDashboardsByTag(orgID int64, signedInUser *m.SignedInUser, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice {
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 
 
-	if len(dashboardByTag) > 0 {
-		for _, tag := range dashboardByTag {
-			searchQuery := search.Query{
-				Title:        "",
-				Tags:         []string{tag},
-				SignedInUser: signedInUser,
-				Limit:        100,
-				IsStarred:    false,
-				OrgId:        orgId,
-			}
+	for _, tag := range dashboardByTag {
+		searchQuery := search.Query{
+			Title:        "",
+			Tags:         []string{tag},
+			SignedInUser: signedInUser,
+			Limit:        100,
+			IsStarred:    false,
+			OrgId:        orgID,
+		}
 
 
-			if err := bus.Dispatch(&searchQuery); err == nil {
-				for _, item := range searchQuery.Result {
-					result = append(result, dtos.PlaylistDashboard{
-						Id:    item.Id,
-						Title: item.Title,
-						Uri:   item.Uri,
-						Order: dashboardTagOrder[tag],
-					})
-				}
+		if err := bus.Dispatch(&searchQuery); err == nil {
+			for _, item := range searchQuery.Result {
+				result = append(result, dtos.PlaylistDashboard{
+					Id:    item.Id,
+					Title: item.Title,
+					Uri:   item.Uri,
+					Order: dashboardTagOrder[tag],
+				})
 			}
 			}
 		}
 		}
 	}
 	}
@@ -64,19 +62,19 @@ func populateDashboardsByTag(orgId int64, signedInUser *m.SignedInUser, dashboar
 	return result
 	return result
 }
 }
 
 
-func LoadPlaylistDashboards(orgId int64, signedInUser *m.SignedInUser, playlistId int64) (dtos.PlaylistDashboardsSlice, error) {
-	playlistItems, _ := LoadPlaylistItems(playlistId)
+func LoadPlaylistDashboards(orgID int64, signedInUser *m.SignedInUser, playlistID int64) (dtos.PlaylistDashboardsSlice, error) {
+	playlistItems, _ := LoadPlaylistItems(playlistID)
 
 
-	dashboardByIds := make([]int64, 0)
+	dashboardByIDs := make([]int64, 0)
 	dashboardByTag := make([]string, 0)
 	dashboardByTag := make([]string, 0)
-	dashboardIdOrder := make(map[int64]int)
+	dashboardIDOrder := make(map[int64]int)
 	dashboardTagOrder := make(map[string]int)
 	dashboardTagOrder := make(map[string]int)
 
 
 	for _, i := range playlistItems {
 	for _, i := range playlistItems {
 		if i.Type == "dashboard_by_id" {
 		if i.Type == "dashboard_by_id" {
-			dashboardId, _ := strconv.ParseInt(i.Value, 10, 64)
-			dashboardByIds = append(dashboardByIds, dashboardId)
-			dashboardIdOrder[dashboardId] = i.Order
+			dashboardID, _ := strconv.ParseInt(i.Value, 10, 64)
+			dashboardByIDs = append(dashboardByIDs, dashboardID)
+			dashboardIDOrder[dashboardID] = i.Order
 		}
 		}
 
 
 		if i.Type == "dashboard_by_tag" {
 		if i.Type == "dashboard_by_tag" {
@@ -87,9 +85,9 @@ func LoadPlaylistDashboards(orgId int64, signedInUser *m.SignedInUser, playlistI
 
 
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 	result := make(dtos.PlaylistDashboardsSlice, 0)
 
 
-	var k, _ = populateDashboardsById(dashboardByIds, dashboardIdOrder)
+	var k, _ = populateDashboardsById(dashboardByIDs, dashboardIDOrder)
 	result = append(result, k...)
 	result = append(result, k...)
-	result = append(result, populateDashboardsByTag(orgId, signedInUser, dashboardByTag, dashboardTagOrder)...)
+	result = append(result, populateDashboardsByTag(orgID, signedInUser, dashboardByTag, dashboardTagOrder)...)
 
 
 	sort.Sort(result)
 	sort.Sort(result)
 	return result, nil
 	return result, nil

+ 43 - 41
pkg/api/plugins.go

@@ -78,48 +78,48 @@ func GetPluginList(c *m.ReqContext) Response {
 	return Json(200, result)
 	return Json(200, result)
 }
 }
 
 
-func GetPluginSettingById(c *m.ReqContext) Response {
-	pluginId := c.Params(":pluginId")
+func GetPluginSettingByID(c *m.ReqContext) Response {
+	pluginID := c.Params(":pluginId")
 
 
-	if def, exists := plugins.Plugins[pluginId]; !exists {
+	def, exists := plugins.Plugins[pluginID]
+	if !exists {
 		return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
 		return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
-	} else {
+	}
 
 
-		dto := &dtos.PluginSetting{
-			Type:          def.Type,
-			Id:            def.Id,
-			Name:          def.Name,
-			Info:          &def.Info,
-			Dependencies:  &def.Dependencies,
-			Includes:      def.Includes,
-			BaseUrl:       def.BaseUrl,
-			Module:        def.Module,
-			DefaultNavUrl: def.DefaultNavUrl,
-			LatestVersion: def.GrafanaNetVersion,
-			HasUpdate:     def.GrafanaNetHasUpdate,
-			State:         def.State,
-		}
+	dto := &dtos.PluginSetting{
+		Type:          def.Type,
+		Id:            def.Id,
+		Name:          def.Name,
+		Info:          &def.Info,
+		Dependencies:  &def.Dependencies,
+		Includes:      def.Includes,
+		BaseUrl:       def.BaseUrl,
+		Module:        def.Module,
+		DefaultNavUrl: def.DefaultNavUrl,
+		LatestVersion: def.GrafanaNetVersion,
+		HasUpdate:     def.GrafanaNetHasUpdate,
+		State:         def.State,
+	}
 
 
-		query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
-		if err := bus.Dispatch(&query); err != nil {
-			if err != m.ErrPluginSettingNotFound {
-				return ApiError(500, "Failed to get login settings", nil)
-			}
-		} else {
-			dto.Enabled = query.Result.Enabled
-			dto.Pinned = query.Result.Pinned
-			dto.JsonData = query.Result.JsonData
+	query := m.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: c.OrgId}
+	if err := bus.Dispatch(&query); err != nil {
+		if err != m.ErrPluginSettingNotFound {
+			return ApiError(500, "Failed to get login settings", nil)
 		}
 		}
-
-		return Json(200, dto)
+	} else {
+		dto.Enabled = query.Result.Enabled
+		dto.Pinned = query.Result.Pinned
+		dto.JsonData = query.Result.JsonData
 	}
 	}
+
+	return Json(200, dto)
 }
 }
 
 
 func UpdatePluginSetting(c *m.ReqContext, cmd m.UpdatePluginSettingCmd) Response {
 func UpdatePluginSetting(c *m.ReqContext, cmd m.UpdatePluginSettingCmd) Response {
-	pluginId := c.Params(":pluginId")
+	pluginID := c.Params(":pluginId")
 
 
 	cmd.OrgId = c.OrgId
 	cmd.OrgId = c.OrgId
-	cmd.PluginId = pluginId
+	cmd.PluginId = pluginID
 
 
 	if _, ok := plugins.Apps[cmd.PluginId]; !ok {
 	if _, ok := plugins.Apps[cmd.PluginId]; !ok {
 		return ApiError(404, "Plugin not installed.", nil)
 		return ApiError(404, "Plugin not installed.", nil)
@@ -133,34 +133,36 @@ func UpdatePluginSetting(c *m.ReqContext, cmd m.UpdatePluginSettingCmd) Response
 }
 }
 
 
 func GetPluginDashboards(c *m.ReqContext) Response {
 func GetPluginDashboards(c *m.ReqContext) Response {
-	pluginId := c.Params(":pluginId")
+	pluginID := c.Params(":pluginId")
 
 
-	if list, err := plugins.GetPluginDashboards(c.OrgId, pluginId); err != nil {
+	list, err := plugins.GetPluginDashboards(c.OrgId, pluginID)
+	if err != nil {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 			return ApiError(404, notfound.Error(), nil)
 			return ApiError(404, notfound.Error(), nil)
 		}
 		}
 
 
 		return ApiError(500, "Failed to get plugin dashboards", err)
 		return ApiError(500, "Failed to get plugin dashboards", err)
-	} else {
-		return Json(200, list)
 	}
 	}
+
+	return Json(200, list)
 }
 }
 
 
 func GetPluginMarkdown(c *m.ReqContext) Response {
 func GetPluginMarkdown(c *m.ReqContext) Response {
-	pluginId := c.Params(":pluginId")
+	pluginID := c.Params(":pluginId")
 	name := c.Params(":name")
 	name := c.Params(":name")
 
 
-	if content, err := plugins.GetPluginMarkdown(pluginId, name); err != nil {
+	content, err := plugins.GetPluginMarkdown(pluginID, name)
+	if err != nil {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 		if notfound, ok := err.(plugins.PluginNotFoundError); ok {
 			return ApiError(404, notfound.Error(), nil)
 			return ApiError(404, notfound.Error(), nil)
 		}
 		}
 
 
 		return ApiError(500, "Could not get markdown file", err)
 		return ApiError(500, "Could not get markdown file", err)
-	} else {
-		resp := Respond(200, content)
-		resp.Header("Content-Type", "text/plain; charset=utf-8")
-		return resp
 	}
 	}
+
+	resp := Respond(200, content)
+	resp.Header("Content-Type", "text/plain; charset=utf-8")
+	return resp
 }
 }
 
 
 func ImportDashboard(c *m.ReqContext, apiCmd dtos.ImportDashboardCommand) Response {
 func ImportDashboard(c *m.ReqContext, apiCmd dtos.ImportDashboardCommand) Response {

+ 5 - 5
pkg/api/preferences.go

@@ -24,8 +24,8 @@ func GetUserPreferences(c *m.ReqContext) Response {
 	return getPreferencesFor(c.OrgId, c.UserId)
 	return getPreferencesFor(c.OrgId, c.UserId)
 }
 }
 
 
-func getPreferencesFor(orgId int64, userId int64) Response {
-	prefsQuery := m.GetPreferencesQuery{UserId: userId, OrgId: orgId}
+func getPreferencesFor(orgID int64, userID int64) Response {
+	prefsQuery := m.GetPreferencesQuery{UserId: userID, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&prefsQuery); err != nil {
 	if err := bus.Dispatch(&prefsQuery); err != nil {
 		return ApiError(500, "Failed to get preferences", err)
 		return ApiError(500, "Failed to get preferences", err)
@@ -45,10 +45,10 @@ func UpdateUserPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response
 	return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd)
 	return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd)
 }
 }
 
 
-func updatePreferencesFor(orgId int64, userId int64, dtoCmd *dtos.UpdatePrefsCmd) Response {
+func updatePreferencesFor(orgID int64, userID int64, dtoCmd *dtos.UpdatePrefsCmd) Response {
 	saveCmd := m.SavePreferencesCommand{
 	saveCmd := m.SavePreferencesCommand{
-		UserId:          userId,
-		OrgId:           orgId,
+		UserId:          userID,
+		OrgId:           orgID,
 		Theme:           dtoCmd.Theme,
 		Theme:           dtoCmd.Theme,
 		Timezone:        dtoCmd.Timezone,
 		Timezone:        dtoCmd.Timezone,
 		HomeDashboardId: dtoCmd.HomeDashboardId,
 		HomeDashboardId: dtoCmd.HomeDashboardId,

+ 8 - 8
pkg/api/search.go

@@ -25,19 +25,19 @@ func Search(c *m.ReqContext) {
 		permission = m.PERMISSION_EDIT
 		permission = m.PERMISSION_EDIT
 	}
 	}
 
 
-	dbids := make([]int64, 0)
+	dbIDs := make([]int64, 0)
 	for _, id := range c.QueryStrings("dashboardIds") {
 	for _, id := range c.QueryStrings("dashboardIds") {
-		dashboardId, err := strconv.ParseInt(id, 10, 64)
+		dashboardID, err := strconv.ParseInt(id, 10, 64)
 		if err == nil {
 		if err == nil {
-			dbids = append(dbids, dashboardId)
+			dbIDs = append(dbIDs, dashboardID)
 		}
 		}
 	}
 	}
 
 
-	folderIds := make([]int64, 0)
+	folderIDs := make([]int64, 0)
 	for _, id := range c.QueryStrings("folderIds") {
 	for _, id := range c.QueryStrings("folderIds") {
-		folderId, err := strconv.ParseInt(id, 10, 64)
+		folderID, err := strconv.ParseInt(id, 10, 64)
 		if err == nil {
 		if err == nil {
-			folderIds = append(folderIds, folderId)
+			folderIDs = append(folderIDs, folderID)
 		}
 		}
 	}
 	}
 
 
@@ -48,9 +48,9 @@ func Search(c *m.ReqContext) {
 		Limit:        limit,
 		Limit:        limit,
 		IsStarred:    starred == "true",
 		IsStarred:    starred == "true",
 		OrgId:        c.OrgId,
 		OrgId:        c.OrgId,
-		DashboardIds: dbids,
+		DashboardIds: dbIDs,
 		Type:         dashboardType,
 		Type:         dashboardType,
-		FolderIds:    folderIds,
+		FolderIds:    folderIDs,
 		Permission:   permission,
 		Permission:   permission,
 	}
 	}
 
 

+ 2 - 2
pkg/api/team.go

@@ -38,7 +38,7 @@ func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response {
 }
 }
 
 
 // DELETE /api/teams/:teamId
 // DELETE /api/teams/:teamId
-func DeleteTeamById(c *m.ReqContext) Response {
+func DeleteTeamByID(c *m.ReqContext) Response {
 	if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}); err != nil {
 	if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}); err != nil {
 		if err == m.ErrTeamNotFound {
 		if err == m.ErrTeamNotFound {
 			return ApiError(404, "Failed to delete Team. ID not found", nil)
 			return ApiError(404, "Failed to delete Team. ID not found", nil)
@@ -82,7 +82,7 @@ func SearchTeams(c *m.ReqContext) Response {
 }
 }
 
 
 // GET /api/teams/:teamId
 // GET /api/teams/:teamId
-func GetTeamById(c *m.ReqContext) Response {
+func GetTeamByID(c *m.ReqContext) Response {
 	query := m.GetTeamByIdQuery{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}
 	query := m.GetTeamByIdQuery{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {

+ 18 - 18
pkg/api/user.go

@@ -14,12 +14,12 @@ func GetSignedInUser(c *m.ReqContext) Response {
 }
 }
 
 
 // GET /api/users/:id
 // GET /api/users/:id
-func GetUserById(c *m.ReqContext) Response {
+func GetUserByID(c *m.ReqContext) Response {
 	return getUserUserProfile(c.ParamsInt64(":id"))
 	return getUserUserProfile(c.ParamsInt64(":id"))
 }
 }
 
 
-func getUserUserProfile(userId int64) Response {
-	query := m.GetUserProfileQuery{UserId: userId}
+func getUserUserProfile(userID int64) Response {
+	query := m.GetUserProfileQuery{UserId: userID}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		if err == m.ErrUserNotFound {
 		if err == m.ErrUserNotFound {
@@ -75,14 +75,14 @@ func UpdateUser(c *m.ReqContext, cmd m.UpdateUserCommand) Response {
 
 
 //POST /api/users/:id/using/:orgId
 //POST /api/users/:id/using/:orgId
 func UpdateUserActiveOrg(c *m.ReqContext) Response {
 func UpdateUserActiveOrg(c *m.ReqContext) Response {
-	userId := c.ParamsInt64(":id")
-	orgId := c.ParamsInt64(":orgId")
+	userID := c.ParamsInt64(":id")
+	orgID := c.ParamsInt64(":orgId")
 
 
-	if !validateUsingOrg(userId, orgId) {
+	if !validateUsingOrg(userID, orgID) {
 		return ApiError(401, "Not a valid organization", nil)
 		return ApiError(401, "Not a valid organization", nil)
 	}
 	}
 
 
-	cmd := m.SetUsingOrgCommand{UserId: userId, OrgId: orgId}
+	cmd := m.SetUsingOrgCommand{UserId: userID, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		return ApiError(500, "Failed to change active organization", err)
 		return ApiError(500, "Failed to change active organization", err)
@@ -116,8 +116,8 @@ func GetUserOrgList(c *m.ReqContext) Response {
 	return getUserOrgList(c.ParamsInt64(":id"))
 	return getUserOrgList(c.ParamsInt64(":id"))
 }
 }
 
 
-func getUserOrgList(userId int64) Response {
-	query := m.GetUserOrgListQuery{UserId: userId}
+func getUserOrgList(userID int64) Response {
+	query := m.GetUserOrgListQuery{UserId: userID}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return ApiError(500, "Failed to get user organizations", err)
 		return ApiError(500, "Failed to get user organizations", err)
@@ -126,8 +126,8 @@ func getUserOrgList(userId int64) Response {
 	return Json(200, query.Result)
 	return Json(200, query.Result)
 }
 }
 
 
-func validateUsingOrg(userId int64, orgId int64) bool {
-	query := m.GetUserOrgListQuery{UserId: userId}
+func validateUsingOrg(userID int64, orgID int64) bool {
+	query := m.GetUserOrgListQuery{UserId: userID}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return false
 		return false
@@ -136,7 +136,7 @@ func validateUsingOrg(userId int64, orgId int64) bool {
 	// validate that the org id in the list
 	// validate that the org id in the list
 	valid := false
 	valid := false
 	for _, other := range query.Result {
 	for _, other := range query.Result {
-		if other.OrgId == orgId {
+		if other.OrgId == orgID {
 			valid = true
 			valid = true
 		}
 		}
 	}
 	}
@@ -146,13 +146,13 @@ func validateUsingOrg(userId int64, orgId int64) bool {
 
 
 // POST /api/user/using/:id
 // POST /api/user/using/:id
 func UserSetUsingOrg(c *m.ReqContext) Response {
 func UserSetUsingOrg(c *m.ReqContext) Response {
-	orgId := c.ParamsInt64(":id")
+	orgID := c.ParamsInt64(":id")
 
 
-	if !validateUsingOrg(c.UserId, orgId) {
+	if !validateUsingOrg(c.UserId, orgID) {
 		return ApiError(401, "Not a valid organization", nil)
 		return ApiError(401, "Not a valid organization", nil)
 	}
 	}
 
 
-	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgId}
+	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		return ApiError(500, "Failed to change active organization", err)
 		return ApiError(500, "Failed to change active organization", err)
@@ -163,13 +163,13 @@ func UserSetUsingOrg(c *m.ReqContext) Response {
 
 
 // GET /profile/switch-org/:id
 // GET /profile/switch-org/:id
 func ChangeActiveOrgAndRedirectToHome(c *m.ReqContext) {
 func ChangeActiveOrgAndRedirectToHome(c *m.ReqContext) {
-	orgId := c.ParamsInt64(":id")
+	orgID := c.ParamsInt64(":id")
 
 
-	if !validateUsingOrg(c.UserId, orgId) {
+	if !validateUsingOrg(c.UserId, orgID) {
 		NotFoundHandler(c)
 		NotFoundHandler(c)
 	}
 	}
 
 
-	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgId}
+	cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgID}
 
 
 	if err := bus.Dispatch(&cmd); err != nil {
 	if err := bus.Dispatch(&cmd); err != nil {
 		NotFoundHandler(c)
 		NotFoundHandler(c)

+ 1 - 1
pkg/cmd/grafana-server/server.go

@@ -120,7 +120,7 @@ func (g *GrafanaServerImpl) initLogging() {
 }
 }
 
 
 func (g *GrafanaServerImpl) startHttpServer() error {
 func (g *GrafanaServerImpl) startHttpServer() error {
-	g.httpServer = api.NewHttpServer()
+	g.httpServer = api.NewHTTPServer()
 
 
 	err := g.httpServer.Start(g.context)
 	err := g.httpServer.Start(g.context)
 
 

+ 3 - 3
pkg/middleware/auth.go

@@ -17,10 +17,10 @@ type AuthOptions struct {
 }
 }
 
 
 func getRequestUserId(c *m.ReqContext) int64 {
 func getRequestUserId(c *m.ReqContext) int64 {
-	userId := c.Session.Get(session.SESS_KEY_USERID)
+	userID := c.Session.Get(session.SESS_KEY_USERID)
 
 
-	if userId != nil {
-		return userId.(int64)
+	if userID != nil {
+		return userID.(int64)
 	}
 	}
 
 
 	return 0
 	return 0

+ 1 - 1
pkg/middleware/dashboard_redirect.go

@@ -20,7 +20,7 @@ func getDashboardUrlBySlug(orgId int64, slug string) (string, error) {
 	return m.GetDashboardUrl(query.Result.Uid, query.Result.Slug), nil
 	return m.GetDashboardUrl(query.Result.Uid, query.Result.Slug), nil
 }
 }
 
 
-func RedirectFromLegacyDashboardUrl() macaron.Handler {
+func RedirectFromLegacyDashboardURL() macaron.Handler {
 	return func(c *m.ReqContext) {
 	return func(c *m.ReqContext) {
 		slug := c.Params("slug")
 		slug := c.Params("slug")
 
 

+ 9 - 9
pkg/middleware/dashboard_redirect_test.go

@@ -13,7 +13,7 @@ import (
 func TestMiddlewareDashboardRedirect(t *testing.T) {
 func TestMiddlewareDashboardRedirect(t *testing.T) {
 	Convey("Given the dashboard redirect middleware", t, func() {
 	Convey("Given the dashboard redirect middleware", t, func() {
 		bus.ClearBusHandlers()
 		bus.ClearBusHandlers()
-		redirectFromLegacyDashboardUrl := RedirectFromLegacyDashboardUrl()
+		redirectFromLegacyDashboardUrl := RedirectFromLegacyDashboardURL()
 		redirectFromLegacyDashboardSoloUrl := RedirectFromLegacyDashboardSoloUrl()
 		redirectFromLegacyDashboardSoloUrl := RedirectFromLegacyDashboardSoloUrl()
 
 
 		fakeDash := m.NewDashboard("Child dash")
 		fakeDash := m.NewDashboard("Child dash")
@@ -34,9 +34,9 @@ func TestMiddlewareDashboardRedirect(t *testing.T) {
 
 
 			Convey("Should redirect to new dashboard url with a 301 Moved Permanently", func() {
 			Convey("Should redirect to new dashboard url with a 301 Moved Permanently", func() {
 				So(sc.resp.Code, ShouldEqual, 301)
 				So(sc.resp.Code, ShouldEqual, 301)
-				redirectUrl, _ := sc.resp.Result().Location()
-				So(redirectUrl.Path, ShouldEqual, m.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug))
-				So(len(redirectUrl.Query()), ShouldEqual, 2)
+				redirectURL, _ := sc.resp.Result().Location()
+				So(redirectURL.Path, ShouldEqual, m.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug))
+				So(len(redirectURL.Query()), ShouldEqual, 2)
 			})
 			})
 		})
 		})
 
 
@@ -47,11 +47,11 @@ func TestMiddlewareDashboardRedirect(t *testing.T) {
 
 
 			Convey("Should redirect to new dashboard url with a 301 Moved Permanently", func() {
 			Convey("Should redirect to new dashboard url with a 301 Moved Permanently", func() {
 				So(sc.resp.Code, ShouldEqual, 301)
 				So(sc.resp.Code, ShouldEqual, 301)
-				redirectUrl, _ := sc.resp.Result().Location()
-				expectedUrl := m.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug)
-				expectedUrl = strings.Replace(expectedUrl, "/d/", "/d-solo/", 1)
-				So(redirectUrl.Path, ShouldEqual, expectedUrl)
-				So(len(redirectUrl.Query()), ShouldEqual, 2)
+				redirectURL, _ := sc.resp.Result().Location()
+				expectedURL := m.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug)
+				expectedURL = strings.Replace(expectedURL, "/d/", "/d-solo/", 1)
+				So(redirectURL.Path, ShouldEqual, expectedURL)
+				So(len(redirectURL.Query()), ShouldEqual, 2)
 			})
 			})
 		})
 		})
 	})
 	})

+ 1 - 1
pkg/middleware/middleware_test.go

@@ -338,7 +338,7 @@ func middlewareScenario(desc string, fn scenarioFunc) {
 		sc.m.Use(GetContextHandler())
 		sc.m.Use(GetContextHandler())
 		// mock out gc goroutine
 		// mock out gc goroutine
 		session.StartSessionGC = func() {}
 		session.StartSessionGC = func() {}
-		sc.m.Use(Sessioner(&ms.Options{}))
+		sc.m.Use(Sessioner(&ms.Options{}, 0))
 		sc.m.Use(OrgRedirect())
 		sc.m.Use(OrgRedirect())
 		sc.m.Use(AddDefaultResponseHeaders())
 		sc.m.Use(AddDefaultResponseHeaders())
 
 

+ 7 - 7
pkg/middleware/recovery_test.go

@@ -14,10 +14,10 @@ import (
 
 
 func TestRecoveryMiddleware(t *testing.T) {
 func TestRecoveryMiddleware(t *testing.T) {
 	Convey("Given an api route that panics", t, func() {
 	Convey("Given an api route that panics", t, func() {
-		apiUrl := "/api/whatever"
-		recoveryScenario("recovery middleware should return json", apiUrl, func(sc *scenarioContext) {
+		apiURL := "/api/whatever"
+		recoveryScenario("recovery middleware should return json", apiURL, func(sc *scenarioContext) {
 			sc.handlerFunc = PanicHandler
 			sc.handlerFunc = PanicHandler
-			sc.fakeReq("GET", apiUrl).exec()
+			sc.fakeReq("GET", apiURL).exec()
 			sc.req.Header.Add("content-type", "application/json")
 			sc.req.Header.Add("content-type", "application/json")
 
 
 			So(sc.resp.Code, ShouldEqual, 500)
 			So(sc.resp.Code, ShouldEqual, 500)
@@ -27,10 +27,10 @@ func TestRecoveryMiddleware(t *testing.T) {
 	})
 	})
 
 
 	Convey("Given a non-api route that panics", t, func() {
 	Convey("Given a non-api route that panics", t, func() {
-		apiUrl := "/whatever"
-		recoveryScenario("recovery middleware should return html", apiUrl, func(sc *scenarioContext) {
+		apiURL := "/whatever"
+		recoveryScenario("recovery middleware should return html", apiURL, func(sc *scenarioContext) {
 			sc.handlerFunc = PanicHandler
 			sc.handlerFunc = PanicHandler
-			sc.fakeReq("GET", apiUrl).exec()
+			sc.fakeReq("GET", apiURL).exec()
 
 
 			So(sc.resp.Code, ShouldEqual, 500)
 			So(sc.resp.Code, ShouldEqual, 500)
 			So(sc.resp.Header().Get("content-type"), ShouldEqual, "text/html; charset=UTF-8")
 			So(sc.resp.Header().Get("content-type"), ShouldEqual, "text/html; charset=UTF-8")
@@ -63,7 +63,7 @@ func recoveryScenario(desc string, url string, fn scenarioFunc) {
 		sc.m.Use(GetContextHandler())
 		sc.m.Use(GetContextHandler())
 		// mock out gc goroutine
 		// mock out gc goroutine
 		session.StartSessionGC = func() {}
 		session.StartSessionGC = func() {}
-		sc.m.Use(Sessioner(&ms.Options{}))
+		sc.m.Use(Sessioner(&ms.Options{}, 0))
 		sc.m.Use(OrgRedirect())
 		sc.m.Use(OrgRedirect())
 		sc.m.Use(AddDefaultResponseHeaders())
 		sc.m.Use(AddDefaultResponseHeaders())
 
 

+ 2 - 2
pkg/middleware/session.go

@@ -8,8 +8,8 @@ import (
 	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/services/session"
 )
 )
 
 
-func Sessioner(options *ms.Options) macaron.Handler {
-	session.Init(options)
+func Sessioner(options *ms.Options, sessionConnMaxLifetime int64) macaron.Handler {
+	session.Init(options, sessionConnMaxLifetime)
 
 
 	return func(ctx *m.ReqContext) {
 	return func(ctx *m.ReqContext) {
 		ctx.Next()
 		ctx.Next()

+ 26 - 3
vendor/github.com/go-macaron/session/mysql/mysql.go → pkg/services/session/mysql.go

@@ -108,6 +108,7 @@ func (p *MysqlProvider) Init(expire int64, connStr string) (err error) {
 	p.expire = expire
 	p.expire = expire
 
 
 	p.c, err = sql.Open("mysql", connStr)
 	p.c, err = sql.Open("mysql", connStr)
+	p.c.SetConnMaxLifetime(time.Second * time.Duration(sessionConnMaxLifetime))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -141,12 +142,29 @@ func (p *MysqlProvider) Read(sid string) (session.RawStore, error) {
 
 
 // Exist returns true if session with given ID exists.
 // Exist returns true if session with given ID exists.
 func (p *MysqlProvider) Exist(sid string) bool {
 func (p *MysqlProvider) Exist(sid string) bool {
+	exists, err := p.queryExists(sid)
+
+	if err != nil {
+		exists, err = p.queryExists(sid)
+	}
+
+	if err != nil {
+		log.Printf("session/mysql: error checking if session exists: %v", err)
+		return false
+	}
+
+	return exists
+}
+
+func (p *MysqlProvider) queryExists(sid string) (bool, error) {
 	var data []byte
 	var data []byte
 	err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data)
 	err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data)
+
 	if err != nil && err != sql.ErrNoRows {
 	if err != nil && err != sql.ErrNoRows {
-		panic("session/mysql: error checking existence: " + err.Error())
+		return false, err
 	}
 	}
-	return err != sql.ErrNoRows
+
+	return err != sql.ErrNoRows, nil
 }
 }
 
 
 // Destory deletes a session by session ID.
 // Destory deletes a session by session ID.
@@ -185,7 +203,12 @@ func (p *MysqlProvider) Count() (total int) {
 
 
 // GC calls GC to clean expired sessions.
 // GC calls GC to clean expired sessions.
 func (p *MysqlProvider) GC() {
 func (p *MysqlProvider) GC() {
-	if _, err := p.c.Exec("DELETE FROM session WHERE  expiry + ? <= UNIX_TIMESTAMP(NOW())", p.expire); err != nil {
+	var err error
+	if _, err = p.c.Exec("DELETE FROM session WHERE  expiry + ? <= UNIX_TIMESTAMP(NOW())", p.expire); err != nil {
+		_, err = p.c.Exec("DELETE FROM session WHERE  expiry + ? <= UNIX_TIMESTAMP(NOW())", p.expire)
+	}
+
+	if err != nil {
 		log.Printf("session/mysql: error garbage collecting: %v", err)
 		log.Printf("session/mysql: error garbage collecting: %v", err)
 	}
 	}
 }
 }

+ 3 - 2
pkg/services/session/session.go

@@ -6,7 +6,6 @@ import (
 
 
 	ms "github.com/go-macaron/session"
 	ms "github.com/go-macaron/session"
 	_ "github.com/go-macaron/session/memcache"
 	_ "github.com/go-macaron/session/memcache"
-	_ "github.com/go-macaron/session/mysql"
 	_ "github.com/go-macaron/session/postgres"
 	_ "github.com/go-macaron/session/postgres"
 	_ "github.com/go-macaron/session/redis"
 	_ "github.com/go-macaron/session/redis"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
@@ -25,6 +24,7 @@ var sessionOptions *ms.Options
 var StartSessionGC func()
 var StartSessionGC func()
 var GetSessionCount func() int
 var GetSessionCount func() int
 var sessionLogger = log.New("session")
 var sessionLogger = log.New("session")
+var sessionConnMaxLifetime int64
 
 
 func init() {
 func init() {
 	StartSessionGC = func() {
 	StartSessionGC = func() {
@@ -37,9 +37,10 @@ func init() {
 	}
 	}
 }
 }
 
 
-func Init(options *ms.Options) {
+func Init(options *ms.Options, connMaxLifetime int64) {
 	var err error
 	var err error
 	sessionOptions = prepareOptions(options)
 	sessionOptions = prepareOptions(options)
+	sessionConnMaxLifetime = connMaxLifetime
 	sessionManager, err = ms.NewManager(options.Provider, *options)
 	sessionManager, err = ms.NewManager(options.Provider, *options)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)

+ 1 - 1
pkg/services/sqlstore/alert.go

@@ -255,7 +255,7 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error {
 		}
 		}
 
 
 		alert.State = cmd.State
 		alert.State = cmd.State
-		alert.StateChanges += 1
+		alert.StateChanges++
 		alert.NewStateDate = timeNow()
 		alert.NewStateDate = timeNow()
 		alert.EvalData = cmd.EvalData
 		alert.EvalData = cmd.EvalData
 
 

+ 1 - 0
pkg/services/sqlstore/dashboard_folder_test.go

@@ -46,6 +46,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
 						OrgId:        1, DashboardIds: []int64{folder.Id, dashInRoot.Id},
 						OrgId:        1, DashboardIds: []int64{folder.Id, dashInRoot.Id},
 					}
 					}
 					err := SearchDashboards(query)
 					err := SearchDashboards(query)
+
 					So(err, ShouldBeNil)
 					So(err, ShouldBeNil)
 					So(len(query.Result), ShouldEqual, 1)
 					So(len(query.Result), ShouldEqual, 1)
 					So(query.Result[0].Id, ShouldEqual, dashInRoot.Id)
 					So(query.Result[0].Id, ShouldEqual, dashInRoot.Id)

+ 1 - 1
pkg/services/sqlstore/migrator/migrator.go

@@ -125,7 +125,7 @@ func (mg *Migrator) exec(m Migration, sess *xorm.Session) error {
 	condition := m.GetCondition()
 	condition := m.GetCondition()
 	if condition != nil {
 	if condition != nil {
 		sql, args := condition.Sql(mg.dialect)
 		sql, args := condition.Sql(mg.dialect)
-		results, err := sess.Query(sql, args...)
+		results, err := sess.SQL(sql).Query(args...)
 		if err != nil || len(results) == 0 {
 		if err != nil || len(results) == 0 {
 			mg.Logger.Info("Skipping migration condition not fulfilled", "id", m.Id())
 			mg.Logger.Info("Skipping migration condition not fulfilled", "id", m.Id())
 			return sess.Rollback()
 			return sess.Rollback()

+ 3 - 0
pkg/services/sqlstore/org_test.go

@@ -2,6 +2,7 @@ package sqlstore
 
 
 import (
 import (
 	"testing"
 	"testing"
+	"time"
 
 
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
 
 
@@ -241,6 +242,8 @@ func TestAccountDataAccess(t *testing.T) {
 func testHelperUpdateDashboardAcl(dashboardId int64, items ...m.DashboardAcl) error {
 func testHelperUpdateDashboardAcl(dashboardId int64, items ...m.DashboardAcl) error {
 	cmd := m.UpdateDashboardAclCommand{DashboardId: dashboardId}
 	cmd := m.UpdateDashboardAclCommand{DashboardId: dashboardId}
 	for _, item := range items {
 	for _, item := range items {
+		item.Created = time.Now()
+		item.Updated = time.Now()
 		cmd.Items = append(cmd.Items, &item)
 		cmd.Items = append(cmd.Items, &item)
 	}
 	}
 	return UpdateDashboardAcl(&cmd)
 	return UpdateDashboardAcl(&cmd)

+ 9 - 4
pkg/services/sqlstore/quota.go

@@ -2,6 +2,7 @@ package sqlstore
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"time"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
@@ -98,8 +99,9 @@ func UpdateOrgQuota(cmd *m.UpdateOrgQuotaCmd) error {
 	return inTransaction(func(sess *DBSession) error {
 	return inTransaction(func(sess *DBSession) error {
 		//Check if quota is already defined in the DB
 		//Check if quota is already defined in the DB
 		quota := m.Quota{
 		quota := m.Quota{
-			Target: cmd.Target,
-			OrgId:  cmd.OrgId,
+			Target:  cmd.Target,
+			OrgId:   cmd.OrgId,
+			Updated: time.Now(),
 		}
 		}
 		has, err := sess.Get(&quota)
 		has, err := sess.Get(&quota)
 		if err != nil {
 		if err != nil {
@@ -107,6 +109,7 @@ func UpdateOrgQuota(cmd *m.UpdateOrgQuotaCmd) error {
 		}
 		}
 		quota.Limit = cmd.Limit
 		quota.Limit = cmd.Limit
 		if has == false {
 		if has == false {
+			quota.Created = time.Now()
 			//No quota in the DB for this target, so create a new one.
 			//No quota in the DB for this target, so create a new one.
 			if _, err := sess.Insert(&quota); err != nil {
 			if _, err := sess.Insert(&quota); err != nil {
 				return err
 				return err
@@ -198,8 +201,9 @@ func UpdateUserQuota(cmd *m.UpdateUserQuotaCmd) error {
 	return inTransaction(func(sess *DBSession) error {
 	return inTransaction(func(sess *DBSession) error {
 		//Check if quota is already defined in the DB
 		//Check if quota is already defined in the DB
 		quota := m.Quota{
 		quota := m.Quota{
-			Target: cmd.Target,
-			UserId: cmd.UserId,
+			Target:  cmd.Target,
+			UserId:  cmd.UserId,
+			Updated: time.Now(),
 		}
 		}
 		has, err := sess.Get(&quota)
 		has, err := sess.Get(&quota)
 		if err != nil {
 		if err != nil {
@@ -207,6 +211,7 @@ func UpdateUserQuota(cmd *m.UpdateUserQuotaCmd) error {
 		}
 		}
 		quota.Limit = cmd.Limit
 		quota.Limit = cmd.Limit
 		if has == false {
 		if has == false {
+			quota.Created = time.Now()
 			//No quota in the DB for this target, so create a new one.
 			//No quota in the DB for this target, so create a new one.
 			if _, err := sess.Insert(&quota); err != nil {
 			if _, err := sess.Insert(&quota); err != nil {
 				return err
 				return err

+ 2 - 2
pkg/services/sqlstore/quota_test.go

@@ -104,12 +104,12 @@ func TestQuotaCommandsAndQueries(t *testing.T) {
 			})
 			})
 		})
 		})
 		Convey("Given saved user quota for org", func() {
 		Convey("Given saved user quota for org", func() {
-			userQoutaCmd := m.UpdateUserQuotaCmd{
+			userQuotaCmd := m.UpdateUserQuotaCmd{
 				UserId: userId,
 				UserId: userId,
 				Target: "org_user",
 				Target: "org_user",
 				Limit:  10,
 				Limit:  10,
 			}
 			}
-			err := UpdateUserQuota(&userQoutaCmd)
+			err := UpdateUserQuota(&userQuotaCmd)
 			So(err, ShouldBeNil)
 			So(err, ShouldBeNil)
 
 
 			Convey("Should be able to get saved quota by user id and target", func() {
 			Convey("Should be able to get saved quota by user id and target", func() {

+ 20 - 12
pkg/services/sqlstore/sqlstore.go

@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+	"time"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
@@ -35,6 +36,7 @@ type DatabaseConfig struct {
 	ServerCertName                             string
 	ServerCertName                             string
 	MaxOpenConn                                int
 	MaxOpenConn                                int
 	MaxIdleConn                                int
 	MaxIdleConn                                int
+	ConnMaxLifetime                            int
 }
 }
 
 
 var (
 var (
@@ -159,18 +161,20 @@ func getEngine() (*xorm.Engine, error) {
 	engine, err := xorm.NewEngine(DbCfg.Type, cnnstr)
 	engine, err := xorm.NewEngine(DbCfg.Type, cnnstr)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
+	}
+
+	engine.SetMaxOpenConns(DbCfg.MaxOpenConn)
+	engine.SetMaxIdleConns(DbCfg.MaxIdleConn)
+	engine.SetConnMaxLifetime(time.Second * time.Duration(DbCfg.ConnMaxLifetime))
+	debugSql := setting.Cfg.Section("database").Key("log_queries").MustBool(false)
+	if !debugSql {
+		engine.SetLogger(&xorm.DiscardLogger{})
 	} else {
 	} else {
-		engine.SetMaxOpenConns(DbCfg.MaxOpenConn)
-		engine.SetMaxIdleConns(DbCfg.MaxIdleConn)
-		debugSql := setting.Cfg.Section("database").Key("log_queries").MustBool(false)
-		if !debugSql {
-			engine.SetLogger(&xorm.DiscardLogger{})
-		} else {
-			engine.SetLogger(NewXormLogger(log.LvlInfo, log.New("sqlstore.xorm")))
-			engine.ShowSQL(true)
-			engine.ShowExecTime(true)
-		}
+		engine.SetLogger(NewXormLogger(log.LvlInfo, log.New("sqlstore.xorm")))
+		engine.ShowSQL(true)
+		engine.ShowExecTime(true)
 	}
 	}
+
 	return engine, nil
 	return engine, nil
 }
 }
 
 
@@ -204,6 +208,7 @@ func LoadConfig() {
 	}
 	}
 	DbCfg.MaxOpenConn = sec.Key("max_open_conn").MustInt(0)
 	DbCfg.MaxOpenConn = sec.Key("max_open_conn").MustInt(0)
 	DbCfg.MaxIdleConn = sec.Key("max_idle_conn").MustInt(0)
 	DbCfg.MaxIdleConn = sec.Key("max_idle_conn").MustInt(0)
+	DbCfg.ConnMaxLifetime = sec.Key("conn_max_lifetime").MustInt(14400)
 
 
 	if DbCfg.Type == "sqlite3" {
 	if DbCfg.Type == "sqlite3" {
 		UseSQLite3 = true
 		UseSQLite3 = true
@@ -227,8 +232,8 @@ var (
 
 
 func InitTestDB(t *testing.T) *xorm.Engine {
 func InitTestDB(t *testing.T) *xorm.Engine {
 	selectedDb := dbSqlite
 	selectedDb := dbSqlite
-	//selectedDb := dbMySql
-	//selectedDb := dbPostgres
+	// selectedDb := dbMySql
+	// selectedDb := dbPostgres
 
 
 	var x *xorm.Engine
 	var x *xorm.Engine
 	var err error
 	var err error
@@ -247,6 +252,9 @@ func InitTestDB(t *testing.T) *xorm.Engine {
 		x, err = xorm.NewEngine(sqlutil.TestDB_Sqlite3.DriverName, sqlutil.TestDB_Sqlite3.ConnStr)
 		x, err = xorm.NewEngine(sqlutil.TestDB_Sqlite3.DriverName, sqlutil.TestDB_Sqlite3.ConnStr)
 	}
 	}
 
 
+	x.DatabaseTZ = time.UTC
+	x.TZLocation = time.UTC
+
 	// x.ShowSQL()
 	// x.ShowSQL()
 
 
 	if err != nil {
 	if err != nil {

+ 4 - 1
pkg/setting/setting.go

@@ -131,7 +131,8 @@ var (
 	PluginAppsSkipVerifyTLS bool
 	PluginAppsSkipVerifyTLS bool
 
 
 	// Session settings.
 	// Session settings.
-	SessionOptions session.Options
+	SessionOptions         session.Options
+	SessionConnMaxLifetime int64
 
 
 	// Global setting objects.
 	// Global setting objects.
 	Cfg          *ini.File
 	Cfg          *ini.File
@@ -634,6 +635,8 @@ func readSessionConfig() {
 	if SessionOptions.CookiePath == "" {
 	if SessionOptions.CookiePath == "" {
 		SessionOptions.CookiePath = "/"
 		SessionOptions.CookiePath = "/"
 	}
 	}
+
+	SessionConnMaxLifetime = Cfg.Section("session").Key("conn_max_lifetime").MustInt64(14400)
 }
 }
 
 
 func initLogging() {
 func initLogging() {

+ 5 - 1
public/app/core/components/code_editor/code_editor.ts

@@ -21,6 +21,8 @@
  * data-tab-size           - Tab size, default is 2.
  * data-tab-size           - Tab size, default is 2.
  * data-behaviours-enabled - Specifies whether to use behaviors or not. "Behaviors" in this case is the auto-pairing of
  * data-behaviours-enabled - Specifies whether to use behaviors or not. "Behaviors" in this case is the auto-pairing of
  *                           special characters, like quotation marks, parenthesis, or brackets.
  *                           special characters, like quotation marks, parenthesis, or brackets.
+ * data-snippets-enabled   - Specifies whether to use snippets or not. "Snippets" are small pieces of code that can be
+ *                           inserted via the completion box.
  *
  *
  * Keybindings:
  * Keybindings:
  * Ctrl-Enter (Command-Enter): run onChange() function
  * Ctrl-Enter (Command-Enter): run onChange() function
@@ -49,6 +51,7 @@ const DEFAULT_MODE = 'text';
 const DEFAULT_MAX_LINES = 10;
 const DEFAULT_MAX_LINES = 10;
 const DEFAULT_TAB_SIZE = 2;
 const DEFAULT_TAB_SIZE = 2;
 const DEFAULT_BEHAVIOURS = true;
 const DEFAULT_BEHAVIOURS = true;
+const DEFAULT_SNIPPETS = true;
 
 
 let editorTemplate = `<div></div>`;
 let editorTemplate = `<div></div>`;
 
 
@@ -59,6 +62,7 @@ function link(scope, elem, attrs) {
   let showGutter = attrs.showGutter !== undefined;
   let showGutter = attrs.showGutter !== undefined;
   let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;
   let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;
   let behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIOURS;
   let behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIOURS;
+  let snippetsEnabled = attrs.snippetsEnabled ? attrs.snippetsEnabled === 'true' : DEFAULT_SNIPPETS;
 
 
   // Initialize editor
   // Initialize editor
   let aceElem = elem.get(0);
   let aceElem = elem.get(0);
@@ -143,7 +147,7 @@ function link(scope, elem, attrs) {
     codeEditor.setOptions({
     codeEditor.setOptions({
       enableBasicAutocompletion: true,
       enableBasicAutocompletion: true,
       enableLiveAutocompletion: true,
       enableLiveAutocompletion: true,
-      enableSnippets: true,
+      enableSnippets: snippetsEnabled,
     });
     });
 
 
     if (scope.getCompleter()) {
     if (scope.getCompleter()) {

+ 8 - 0
public/app/core/services/keybindingSrv.ts

@@ -178,6 +178,14 @@ export class KeybindingSrv {
       }
       }
     });
     });
 
 
+    // duplicate panel
+    this.bind('p d', () => {
+      if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
+        let panelIndex = dashboard.getPanelInfoById(dashboard.meta.focusPanelId).index;
+        dashboard.duplicatePanel(dashboard.panels[panelIndex]);
+      }
+    });
+
     // share panel
     // share panel
     this.bind('p s', () => {
     this.bind('p s', () => {
       if (dashboard.meta.focusPanelId) {
       if (dashboard.meta.focusPanelId) {

+ 4 - 1
public/app/features/dashboard/specs/viewstate_srv_specs.ts

@@ -30,7 +30,10 @@ describe('when updating view state', function() {
   beforeEach(
   beforeEach(
     angularMocks.inject(function(dashboardViewStateSrv, $location, $rootScope) {
     angularMocks.inject(function(dashboardViewStateSrv, $location, $rootScope) {
       $rootScope.onAppEvent = function() {};
       $rootScope.onAppEvent = function() {};
-      $rootScope.dashboard = { meta: {} };
+      $rootScope.dashboard = {
+        meta: {},
+        panels: [],
+      };
       viewState = dashboardViewStateSrv.create($rootScope);
       viewState = dashboardViewStateSrv.create($rootScope);
       location = $location;
       location = $location;
     })
     })

+ 21 - 1
public/app/features/dashboard/view_state_srv.ts

@@ -1,6 +1,7 @@
 import angular from 'angular';
 import angular from 'angular';
 import _ from 'lodash';
 import _ from 'lodash';
 import config from 'app/core/config';
 import config from 'app/core/config';
+import { DashboardModel } from './dashboard_model';
 
 
 // represents the transient view state
 // represents the transient view state
 // like fullscreen panel & edit
 // like fullscreen panel & edit
@@ -8,7 +9,7 @@ export class DashboardViewState {
   state: any;
   state: any;
   panelScopes: any;
   panelScopes: any;
   $scope: any;
   $scope: any;
-  dashboard: any;
+  dashboard: DashboardModel;
   editStateChanged: any;
   editStateChanged: any;
   fullscreenPanel: any;
   fullscreenPanel: any;
   oldTimeRange: any;
   oldTimeRange: any;
@@ -89,6 +90,12 @@ export class DashboardViewState {
       }
       }
     }
     }
 
 
+    if ((this.state.fullscreen || this.dashboard.meta.soloMode) && this.state.panelId) {
+      // Trying to render panel in fullscreen when it's in the collapsed row causes an issue.
+      // So in this case expand collapsed row first.
+      this.toggleCollapsedPanelRow(this.state.panelId);
+    }
+
     // if no edit state cleanup tab parm
     // if no edit state cleanup tab parm
     if (!this.state.edit) {
     if (!this.state.edit) {
       delete this.state.tab;
       delete this.state.tab;
@@ -103,6 +110,19 @@ export class DashboardViewState {
     this.syncState();
     this.syncState();
   }
   }
 
 
+  toggleCollapsedPanelRow(panelId) {
+    for (let panel of this.dashboard.panels) {
+      if (panel.collapsed) {
+        for (let rowPanel of panel.panels) {
+          if (rowPanel.id === panelId) {
+            this.dashboard.toggleRow(panel);
+            return;
+          }
+        }
+      }
+    }
+  }
+
   syncState() {
   syncState() {
     if (this.panelScopes.length === 0) {
     if (this.panelScopes.length === 0) {
       return;
       return;

+ 5 - 2
public/app/features/panel/metrics_panel_ctrl.ts

@@ -78,8 +78,11 @@ class MetricsPanelCtrl extends PanelCtrl {
         data = data.data;
         data = data.data;
       }
       }
 
 
-      this.events.emit('data-snapshot-load', data);
-      return;
+      // Defer panel rendering till the next digest cycle.
+      // For some reason snapshot panels don't init at this time, so this helps to avoid rendering issues.
+      return this.$timeout(() => {
+        this.events.emit('data-snapshot-load', data);
+      });
     }
     }
 
 
     // // ignore if we have data stream
     // // ignore if we have data stream

+ 0 - 155
public/app/plugins/datasource/graphite/add_graphite_func.js

@@ -1,155 +0,0 @@
-define(['angular', 'lodash', 'jquery', 'rst2html', 'tether-drop'], function(angular, _, $, rst2html, Drop) {
-  'use strict';
-
-  angular.module('grafana.directives').directive('graphiteAddFunc', function($compile) {
-    var inputTemplate =
-      '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
-
-    var buttonTemplate =
-      '<a class="gf-form-label query-part dropdown-toggle"' +
-      ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' +
-      '<i class="fa fa-plus"></i></a>';
-
-    return {
-      link: function($scope, elem) {
-        var ctrl = $scope.ctrl;
-
-        var $input = $(inputTemplate);
-        var $button = $(buttonTemplate);
-
-        $input.appendTo(elem);
-        $button.appendTo(elem);
-
-        ctrl.datasource.getFuncDefs().then(function(funcDefs) {
-          var allFunctions = _.map(funcDefs, 'name').sort();
-
-          $scope.functionMenu = createFunctionDropDownMenu(funcDefs);
-
-          $input.attr('data-provide', 'typeahead');
-          $input.typeahead({
-            source: allFunctions,
-            minLength: 1,
-            items: 10,
-            updater: function(value) {
-              var funcDef = ctrl.datasource.getFuncDef(value);
-              if (!funcDef) {
-                // try find close match
-                value = value.toLowerCase();
-                funcDef = _.find(allFunctions, function(funcName) {
-                  return funcName.toLowerCase().indexOf(value) === 0;
-                });
-
-                if (!funcDef) {
-                  return;
-                }
-              }
-
-              $scope.$apply(function() {
-                ctrl.addFunction(funcDef);
-              });
-
-              $input.trigger('blur');
-              return '';
-            },
-          });
-
-          $button.click(function() {
-            $button.hide();
-            $input.show();
-            $input.focus();
-          });
-
-          $input.keyup(function() {
-            elem.toggleClass('open', $input.val() === '');
-          });
-
-          $input.blur(function() {
-            // clicking the function dropdown menu wont
-            // work if you remove class at once
-            setTimeout(function() {
-              $input.val('');
-              $input.hide();
-              $button.show();
-              elem.removeClass('open');
-            }, 200);
-          });
-
-          $compile(elem.contents())($scope);
-        });
-
-        var drop;
-        var cleanUpDrop = function() {
-          if (drop) {
-            drop.destroy();
-            drop = null;
-          }
-        };
-
-        $(elem)
-          .on('mouseenter', 'ul.dropdown-menu li', function() {
-            cleanUpDrop();
-
-            var funcDef;
-            try {
-              funcDef = ctrl.datasource.getFuncDef($('a', this).text());
-            } catch (e) {
-              // ignore
-            }
-
-            if (funcDef && funcDef.description) {
-              var shortDesc = funcDef.description;
-              if (shortDesc.length > 500) {
-                shortDesc = shortDesc.substring(0, 497) + '...';
-              }
-
-              var contentElement = document.createElement('div');
-              contentElement.innerHTML = '<h4>' + funcDef.name + '</h4>' + rst2html(shortDesc);
-
-              drop = new Drop({
-                target: this,
-                content: contentElement,
-                classes: 'drop-popover',
-                openOn: 'always',
-                tetherOptions: {
-                  attachment: 'bottom left',
-                  targetAttachment: 'bottom right',
-                },
-              });
-            }
-          })
-          .on('mouseout', 'ul.dropdown-menu li', function() {
-            cleanUpDrop();
-          });
-
-        $scope.$on('$destroy', cleanUpDrop);
-      },
-    };
-  });
-
-  function createFunctionDropDownMenu(funcDefs) {
-    var categories = {};
-
-    _.forEach(funcDefs, function(funcDef) {
-      if (!funcDef.category) {
-        return;
-      }
-      if (!categories[funcDef.category]) {
-        categories[funcDef.category] = [];
-      }
-      categories[funcDef.category].push({
-        text: funcDef.name,
-        click: "ctrl.addFunction('" + funcDef.name + "')",
-      });
-    });
-
-    return _.sortBy(
-      _.map(categories, function(submenu, category) {
-        return {
-          text: category,
-          submenu: _.sortBy(submenu, 'text'),
-        };
-      }),
-      'text'
-    );
-  }
-});

+ 159 - 0
public/app/plugins/datasource/graphite/add_graphite_func.ts

@@ -0,0 +1,159 @@
+import angular from 'angular';
+import _ from 'lodash';
+import $ from 'jquery';
+import rst2html from 'rst2html';
+import Drop from 'tether-drop';
+
+export function graphiteAddFunc($compile) {
+  const inputTemplate =
+    '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
+
+  const buttonTemplate =
+    '<a class="gf-form-label query-part dropdown-toggle"' +
+    ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' +
+    '<i class="fa fa-plus"></i></a>';
+
+  return {
+    link: function($scope, elem) {
+      var ctrl = $scope.ctrl;
+
+      var $input = $(inputTemplate);
+      var $button = $(buttonTemplate);
+
+      $input.appendTo(elem);
+      $button.appendTo(elem);
+
+      ctrl.datasource.getFuncDefs().then(function(funcDefs) {
+        var allFunctions = _.map(funcDefs, 'name').sort();
+
+        $scope.functionMenu = createFunctionDropDownMenu(funcDefs);
+
+        $input.attr('data-provide', 'typeahead');
+        $input.typeahead({
+          source: allFunctions,
+          minLength: 1,
+          items: 10,
+          updater: function(value) {
+            var funcDef = ctrl.datasource.getFuncDef(value);
+            if (!funcDef) {
+              // try find close match
+              value = value.toLowerCase();
+              funcDef = _.find(allFunctions, function(funcName) {
+                return funcName.toLowerCase().indexOf(value) === 0;
+              });
+
+              if (!funcDef) {
+                return '';
+              }
+            }
+
+            $scope.$apply(function() {
+              ctrl.addFunction(funcDef);
+            });
+
+            $input.trigger('blur');
+            return '';
+          },
+        });
+
+        $button.click(function() {
+          $button.hide();
+          $input.show();
+          $input.focus();
+        });
+
+        $input.keyup(function() {
+          elem.toggleClass('open', $input.val() === '');
+        });
+
+        $input.blur(function() {
+          // clicking the function dropdown menu wont
+          // work if you remove class at once
+          setTimeout(function() {
+            $input.val('');
+            $input.hide();
+            $button.show();
+            elem.removeClass('open');
+          }, 200);
+        });
+
+        $compile(elem.contents())($scope);
+      });
+
+      var drop;
+      var cleanUpDrop = function() {
+        if (drop) {
+          drop.destroy();
+          drop = null;
+        }
+      };
+
+      $(elem)
+        .on('mouseenter', 'ul.dropdown-menu li', function() {
+          cleanUpDrop();
+
+          var funcDef;
+          try {
+            funcDef = ctrl.datasource.getFuncDef($('a', this).text());
+          } catch (e) {
+            // ignore
+          }
+
+          if (funcDef && funcDef.description) {
+            var shortDesc = funcDef.description;
+            if (shortDesc.length > 500) {
+              shortDesc = shortDesc.substring(0, 497) + '...';
+            }
+
+            var contentElement = document.createElement('div');
+            contentElement.innerHTML = '<h4>' + funcDef.name + '</h4>' + rst2html(shortDesc);
+
+            drop = new Drop({
+              target: this,
+              content: contentElement,
+              classes: 'drop-popover',
+              openOn: 'always',
+              tetherOptions: {
+                attachment: 'bottom left',
+                targetAttachment: 'bottom right',
+              },
+            });
+          }
+        })
+        .on('mouseout', 'ul.dropdown-menu li', function() {
+          cleanUpDrop();
+        });
+
+      $scope.$on('$destroy', cleanUpDrop);
+    },
+  };
+}
+
+angular.module('grafana.directives').directive('graphiteAddFunc', graphiteAddFunc);
+
+function createFunctionDropDownMenu(funcDefs) {
+  var categories = {};
+
+  _.forEach(funcDefs, function(funcDef) {
+    if (!funcDef.category) {
+      return;
+    }
+    if (!categories[funcDef.category]) {
+      categories[funcDef.category] = [];
+    }
+    categories[funcDef.category].push({
+      text: funcDef.name,
+      click: "ctrl.addFunction('" + funcDef.name + "')",
+    });
+  });
+
+  return _.sortBy(
+    _.map(categories, function(submenu, category) {
+      return {
+        text: category,
+        submenu: _.sortBy(submenu, 'text'),
+      };
+    }),
+    'text'
+  );
+}

+ 0 - 309
public/app/plugins/datasource/graphite/func_editor.js

@@ -1,309 +0,0 @@
-define([
-  'angular',
-  'lodash',
-  'jquery',
-  'rst2html',
-],
-function (angular, _, $, rst2html) {
-  'use strict';
-
-  angular
-    .module('grafana.directives')
-    .directive('graphiteFuncEditor', function($compile, templateSrv, popoverSrv) {
-
-      var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
-      var paramTemplate = '<input type="text" style="display:none"' +
-                          ' class="input-small tight-form-func-param"></input>';
-
-      var funcControlsTemplate =
-         '<div class="tight-form-func-controls">' +
-           '<span class="pointer fa fa-arrow-left"></span>' +
-           '<span class="pointer fa fa-question-circle"></span>' +
-           '<span class="pointer fa fa-remove" ></span>' +
-           '<span class="pointer fa fa-arrow-right"></span>' +
-         '</div>';
-
-      return {
-        restrict: 'A',
-        link: function postLink($scope, elem) {
-          var $funcLink = $(funcSpanTemplate);
-          var $funcControls = $(funcControlsTemplate);
-          var ctrl = $scope.ctrl;
-          var func = $scope.func;
-          var scheduledRelink = false;
-          var paramCountAtLink = 0;
-          var cancelBlur = null;
-
-          function clickFuncParam(paramIndex) {
-            /*jshint validthis:true */
-
-            var $link = $(this);
-            var $comma = $link.prev('.comma');
-            var $input = $link.next();
-
-            $input.val(func.params[paramIndex]);
-
-            $comma.removeClass('query-part__last');
-            $link.hide();
-            $input.show();
-            $input.focus();
-            $input.select();
-
-            var typeahead = $input.data('typeahead');
-            if (typeahead) {
-              $input.val('');
-              typeahead.lookup();
-            }
-          }
-
-          function scheduledRelinkIfNeeded() {
-            if (paramCountAtLink === func.params.length) {
-              return;
-            }
-
-            if (!scheduledRelink) {
-              scheduledRelink = true;
-              setTimeout(function() {
-                relink();
-                scheduledRelink = false;
-              }, 200);
-            }
-          }
-
-          function paramDef(index) {
-            if (index < func.def.params.length) {
-              return func.def.params[index];
-            }
-            if (_.last(func.def.params).multiple) {
-              return _.assign({}, _.last(func.def.params), {optional: true});
-            }
-            return {};
-          }
-
-          function switchToLink(inputElem, paramIndex) {
-            /*jshint validthis:true */
-            var $input = $(inputElem);
-
-            clearTimeout(cancelBlur);
-            cancelBlur = null;
-
-            var $link = $input.prev();
-            var $comma = $link.prev('.comma');
-            var newValue = $input.val();
-
-            // remove optional empty params
-            if (newValue !== '' || paramDef(paramIndex).optional) {
-              func.updateParam(newValue, paramIndex);
-              $link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : '&nbsp;');
-            }
-
-            scheduledRelinkIfNeeded();
-
-            $scope.$apply(function() {
-              ctrl.targetChanged();
-            });
-
-            if ($link.hasClass('query-part__last') && newValue === '') {
-              $comma.addClass('query-part__last');
-            } else {
-              $link.removeClass('query-part__last');
-            }
-
-            $input.hide();
-            $link.show();
-          }
-
-          // this = input element
-          function inputBlur(paramIndex) {
-            /*jshint validthis:true */
-            var inputElem = this;
-            // happens long before the click event on the typeahead options
-            // need to have long delay because the blur
-            cancelBlur = setTimeout(function() {
-              switchToLink(inputElem, paramIndex);
-            }, 200);
-          }
-
-          function inputKeyPress(paramIndex, e) {
-            /*jshint validthis:true */
-            if(e.which === 13) {
-              $(this).blur();
-            }
-          }
-
-          function inputKeyDown() {
-            /*jshint validthis:true */
-            this.style.width = (3 + this.value.length) * 8 + 'px';
-          }
-
-          function addTypeahead($input, paramIndex) {
-            $input.attr('data-provide', 'typeahead');
-
-            var options = paramDef(paramIndex).options;
-            if (paramDef(paramIndex).type === 'int') {
-              options = _.map(options, function(val) { return val.toString(); });
-            }
-
-            $input.typeahead({
-              source: options,
-              minLength: 0,
-              items: 20,
-              updater: function (value) {
-                $input.val(value);
-                switchToLink($input[0], paramIndex);
-                return value;
-              }
-            });
-
-            var typeahead = $input.data('typeahead');
-            typeahead.lookup = function () {
-              this.query = this.$element.val() || '';
-              return this.process(this.source);
-            };
-          }
-
-          function toggleFuncControls() {
-            var targetDiv = elem.closest('.tight-form');
-
-            if (elem.hasClass('show-function-controls')) {
-              elem.removeClass('show-function-controls');
-              targetDiv.removeClass('has-open-function');
-              $funcControls.hide();
-              return;
-            }
-
-            elem.addClass('show-function-controls');
-            targetDiv.addClass('has-open-function');
-
-            $funcControls.show();
-          }
-
-          function addElementsAndCompile() {
-            $funcControls.appendTo(elem);
-            $funcLink.appendTo(elem);
-
-            var defParams = _.clone(func.def.params);
-            var lastParam = _.last(func.def.params);
-
-            while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
-              defParams.push(_.assign({}, lastParam, {optional: true}));
-            }
-
-            _.each(defParams, function(param, index) {
-              if (param.optional && func.params.length < index) {
-                return false;
-              }
-
-              var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
-
-              var last = (index >= func.params.length - 1) && param.optional && !paramValue;
-              if (last && param.multiple) {
-                paramValue = '+';
-              }
-
-              if (index > 0) {
-                $('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem);
-              }
-
-              var $paramLink = $(
-                '<a ng-click="" class="graphite-func-param-link' + (last ? ' query-part__last' : '') + '">'
-                + (paramValue || '&nbsp;') + '</a>');
-              var $input = $(paramTemplate);
-              $input.attr('placeholder', param.name);
-
-              paramCountAtLink++;
-
-              $paramLink.appendTo(elem);
-              $input.appendTo(elem);
-
-              $input.blur(_.partial(inputBlur, index));
-              $input.keyup(inputKeyDown);
-              $input.keypress(_.partial(inputKeyPress, index));
-              $paramLink.click(_.partial(clickFuncParam, index));
-
-              if (param.options) {
-                addTypeahead($input, index);
-              }
-            });
-
-            $('<span>)</span>').appendTo(elem);
-
-            $compile(elem.contents())($scope);
-          }
-
-          function ifJustAddedFocusFirstParam() {
-            if ($scope.func.added) {
-              $scope.func.added = false;
-              setTimeout(function() {
-                elem.find('.graphite-func-param-link').first().click();
-              }, 10);
-            }
-          }
-
-          function registerFuncControlsToggle() {
-            $funcLink.click(toggleFuncControls);
-          }
-
-          function registerFuncControlsActions() {
-            $funcControls.click(function(e) {
-              var $target = $(e.target);
-              if ($target.hasClass('fa-remove')) {
-                toggleFuncControls();
-                $scope.$apply(function() {
-                  ctrl.removeFunction($scope.func);
-                });
-                return;
-              }
-
-              if ($target.hasClass('fa-arrow-left')) {
-                $scope.$apply(function() {
-                  _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index - 1);
-                  ctrl.targetChanged();
-                });
-                return;
-              }
-
-              if ($target.hasClass('fa-arrow-right')) {
-                $scope.$apply(function() {
-                  _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index + 1);
-                  ctrl.targetChanged();
-                });
-                return;
-              }
-
-              if ($target.hasClass('fa-question-circle')) {
-                var funcDef = ctrl.datasource.getFuncDef(func.def.name);
-                if (funcDef && funcDef.description) {
-                  popoverSrv.show({
-                    element: e.target,
-                    position: 'bottom left',
-                    classNames: 'drop-popover drop-function-def',
-                    template: '<div style="overflow:auto;max-height:30rem;">'
-                      + '<h4>' + funcDef.name + '</h4>' + rst2html(funcDef.description) + '</div>',
-                    openOn: 'click',
-                  });
-                } else {
-                  window.open(
-                    "http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + func.def.name,'_blank');
-                }
-                return;
-              }
-            });
-          }
-
-          function relink() {
-            elem.children().remove();
-
-            addElementsAndCompile();
-            ifJustAddedFocusFirstParam();
-            registerFuncControlsToggle();
-            registerFuncControlsActions();
-          }
-
-          relink();
-        }
-      };
-
-    });
-
-});

+ 317 - 0
public/app/plugins/datasource/graphite/func_editor.ts

@@ -0,0 +1,317 @@
+import angular from 'angular';
+import _ from 'lodash';
+import $ from 'jquery';
+import rst2html from 'rst2html';
+
+export function graphiteFuncEditor($compile, templateSrv, popoverSrv) {
+  const funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
+  const paramTemplate =
+    '<input type="text" style="display:none"' + ' class="input-small tight-form-func-param"></input>';
+
+  const funcControlsTemplate = `
+    <div class="tight-form-func-controls">
+      <span class="pointer fa fa-arrow-left"></span>
+      <span class="pointer fa fa-question-circle"></span>
+      <span class="pointer fa fa-remove" ></span>
+      <span class="pointer fa fa-arrow-right"></span>
+    </div>`;
+
+  return {
+    restrict: 'A',
+    link: function postLink($scope, elem) {
+      var $funcLink = $(funcSpanTemplate);
+      var $funcControls = $(funcControlsTemplate);
+      var ctrl = $scope.ctrl;
+      var func = $scope.func;
+      var scheduledRelink = false;
+      var paramCountAtLink = 0;
+      var cancelBlur = null;
+
+      function clickFuncParam(paramIndex) {
+        /*jshint validthis:true */
+
+        var $link = $(this);
+        var $comma = $link.prev('.comma');
+        var $input = $link.next();
+
+        $input.val(func.params[paramIndex]);
+
+        $comma.removeClass('query-part__last');
+        $link.hide();
+        $input.show();
+        $input.focus();
+        $input.select();
+
+        var typeahead = $input.data('typeahead');
+        if (typeahead) {
+          $input.val('');
+          typeahead.lookup();
+        }
+      }
+
+      function scheduledRelinkIfNeeded() {
+        if (paramCountAtLink === func.params.length) {
+          return;
+        }
+
+        if (!scheduledRelink) {
+          scheduledRelink = true;
+          setTimeout(function() {
+            relink();
+            scheduledRelink = false;
+          }, 200);
+        }
+      }
+
+      function paramDef(index) {
+        if (index < func.def.params.length) {
+          return func.def.params[index];
+        }
+        if (_.last(func.def.params).multiple) {
+          return _.assign({}, _.last(func.def.params), { optional: true });
+        }
+        return {};
+      }
+
+      function switchToLink(inputElem, paramIndex) {
+        /*jshint validthis:true */
+        var $input = $(inputElem);
+
+        clearTimeout(cancelBlur);
+        cancelBlur = null;
+
+        var $link = $input.prev();
+        var $comma = $link.prev('.comma');
+        var newValue = $input.val();
+
+        // remove optional empty params
+        if (newValue !== '' || paramDef(paramIndex).optional) {
+          func.updateParam(newValue, paramIndex);
+          $link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : '&nbsp;');
+        }
+
+        scheduledRelinkIfNeeded();
+
+        $scope.$apply(function() {
+          ctrl.targetChanged();
+        });
+
+        if ($link.hasClass('query-part__last') && newValue === '') {
+          $comma.addClass('query-part__last');
+        } else {
+          $link.removeClass('query-part__last');
+        }
+
+        $input.hide();
+        $link.show();
+      }
+
+      // this = input element
+      function inputBlur(paramIndex) {
+        /*jshint validthis:true */
+        var inputElem = this;
+        // happens long before the click event on the typeahead options
+        // need to have long delay because the blur
+        cancelBlur = setTimeout(function() {
+          switchToLink(inputElem, paramIndex);
+        }, 200);
+      }
+
+      function inputKeyPress(paramIndex, e) {
+        /*jshint validthis:true */
+        if (e.which === 13) {
+          $(this).blur();
+        }
+      }
+
+      function inputKeyDown() {
+        /*jshint validthis:true */
+        this.style.width = (3 + this.value.length) * 8 + 'px';
+      }
+
+      function addTypeahead($input, paramIndex) {
+        $input.attr('data-provide', 'typeahead');
+
+        var options = paramDef(paramIndex).options;
+        if (paramDef(paramIndex).type === 'int') {
+          options = _.map(options, function(val) {
+            return val.toString();
+          });
+        }
+
+        $input.typeahead({
+          source: options,
+          minLength: 0,
+          items: 20,
+          updater: function(value) {
+            $input.val(value);
+            switchToLink($input[0], paramIndex);
+            return value;
+          },
+        });
+
+        var typeahead = $input.data('typeahead');
+        typeahead.lookup = function() {
+          this.query = this.$element.val() || '';
+          return this.process(this.source);
+        };
+      }
+
+      function toggleFuncControls() {
+        var targetDiv = elem.closest('.tight-form');
+
+        if (elem.hasClass('show-function-controls')) {
+          elem.removeClass('show-function-controls');
+          targetDiv.removeClass('has-open-function');
+          $funcControls.hide();
+          return;
+        }
+
+        elem.addClass('show-function-controls');
+        targetDiv.addClass('has-open-function');
+
+        $funcControls.show();
+      }
+
+      function addElementsAndCompile() {
+        $funcControls.appendTo(elem);
+        $funcLink.appendTo(elem);
+
+        var defParams = _.clone(func.def.params);
+        var lastParam = _.last(func.def.params);
+
+        while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
+          defParams.push(_.assign({}, lastParam, { optional: true }));
+        }
+
+        _.each(defParams, function(param, index) {
+          if (param.optional && func.params.length < index) {
+            return false;
+          }
+
+          var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
+
+          var last = index >= func.params.length - 1 && param.optional && !paramValue;
+          if (last && param.multiple) {
+            paramValue = '+';
+          }
+
+          if (index > 0) {
+            $('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem);
+          }
+
+          var $paramLink = $(
+            '<a ng-click="" class="graphite-func-param-link' +
+              (last ? ' query-part__last' : '') +
+              '">' +
+              (paramValue || '&nbsp;') +
+              '</a>'
+          );
+          var $input = $(paramTemplate);
+          $input.attr('placeholder', param.name);
+
+          paramCountAtLink++;
+
+          $paramLink.appendTo(elem);
+          $input.appendTo(elem);
+
+          $input.blur(_.partial(inputBlur, index));
+          $input.keyup(inputKeyDown);
+          $input.keypress(_.partial(inputKeyPress, index));
+          $paramLink.click(_.partial(clickFuncParam, index));
+
+          if (param.options) {
+            addTypeahead($input, index);
+          }
+
+          return true;
+        });
+
+        $('<span>)</span>').appendTo(elem);
+
+        $compile(elem.contents())($scope);
+      }
+
+      function ifJustAddedFocusFirstParam() {
+        if ($scope.func.added) {
+          $scope.func.added = false;
+          setTimeout(function() {
+            elem
+              .find('.graphite-func-param-link')
+              .first()
+              .click();
+          }, 10);
+        }
+      }
+
+      function registerFuncControlsToggle() {
+        $funcLink.click(toggleFuncControls);
+      }
+
+      function registerFuncControlsActions() {
+        $funcControls.click(function(e) {
+          var $target = $(e.target);
+          if ($target.hasClass('fa-remove')) {
+            toggleFuncControls();
+            $scope.$apply(function() {
+              ctrl.removeFunction($scope.func);
+            });
+            return;
+          }
+
+          if ($target.hasClass('fa-arrow-left')) {
+            $scope.$apply(function() {
+              _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index - 1);
+              ctrl.targetChanged();
+            });
+            return;
+          }
+
+          if ($target.hasClass('fa-arrow-right')) {
+            $scope.$apply(function() {
+              _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index + 1);
+              ctrl.targetChanged();
+            });
+            return;
+          }
+
+          if ($target.hasClass('fa-question-circle')) {
+            var funcDef = ctrl.datasource.getFuncDef(func.def.name);
+            if (funcDef && funcDef.description) {
+              popoverSrv.show({
+                element: e.target,
+                position: 'bottom left',
+                classNames: 'drop-popover drop-function-def',
+                template: `
+                  <div style="overflow:auto;max-height:30rem;">
+                    <h4> ${funcDef.name} </h4>
+                    ${rst2html(funcDef.description)}
+                  </div>`,
+                openOn: 'click',
+              });
+            } else {
+              window.open(
+                'http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions.' + func.def.name,
+                '_blank'
+              );
+            }
+            return;
+          }
+        });
+      }
+
+      function relink() {
+        elem.children().remove();
+
+        addElementsAndCompile();
+        ifJustAddedFocusFirstParam();
+        registerFuncControlsToggle();
+        registerFuncControlsActions();
+      }
+
+      relink();
+    },
+  };
+}
+
+angular.module('grafana.directives').directive('graphiteFuncEditor', graphiteFuncEditor);

+ 64 - 67
public/app/plugins/datasource/mssql/specs/datasource_specs.ts

@@ -1,24 +1,26 @@
-import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
+import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common';
 import moment from 'moment';
 import moment from 'moment';
 import helpers from 'test/specs/helpers';
 import helpers from 'test/specs/helpers';
-import {MssqlDatasource} from '../datasource';
-import {CustomVariable} from 'app/features/templating/custom_variable';
+import { MssqlDatasource } from '../datasource';
+import { CustomVariable } from 'app/features/templating/custom_variable';
 
 
 describe('MSSQLDatasource', function() {
 describe('MSSQLDatasource', function() {
   var ctx = new helpers.ServiceTestContext();
   var ctx = new helpers.ServiceTestContext();
-  var instanceSettings = {name: 'mssql'};
+  var instanceSettings = { name: 'mssql' };
 
 
   beforeEach(angularMocks.module('grafana.core'));
   beforeEach(angularMocks.module('grafana.core'));
   beforeEach(angularMocks.module('grafana.services'));
   beforeEach(angularMocks.module('grafana.services'));
   beforeEach(ctx.providePhase(['backendSrv']));
   beforeEach(ctx.providePhase(['backendSrv']));
 
 
-  beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
-    ctx.$q = $q;
-    ctx.$httpBackend =  $httpBackend;
-    ctx.$rootScope = $rootScope;
-    ctx.ds = $injector.instantiate(MssqlDatasource, {instanceSettings: instanceSettings});
-    $httpBackend.when('GET', /\.html$/).respond('');
-  }));
+  beforeEach(
+    angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
+      ctx.$q = $q;
+      ctx.$httpBackend = $httpBackend;
+      ctx.$rootScope = $rootScope;
+      ctx.ds = $injector.instantiate(MssqlDatasource, { instanceSettings: instanceSettings });
+      $httpBackend.when('GET', /\.html$/).respond('');
+    })
+  );
 
 
   describe('When performing annotationQuery', function() {
   describe('When performing annotationQuery', function() {
     let results;
     let results;
@@ -28,12 +30,12 @@ describe('MSSQLDatasource', function() {
     const options = {
     const options = {
       annotation: {
       annotation: {
         name: annotationName,
         name: annotationName,
-        rawQuery: 'select time, text, tags from table;'
+        rawQuery: 'select time, text, tags from table;',
       },
       },
       range: {
       range: {
         from: moment(1432288354),
         from: moment(1432288354),
-        to: moment(1432288401)
-      }
+        to: moment(1432288401),
+      },
     };
     };
 
 
     const response = {
     const response = {
@@ -42,23 +44,25 @@ describe('MSSQLDatasource', function() {
           refId: annotationName,
           refId: annotationName,
           tables: [
           tables: [
             {
             {
-              columns: [{text: 'time'}, {text: 'text'}, {text: 'tags'}],
+              columns: [{ text: 'time' }, { text: 'text' }, { text: 'tags' }],
               rows: [
               rows: [
                 [1432288355, 'some text', 'TagA,TagB'],
                 [1432288355, 'some text', 'TagA,TagB'],
                 [1432288390, 'some text2', ' TagB , TagC'],
                 [1432288390, 'some text2', ' TagB , TagC'],
-                [1432288400, 'some text3']
-              ]
-            }
-          ]
-        }
-      }
+                [1432288400, 'some text3'],
+              ],
+            },
+          ],
+        },
+      },
     };
     };
 
 
     beforeEach(function() {
     beforeEach(function() {
       ctx.backendSrv.datasourceRequest = function(options) {
       ctx.backendSrv.datasourceRequest = function(options) {
-        return ctx.$q.when({data: response, status: 200});
+        return ctx.$q.when({ data: response, status: 200 });
       };
       };
-      ctx.ds.annotationQuery(options).then(function(data) { results = data; });
+      ctx.ds.annotationQuery(options).then(function(data) {
+        results = data;
+      });
       ctx.$rootScope.$apply();
       ctx.$rootScope.$apply();
     });
     });
 
 
@@ -83,28 +87,26 @@ describe('MSSQLDatasource', function() {
       results: {
       results: {
         tempvar: {
         tempvar: {
           meta: {
           meta: {
-            rowCount: 3
+            rowCount: 3,
           },
           },
           refId: 'tempvar',
           refId: 'tempvar',
           tables: [
           tables: [
             {
             {
-              columns: [{text: 'title'}, {text: 'text'}],
-              rows: [
-                ['aTitle', 'some text'],
-                ['aTitle2', 'some text2'],
-                ['aTitle3', 'some text3']
-              ]
-            }
-          ]
-        }
-      }
+              columns: [{ text: 'title' }, { text: 'text' }],
+              rows: [['aTitle', 'some text'], ['aTitle2', 'some text2'], ['aTitle3', 'some text3']],
+            },
+          ],
+        },
+      },
     };
     };
 
 
     beforeEach(function() {
     beforeEach(function() {
       ctx.backendSrv.datasourceRequest = function(options) {
       ctx.backendSrv.datasourceRequest = function(options) {
-        return ctx.$q.when({data: response, status: 200});
+        return ctx.$q.when({ data: response, status: 200 });
       };
       };
-      ctx.ds.metricFindQuery(query).then(function(data) { results = data; });
+      ctx.ds.metricFindQuery(query).then(function(data) {
+        results = data;
+      });
       ctx.$rootScope.$apply();
       ctx.$rootScope.$apply();
     });
     });
 
 
@@ -122,28 +124,26 @@ describe('MSSQLDatasource', function() {
       results: {
       results: {
         tempvar: {
         tempvar: {
           meta: {
           meta: {
-            rowCount: 3
+            rowCount: 3,
           },
           },
           refId: 'tempvar',
           refId: 'tempvar',
           tables: [
           tables: [
             {
             {
-              columns: [{text: '__value'}, {text: '__text'}],
-              rows: [
-                ['value1', 'aTitle'],
-                ['value2', 'aTitle2'],
-                ['value3', 'aTitle3']
-              ]
-            }
-          ]
-        }
-      }
+              columns: [{ text: '__value' }, { text: '__text' }],
+              rows: [['value1', 'aTitle'], ['value2', 'aTitle2'], ['value3', 'aTitle3']],
+            },
+          ],
+        },
+      },
     };
     };
 
 
     beforeEach(function() {
     beforeEach(function() {
       ctx.backendSrv.datasourceRequest = function(options) {
       ctx.backendSrv.datasourceRequest = function(options) {
-        return ctx.$q.when({data: response, status: 200});
+        return ctx.$q.when({ data: response, status: 200 });
       };
       };
-      ctx.ds.metricFindQuery(query).then(function(data) { results = data; });
+      ctx.ds.metricFindQuery(query).then(function(data) {
+        results = data;
+      });
       ctx.$rootScope.$apply();
       ctx.$rootScope.$apply();
     });
     });
 
 
@@ -163,28 +163,26 @@ describe('MSSQLDatasource', function() {
       results: {
       results: {
         tempvar: {
         tempvar: {
           meta: {
           meta: {
-            rowCount: 3
+            rowCount: 3,
           },
           },
           refId: 'tempvar',
           refId: 'tempvar',
           tables: [
           tables: [
             {
             {
-              columns: [{text: '__text'}, {text: '__value'}],
-              rows: [
-                ['aTitle', 'same'],
-                ['aTitle', 'same'],
-                ['aTitle', 'diff']
-              ]
-            }
-          ]
-        }
-      }
+              columns: [{ text: '__text' }, { text: '__value' }],
+              rows: [['aTitle', 'same'], ['aTitle', 'same'], ['aTitle', 'diff']],
+            },
+          ],
+        },
+      },
     };
     };
 
 
     beforeEach(function() {
     beforeEach(function() {
       ctx.backendSrv.datasourceRequest = function(options) {
       ctx.backendSrv.datasourceRequest = function(options) {
-        return ctx.$q.when({data: response, status: 200});
+        return ctx.$q.when({ data: response, status: 200 });
       };
       };
-      ctx.ds.metricFindQuery(query).then(function(data) { results = data; });
+      ctx.ds.metricFindQuery(query).then(function(data) {
+        results = data;
+      });
       ctx.$rootScope.$apply();
       ctx.$rootScope.$apply();
     });
     });
 
 
@@ -197,7 +195,7 @@ describe('MSSQLDatasource', function() {
 
 
   describe('When interpolating variables', () => {
   describe('When interpolating variables', () => {
     beforeEach(function() {
     beforeEach(function() {
-      ctx.variable = new CustomVariable({},{});
+      ctx.variable = new CustomVariable({}, {});
     });
     });
 
 
     describe('and value is a string', () => {
     describe('and value is a string', () => {
@@ -214,23 +212,22 @@ describe('MSSQLDatasource', function() {
 
 
     describe('and value is an array of strings', () => {
     describe('and value is an array of strings', () => {
       it('should return comma separated quoted values', () => {
       it('should return comma separated quoted values', () => {
-        expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).to.eql('\'a\',\'b\',\'c\'');
+        expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).to.eql("'a','b','c'");
       });
       });
     });
     });
 
 
     describe('and variable allows multi-value and value is a string', () => {
     describe('and variable allows multi-value and value is a string', () => {
       it('should return a quoted value', () => {
       it('should return a quoted value', () => {
         ctx.variable.multi = true;
         ctx.variable.multi = true;
-        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql('\'abc\'');
+        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql("'abc'");
       });
       });
     });
     });
 
 
     describe('and variable allows all and value is a string', () => {
     describe('and variable allows all and value is a string', () => {
       it('should return a quoted value', () => {
       it('should return a quoted value', () => {
         ctx.variable.includeAll = true;
         ctx.variable.includeAll = true;
-        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql('\'abc\'');
+        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql("'abc'");
       });
       });
     });
     });
-
   });
   });
 });
 });

+ 3 - 6
public/app/plugins/datasource/prometheus/specs/completer_specs.ts

@@ -47,12 +47,9 @@ describe('Prometheus editor completer', function() {
     variables: [
     variables: [
       {
       {
         name: 'var_name',
         name: 'var_name',
-        options: [
-          { text: 'foo', value: 'foo', selected: false },
-          { text: 'bar', value: 'bar', selected: true }
-        ]
-      }
-    ]
+        options: [{ text: 'foo', value: 'foo', selected: false }, { text: 'bar', value: 'bar', selected: true }],
+      },
+    ],
   };
   };
   let completer = new PromCompleter(datasourceStub, templateSrv);
   let completer = new PromCompleter(datasourceStub, templateSrv);
 
 

+ 6 - 2
public/app/stores/ViewStore/ViewStore.ts

@@ -26,7 +26,9 @@ export const ViewStore = types
     function updateQuery(query: any) {
     function updateQuery(query: any) {
       self.query.clear();
       self.query.clear();
       for (let key of Object.keys(query)) {
       for (let key of Object.keys(query)) {
-        self.query.set(key, query[key]);
+        if (query[key]) {
+          self.query.set(key, query[key]);
+        }
       }
       }
     }
     }
 
 
@@ -34,7 +36,9 @@ export const ViewStore = types
     function updateRouteParams(routeParams: any) {
     function updateRouteParams(routeParams: any) {
       self.routeParams.clear();
       self.routeParams.clear();
       for (let key of Object.keys(routeParams)) {
       for (let key of Object.keys(routeParams)) {
-        self.routeParams.set(key, routeParams[key]);
+        if (routeParams[key]) {
+          self.routeParams.set(key, routeParams[key]);
+        }
       }
       }
     }
     }
 
 

BIN
public/img/kibana.png


BIN
public/img/small.png


+ 4 - 1
vendor/github.com/go-xorm/core/column.go

@@ -13,12 +13,13 @@ const (
 	ONLYFROMDB
 	ONLYFROMDB
 )
 )
 
 
-// database column
+// Column defines database column
 type Column struct {
 type Column struct {
 	Name            string
 	Name            string
 	TableName       string
 	TableName       string
 	FieldName       string
 	FieldName       string
 	SQLType         SQLType
 	SQLType         SQLType
+	IsJSON          bool
 	Length          int
 	Length          int
 	Length2         int
 	Length2         int
 	Nullable        bool
 	Nullable        bool
@@ -37,6 +38,7 @@ type Column struct {
 	SetOptions      map[string]int
 	SetOptions      map[string]int
 	DisableTimeZone bool
 	DisableTimeZone bool
 	TimeZone        *time.Location // column specified time zone
 	TimeZone        *time.Location // column specified time zone
+	Comment         string
 }
 }
 
 
 func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
 func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
@@ -60,6 +62,7 @@ func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable
 		IsVersion:       false,
 		IsVersion:       false,
 		DefaultIsEmpty:  false,
 		DefaultIsEmpty:  false,
 		EnumOptions:     make(map[string]int),
 		EnumOptions:     make(map[string]int),
+		Comment:         "",
 	}
 	}
 }
 }
 
 

+ 3 - 0
vendor/github.com/go-xorm/core/dialect.go

@@ -244,6 +244,9 @@ func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset stri
 				sql += col.StringNoPk(b.dialect)
 				sql += col.StringNoPk(b.dialect)
 			}
 			}
 			sql = strings.TrimSpace(sql)
 			sql = strings.TrimSpace(sql)
+			if b.DriverName() == MYSQL && len(col.Comment) > 0 {
+				sql += " COMMENT '" + col.Comment + "'"
+			}
 			sql += ", "
 			sql += ", "
 		}
 		}
 
 

+ 12 - 0
vendor/github.com/go-xorm/core/rows.go

@@ -247,6 +247,18 @@ type Row struct {
 	err error // deferred error for easy chaining
 	err error // deferred error for easy chaining
 }
 }
 
 
+// ErrorRow return an error row
+func ErrorRow(err error) *Row {
+	return &Row{
+		err: err,
+	}
+}
+
+// NewRow from rows
+func NewRow(rows *Rows, err error) *Row {
+	return &Row{rows, err}
+}
+
 func (row *Row) Columns() ([]string, error) {
 func (row *Row) Columns() ([]string, error) {
 	if row.err != nil {
 	if row.err != nil {
 		return nil, row.err
 		return nil, row.err

+ 1 - 0
vendor/github.com/go-xorm/core/table.go

@@ -22,6 +22,7 @@ type Table struct {
 	Cacher        Cacher
 	Cacher        Cacher
 	StoreEngine   string
 	StoreEngine   string
 	Charset       string
 	Charset       string
+	Comment       string
 }
 }
 
 
 func (table *Table) Columns() []*Column {
 func (table *Table) Columns() []*Column {

+ 3 - 2
vendor/github.com/go-xorm/core/type.go

@@ -100,7 +100,8 @@ var (
 	LongBlob   = "LONGBLOB"
 	LongBlob   = "LONGBLOB"
 	Bytea      = "BYTEA"
 	Bytea      = "BYTEA"
 
 
-	Bool = "BOOL"
+	Bool    = "BOOL"
+	Boolean = "BOOLEAN"
 
 
 	Serial    = "SERIAL"
 	Serial    = "SERIAL"
 	BigSerial = "BIGSERIAL"
 	BigSerial = "BIGSERIAL"
@@ -163,7 +164,7 @@ var (
 	uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"}
 	uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"}
 )
 )
 
 
-// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparision
+// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparison
 var (
 var (
 	c_EMPTY_STRING       string
 	c_EMPTY_STRING       string
 	c_BOOL_DEFAULT       bool
 	c_BOOL_DEFAULT       bool

+ 12 - 22
vendor/github.com/go-xorm/xorm/lru_cacher.go → vendor/github.com/go-xorm/xorm/cache_lru.go

@@ -15,13 +15,12 @@ import (
 
 
 // LRUCacher implments cache object facilities
 // LRUCacher implments cache object facilities
 type LRUCacher struct {
 type LRUCacher struct {
-	idList   *list.List
-	sqlList  *list.List
-	idIndex  map[string]map[string]*list.Element
-	sqlIndex map[string]map[string]*list.Element
-	store    core.CacheStore
-	mutex    sync.Mutex
-	// maxSize    int
+	idList         *list.List
+	sqlList        *list.List
+	idIndex        map[string]map[string]*list.Element
+	sqlIndex       map[string]map[string]*list.Element
+	store          core.CacheStore
+	mutex          sync.Mutex
 	MaxElementSize int
 	MaxElementSize int
 	Expired        time.Duration
 	Expired        time.Duration
 	GcInterval     time.Duration
 	GcInterval     time.Duration
@@ -54,8 +53,6 @@ func (m *LRUCacher) RunGC() {
 
 
 // GC check ids lit and sql list to remove all element expired
 // GC check ids lit and sql list to remove all element expired
 func (m *LRUCacher) GC() {
 func (m *LRUCacher) GC() {
-	//fmt.Println("begin gc ...")
-	//defer fmt.Println("end gc ...")
 	m.mutex.Lock()
 	m.mutex.Lock()
 	defer m.mutex.Unlock()
 	defer m.mutex.Unlock()
 	var removedNum int
 	var removedNum int
@@ -64,12 +61,10 @@ func (m *LRUCacher) GC() {
 			time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
 			time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
 			removedNum++
 			removedNum++
 			next := e.Next()
 			next := e.Next()
-			//fmt.Println("removing ...", e.Value)
 			node := e.Value.(*idNode)
 			node := e.Value.(*idNode)
 			m.delBean(node.tbName, node.id)
 			m.delBean(node.tbName, node.id)
 			e = next
 			e = next
 		} else {
 		} else {
-			//fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.idList.Len())
 			break
 			break
 		}
 		}
 	}
 	}
@@ -80,12 +75,10 @@ func (m *LRUCacher) GC() {
 			time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
 			time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
 			removedNum++
 			removedNum++
 			next := e.Next()
 			next := e.Next()
-			//fmt.Println("removing ...", e.Value)
 			node := e.Value.(*sqlNode)
 			node := e.Value.(*sqlNode)
 			m.delIds(node.tbName, node.sql)
 			m.delIds(node.tbName, node.sql)
 			e = next
 			e = next
 		} else {
 		} else {
-			//fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.sqlList.Len())
 			break
 			break
 		}
 		}
 	}
 	}
@@ -116,7 +109,6 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
 	}
 	}
 
 
 	m.delIds(tableName, sql)
 	m.delIds(tableName, sql)
-
 	return nil
 	return nil
 }
 }
 
 
@@ -134,7 +126,6 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
 			// if expired, remove the node and return nil
 			// if expired, remove the node and return nil
 			if time.Now().Sub(lastTime) > m.Expired {
 			if time.Now().Sub(lastTime) > m.Expired {
 				m.delBean(tableName, id)
 				m.delBean(tableName, id)
-				//m.clearIds(tableName)
 				return nil
 				return nil
 			}
 			}
 			m.idList.MoveToBack(el)
 			m.idList.MoveToBack(el)
@@ -148,7 +139,6 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
 
 
 	// store bean is not exist, then remove memory's index
 	// store bean is not exist, then remove memory's index
 	m.delBean(tableName, id)
 	m.delBean(tableName, id)
-	//m.clearIds(tableName)
 	return nil
 	return nil
 }
 }
 
 
@@ -166,8 +156,8 @@ func (m *LRUCacher) clearIds(tableName string) {
 // ClearIds clears all sql-ids mapping on table tableName from cache
 // ClearIds clears all sql-ids mapping on table tableName from cache
 func (m *LRUCacher) ClearIds(tableName string) {
 func (m *LRUCacher) ClearIds(tableName string) {
 	m.mutex.Lock()
 	m.mutex.Lock()
-	defer m.mutex.Unlock()
 	m.clearIds(tableName)
 	m.clearIds(tableName)
+	m.mutex.Unlock()
 }
 }
 
 
 func (m *LRUCacher) clearBeans(tableName string) {
 func (m *LRUCacher) clearBeans(tableName string) {
@@ -184,14 +174,13 @@ func (m *LRUCacher) clearBeans(tableName string) {
 // ClearBeans clears all beans in some table
 // ClearBeans clears all beans in some table
 func (m *LRUCacher) ClearBeans(tableName string) {
 func (m *LRUCacher) ClearBeans(tableName string) {
 	m.mutex.Lock()
 	m.mutex.Lock()
-	defer m.mutex.Unlock()
 	m.clearBeans(tableName)
 	m.clearBeans(tableName)
+	m.mutex.Unlock()
 }
 }
 
 
 // PutIds pus ids into table
 // PutIds pus ids into table
 func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
 func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
 	m.mutex.Lock()
 	m.mutex.Lock()
-	defer m.mutex.Unlock()
 	if _, ok := m.sqlIndex[tableName]; !ok {
 	if _, ok := m.sqlIndex[tableName]; !ok {
 		m.sqlIndex[tableName] = make(map[string]*list.Element)
 		m.sqlIndex[tableName] = make(map[string]*list.Element)
 	}
 	}
@@ -207,12 +196,12 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
 		node := e.Value.(*sqlNode)
 		node := e.Value.(*sqlNode)
 		m.delIds(node.tbName, node.sql)
 		m.delIds(node.tbName, node.sql)
 	}
 	}
+	m.mutex.Unlock()
 }
 }
 
 
 // PutBean puts beans into table
 // PutBean puts beans into table
 func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
 func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
 	m.mutex.Lock()
 	m.mutex.Lock()
-	defer m.mutex.Unlock()
 	var el *list.Element
 	var el *list.Element
 	var ok bool
 	var ok bool
 
 
@@ -229,6 +218,7 @@ func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
 		node := e.Value.(*idNode)
 		node := e.Value.(*idNode)
 		m.delBean(node.tbName, node.id)
 		m.delBean(node.tbName, node.id)
 	}
 	}
+	m.mutex.Unlock()
 }
 }
 
 
 func (m *LRUCacher) delIds(tableName, sql string) {
 func (m *LRUCacher) delIds(tableName, sql string) {
@@ -244,8 +234,8 @@ func (m *LRUCacher) delIds(tableName, sql string) {
 // DelIds deletes ids
 // DelIds deletes ids
 func (m *LRUCacher) DelIds(tableName, sql string) {
 func (m *LRUCacher) DelIds(tableName, sql string) {
 	m.mutex.Lock()
 	m.mutex.Lock()
-	defer m.mutex.Unlock()
 	m.delIds(tableName, sql)
 	m.delIds(tableName, sql)
+	m.mutex.Unlock()
 }
 }
 
 
 func (m *LRUCacher) delBean(tableName string, id string) {
 func (m *LRUCacher) delBean(tableName string, id string) {
@@ -261,8 +251,8 @@ func (m *LRUCacher) delBean(tableName string, id string) {
 // DelBean deletes beans in some table
 // DelBean deletes beans in some table
 func (m *LRUCacher) DelBean(tableName string, id string) {
 func (m *LRUCacher) DelBean(tableName string, id string) {
 	m.mutex.Lock()
 	m.mutex.Lock()
-	defer m.mutex.Unlock()
 	m.delBean(tableName, id)
 	m.delBean(tableName, id)
+	m.mutex.Unlock()
 }
 }
 
 
 type idNode struct {
 type idNode struct {

+ 0 - 0
vendor/github.com/go-xorm/xorm/memory_store.go → vendor/github.com/go-xorm/xorm/cache_memory_store.go


+ 26 - 0
vendor/github.com/go-xorm/xorm/context.go

@@ -0,0 +1,26 @@
+// 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.8
+
+package xorm
+
+import "context"
+
+// PingContext tests if database is alive
+func (engine *Engine) PingContext(ctx context.Context) error {
+	session := engine.NewSession()
+	defer session.Close()
+	return session.PingContext(ctx)
+}
+
+// PingContext test if database is ok
+func (session *Session) PingContext(ctx context.Context) error {
+	if session.isAutoClose {
+		defer session.Close()
+	}
+
+	session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName())
+	return session.DB().PingContext(ctx)
+}

+ 102 - 3
vendor/github.com/go-xorm/xorm/convert.go

@@ -209,10 +209,10 @@ func convertAssign(dest, src interface{}) error {
 		if src == nil {
 		if src == nil {
 			dv.Set(reflect.Zero(dv.Type()))
 			dv.Set(reflect.Zero(dv.Type()))
 			return nil
 			return nil
-		} else {
-			dv.Set(reflect.New(dv.Type().Elem()))
-			return convertAssign(dv.Interface(), src)
 		}
 		}
+
+		dv.Set(reflect.New(dv.Type().Elem()))
+		return convertAssign(dv.Interface(), src)
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		s := asString(src)
 		s := asString(src)
 		i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
 		i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
@@ -247,3 +247,102 @@ func convertAssign(dest, src interface{}) error {
 
 
 	return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
 	return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
 }
 }
+
+func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
+	switch tp.Kind() {
+	case reflect.Int64:
+		return vv.Int(), nil
+	case reflect.Int:
+		return int(vv.Int()), nil
+	case reflect.Int32:
+		return int32(vv.Int()), nil
+	case reflect.Int16:
+		return int16(vv.Int()), nil
+	case reflect.Int8:
+		return int8(vv.Int()), nil
+	case reflect.Uint64:
+		return vv.Uint(), nil
+	case reflect.Uint:
+		return uint(vv.Uint()), nil
+	case reflect.Uint32:
+		return uint32(vv.Uint()), nil
+	case reflect.Uint16:
+		return uint16(vv.Uint()), nil
+	case reflect.Uint8:
+		return uint8(vv.Uint()), nil
+	case reflect.String:
+		return vv.String(), nil
+	case reflect.Slice:
+		if tp.Elem().Kind() == reflect.Uint8 {
+			v, err := strconv.ParseInt(string(vv.Interface().([]byte)), 10, 64)
+			if err != nil {
+				return nil, err
+			}
+			return v, nil
+		}
+
+	}
+	return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
+}
+
+func convertFloat(v interface{}) (float64, error) {
+	switch v.(type) {
+	case float32:
+		return float64(v.(float32)), nil
+	case float64:
+		return v.(float64), nil
+	case string:
+		i, err := strconv.ParseFloat(v.(string), 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	case []byte:
+		i, err := strconv.ParseFloat(string(v.([]byte)), 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	}
+	return 0, fmt.Errorf("unsupported type: %v", v)
+}
+
+func convertInt(v interface{}) (int64, error) {
+	switch v.(type) {
+	case int:
+		return int64(v.(int)), nil
+	case int8:
+		return int64(v.(int8)), nil
+	case int16:
+		return int64(v.(int16)), nil
+	case int32:
+		return int64(v.(int32)), nil
+	case int64:
+		return v.(int64), nil
+	case []byte:
+		i, err := strconv.ParseInt(string(v.([]byte)), 10, 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	case string:
+		i, err := strconv.ParseInt(v.(string), 10, 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	}
+	return 0, fmt.Errorf("unsupported type: %v", v)
+}
+
+func asBool(bs []byte) (bool, error) {
+	if len(bs) == 0 {
+		return false, nil
+	}
+	if bs[0] == 0x00 {
+		return false, nil
+	} else if bs[0] == 0x01 {
+		return true, nil
+	}
+	return strconv.ParseBool(string(bs))
+}

+ 21 - 10
vendor/github.com/go-xorm/xorm/dialect_mssql.go

@@ -215,10 +215,10 @@ func (db *mssql) SqlType(c *core.Column) string {
 	var res string
 	var res string
 	switch t := c.SQLType.Name; t {
 	switch t := c.SQLType.Name; t {
 	case core.Bool:
 	case core.Bool:
-		res = core.TinyInt
-		if c.Default == "true" {
+		res = core.Bit
+		if strings.EqualFold(c.Default, "true") {
 			c.Default = "1"
 			c.Default = "1"
-		} else if c.Default == "false" {
+		} else {
 			c.Default = "0"
 			c.Default = "0"
 		}
 		}
 	case core.Serial:
 	case core.Serial:
@@ -250,6 +250,9 @@ func (db *mssql) SqlType(c *core.Column) string {
 	case core.Uuid:
 	case core.Uuid:
 		res = core.Varchar
 		res = core.Varchar
 		c.Length = 40
 		c.Length = 40
+	case core.TinyInt:
+		res = core.TinyInt
+		c.Length = 0
 	default:
 	default:
 		res = t
 		res = t
 	}
 	}
@@ -335,9 +338,15 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
 func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
 func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
 	args := []interface{}{}
 	args := []interface{}{}
 	s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
 	s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
-	      replace(replace(isnull(c.text,''),'(',''),')','') as vdefault   
-          from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id 
-          left join  sys.syscomments c  on a.default_object_id=c.id 
+	      replace(replace(isnull(c.text,''),'(',''),')','') as vdefault,
+		  ISNULL(i.is_primary_key, 0)
+          from sys.columns a 
+		  left join sys.types b on a.user_type_id=b.user_type_id
+          left join sys.syscomments c on a.default_object_id=c.id
+		  LEFT OUTER JOIN 
+    sys.index_columns ic ON ic.object_id = a.object_id AND ic.column_id = a.column_id
+		  LEFT OUTER JOIN 
+    sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
           where a.object_id=object_id('` + tableName + `')`
           where a.object_id=object_id('` + tableName + `')`
 	db.LogSQL(s, args)
 	db.LogSQL(s, args)
 
 
@@ -352,8 +361,8 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
 	for rows.Next() {
 	for rows.Next() {
 		var name, ctype, vdefault string
 		var name, ctype, vdefault string
 		var maxLen, precision, scale int
 		var maxLen, precision, scale int
-		var nullable bool
-		err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault)
+		var nullable, isPK bool
+		err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault, &isPK)
 		if err != nil {
 		if err != nil {
 			return nil, nil, err
 			return nil, nil, err
 		}
 		}
@@ -363,6 +372,7 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
 		col.Name = strings.Trim(name, "` ")
 		col.Name = strings.Trim(name, "` ")
 		col.Nullable = nullable
 		col.Nullable = nullable
 		col.Default = vdefault
 		col.Default = vdefault
+		col.IsPrimaryKey = isPK
 		ct := strings.ToUpper(ctype)
 		ct := strings.ToUpper(ctype)
 		if ct == "DECIMAL" {
 		if ct == "DECIMAL" {
 			col.Length = precision
 			col.Length = precision
@@ -468,9 +478,10 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
 		}
 		}
 
 
 		colName = strings.Trim(colName, "` ")
 		colName = strings.Trim(colName, "` ")
-
+		var isRegular bool
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 			indexName = indexName[5+len(tableName):]
 			indexName = indexName[5+len(tableName):]
+			isRegular = true
 		}
 		}
 
 
 		var index *core.Index
 		var index *core.Index
@@ -479,6 +490,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
 			index = new(core.Index)
 			index = new(core.Index)
 			index.Type = indexType
 			index.Type = indexType
 			index.Name = indexName
 			index.Name = indexName
+			index.IsRegular = isRegular
 			indexes[indexName] = index
 			indexes[indexName] = index
 		}
 		}
 		index.AddColumn(colName)
 		index.AddColumn(colName)
@@ -534,7 +546,6 @@ type odbcDriver struct {
 func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
 func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
 	kv := strings.Split(dataSourceName, ";")
 	kv := strings.Split(dataSourceName, ";")
 	var dbName string
 	var dbName string
-
 	for _, c := range kv {
 	for _, c := range kv {
 		vv := strings.Split(strings.TrimSpace(c), "=")
 		vv := strings.Split(strings.TrimSpace(c), "=")
 		if len(vv) == 2 {
 		if len(vv) == 2 {

+ 8 - 6
vendor/github.com/go-xorm/xorm/dialect_mysql.go

@@ -299,7 +299,7 @@ func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
 func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
 func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
 	args := []interface{}{db.DbName, tableName}
 	args := []interface{}{db.DbName, tableName}
 	s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
 	s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
-		" `COLUMN_KEY`, `EXTRA` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
+		" `COLUMN_KEY`, `EXTRA`,`COLUMN_COMMENT` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
 	db.LogSQL(s, args)
 	db.LogSQL(s, args)
 
 
 	rows, err := db.DB().Query(s, args...)
 	rows, err := db.DB().Query(s, args...)
@@ -314,13 +314,14 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column
 		col := new(core.Column)
 		col := new(core.Column)
 		col.Indexes = make(map[string]int)
 		col.Indexes = make(map[string]int)
 
 
-		var columnName, isNullable, colType, colKey, extra string
+		var columnName, isNullable, colType, colKey, extra, comment string
 		var colDefault *string
 		var colDefault *string
-		err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra)
+		err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra, &comment)
 		if err != nil {
 		if err != nil {
 			return nil, nil, err
 			return nil, nil, err
 		}
 		}
 		col.Name = strings.Trim(columnName, "` ")
 		col.Name = strings.Trim(columnName, "` ")
+		col.Comment = comment
 		if "YES" == isNullable {
 		if "YES" == isNullable {
 			col.Nullable = true
 			col.Nullable = true
 		}
 		}
@@ -407,7 +408,7 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column
 
 
 func (db *mysql) GetTables() ([]*core.Table, error) {
 func (db *mysql) GetTables() ([]*core.Table, error) {
 	args := []interface{}{db.DbName}
 	args := []interface{}{db.DbName}
-	s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from " +
+	s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT`, `TABLE_COMMENT` from " +
 		"`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
 		"`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
 	db.LogSQL(s, args)
 	db.LogSQL(s, args)
 
 
@@ -420,14 +421,15 @@ func (db *mysql) GetTables() ([]*core.Table, error) {
 	tables := make([]*core.Table, 0)
 	tables := make([]*core.Table, 0)
 	for rows.Next() {
 	for rows.Next() {
 		table := core.NewEmptyTable()
 		table := core.NewEmptyTable()
-		var name, engine, tableRows string
+		var name, engine, tableRows, comment string
 		var autoIncr *string
 		var autoIncr *string
-		err = rows.Scan(&name, &engine, &tableRows, &autoIncr)
+		err = rows.Scan(&name, &engine, &tableRows, &autoIncr, &comment)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 
 
 		table.Name = name
 		table.Name = name
+		table.Comment = comment
 		table.StoreEngine = engine
 		table.StoreEngine = engine
 		tables = append(tables, table)
 		tables = append(tables, table)
 	}
 	}

+ 7 - 0
vendor/github.com/go-xorm/xorm/dialect_oracle.go

@@ -824,6 +824,12 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
 
 
 		indexName = strings.Trim(indexName, `" `)
 		indexName = strings.Trim(indexName, `" `)
 
 
+		var isRegular bool
+		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+			indexName = indexName[5+len(tableName):]
+			isRegular = true
+		}
+
 		if uniqueness == "UNIQUE" {
 		if uniqueness == "UNIQUE" {
 			indexType = core.UniqueType
 			indexType = core.UniqueType
 		} else {
 		} else {
@@ -836,6 +842,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
 			index = new(core.Index)
 			index = new(core.Index)
 			index.Type = indexType
 			index.Type = indexType
 			index.Name = indexName
 			index.Name = indexName
+			index.IsRegular = isRegular
 			indexes[indexName] = index
 			indexes[indexName] = index
 		}
 		}
 		index.AddColumn(colName)
 		index.AddColumn(colName)

+ 24 - 47
vendor/github.com/go-xorm/xorm/dialect_postgres.go

@@ -8,7 +8,6 @@ import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
-	"sort"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
@@ -781,6 +780,9 @@ func (db *postgres) SqlType(c *core.Column) string {
 	case core.TinyInt:
 	case core.TinyInt:
 		res = core.SmallInt
 		res = core.SmallInt
 		return res
 		return res
+	case core.Bit:
+		res = core.Boolean
+		return res
 	case core.MediumInt, core.Int, core.Integer:
 	case core.MediumInt, core.Int, core.Integer:
 		if c.IsAutoIncrement {
 		if c.IsAutoIncrement {
 			return core.Serial
 			return core.Serial
@@ -1078,9 +1080,10 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
 		}
 		}
 		cs := strings.Split(indexdef, "(")
 		cs := strings.Split(indexdef, "(")
 		colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
 		colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
-
+		var isRegular bool
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 			newIdxName := indexName[5+len(tableName):]
 			newIdxName := indexName[5+len(tableName):]
+			isRegular = true
 			if newIdxName != "" {
 			if newIdxName != "" {
 				indexName = newIdxName
 				indexName = newIdxName
 			}
 			}
@@ -1090,6 +1093,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
 		for _, colName := range colNames {
 		for _, colName := range colNames {
 			index.Cols = append(index.Cols, strings.Trim(colName, `" `))
 			index.Cols = append(index.Cols, strings.Trim(colName, `" `))
 		}
 		}
+		index.IsRegular = isRegular
 		indexes[index.Name] = index
 		indexes[index.Name] = index
 	}
 	}
 	return indexes, nil
 	return indexes, nil
@@ -1112,10 +1116,6 @@ func (vs values) Get(k string) (v string) {
 	return vs[k]
 	return vs[k]
 }
 }
 
 
-func errorf(s string, args ...interface{}) {
-	panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
-}
-
 func parseURL(connstr string) (string, error) {
 func parseURL(connstr string) (string, error) {
 	u, err := url.Parse(connstr)
 	u, err := url.Parse(connstr)
 	if err != nil {
 	if err != nil {
@@ -1126,46 +1126,18 @@ func parseURL(connstr string) (string, error) {
 		return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
 		return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
 	}
 	}
 
 
-	var kvs []string
 	escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
 	escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
-	accrue := func(k, v string) {
-		if v != "" {
-			kvs = append(kvs, k+"="+escaper.Replace(v))
-		}
-	}
-
-	if u.User != nil {
-		v := u.User.Username()
-		accrue("user", v)
-
-		v, _ = u.User.Password()
-		accrue("password", v)
-	}
-
-	i := strings.Index(u.Host, ":")
-	if i < 0 {
-		accrue("host", u.Host)
-	} else {
-		accrue("host", u.Host[:i])
-		accrue("port", u.Host[i+1:])
-	}
 
 
 	if u.Path != "" {
 	if u.Path != "" {
-		accrue("dbname", u.Path[1:])
-	}
-
-	q := u.Query()
-	for k := range q {
-		accrue(k, q.Get(k))
+		return escaper.Replace(u.Path[1:]), nil
 	}
 	}
 
 
-	sort.Strings(kvs) // Makes testing easier (not a performance concern)
-	return strings.Join(kvs, " "), nil
+	return "", nil
 }
 }
 
 
-func parseOpts(name string, o values) {
+func parseOpts(name string, o values) error {
 	if len(name) == 0 {
 	if len(name) == 0 {
-		return
+		return fmt.Errorf("invalid options: %s", name)
 	}
 	}
 
 
 	name = strings.TrimSpace(name)
 	name = strings.TrimSpace(name)
@@ -1174,31 +1146,36 @@ func parseOpts(name string, o values) {
 	for _, p := range ps {
 	for _, p := range ps {
 		kv := strings.Split(p, "=")
 		kv := strings.Split(p, "=")
 		if len(kv) < 2 {
 		if len(kv) < 2 {
-			errorf("invalid option: %q", p)
+			return fmt.Errorf("invalid option: %q", p)
 		}
 		}
 		o.Set(kv[0], kv[1])
 		o.Set(kv[0], kv[1])
 	}
 	}
+
+	return nil
 }
 }
 
 
 func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
 func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
 	db := &core.Uri{DbType: core.POSTGRES}
 	db := &core.Uri{DbType: core.POSTGRES}
-	o := make(values)
 	var err error
 	var err error
+
 	if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
 	if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
-		dataSourceName, err = parseURL(dataSourceName)
+		db.DbName, err = parseURL(dataSourceName)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
+	} else {
+		o := make(values)
+		err = parseOpts(dataSourceName, o)
+		if err != nil {
+			return nil, err
+		}
+
+		db.DbName = o.Get("dbname")
 	}
 	}
-	parseOpts(dataSourceName, o)
 
 
-	db.DbName = o.Get("dbname")
 	if db.DbName == "" {
 	if db.DbName == "" {
 		return nil, errors.New("dbname is empty")
 		return nil, errors.New("dbname is empty")
 	}
 	}
-	/*db.Schema = o.Get("schema")
-	if len(db.Schema) == 0 {
-		db.Schema = "public"
-	}*/
+
 	return db, nil
 	return db, nil
 }
 }

+ 17 - 4
vendor/github.com/go-xorm/xorm/dialect_sqlite3.go

@@ -14,10 +14,6 @@ import (
 	"github.com/go-xorm/core"
 	"github.com/go-xorm/core"
 )
 )
 
 
-// func init() {
-// 	RegisterDialect("sqlite3", &sqlite3{})
-// }
-
 var (
 var (
 	sqlite3ReservedWords = map[string]bool{
 	sqlite3ReservedWords = map[string]bool{
 		"ABORT":             true,
 		"ABORT":             true,
@@ -310,11 +306,25 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu
 	for _, colStr := range colCreates {
 	for _, colStr := range colCreates {
 		reg = regexp.MustCompile(`,\s`)
 		reg = regexp.MustCompile(`,\s`)
 		colStr = reg.ReplaceAllString(colStr, ",")
 		colStr = reg.ReplaceAllString(colStr, ",")
+		if strings.HasPrefix(strings.TrimSpace(colStr), "PRIMARY KEY") {
+			parts := strings.Split(strings.TrimSpace(colStr), "(")
+			if len(parts) == 2 {
+				pkCols := strings.Split(strings.TrimRight(strings.TrimSpace(parts[1]), ")"), ",")
+				for _, pk := range pkCols {
+					if col, ok := cols[strings.Trim(strings.TrimSpace(pk), "`")]; ok {
+						col.IsPrimaryKey = true
+					}
+				}
+			}
+			continue
+		}
+
 		fields := strings.Fields(strings.TrimSpace(colStr))
 		fields := strings.Fields(strings.TrimSpace(colStr))
 		col := new(core.Column)
 		col := new(core.Column)
 		col.Indexes = make(map[string]int)
 		col.Indexes = make(map[string]int)
 		col.Nullable = true
 		col.Nullable = true
 		col.DefaultIsEmpty = true
 		col.DefaultIsEmpty = true
+
 		for idx, field := range fields {
 		for idx, field := range fields {
 			if idx == 0 {
 			if idx == 0 {
 				col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`)
 				col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`)
@@ -405,8 +415,10 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
 		}
 		}
 
 
 		indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
 		indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
+		var isRegular bool
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 			index.Name = indexName[5+len(tableName):]
 			index.Name = indexName[5+len(tableName):]
+			isRegular = true
 		} else {
 		} else {
 			index.Name = indexName
 			index.Name = indexName
 		}
 		}
@@ -425,6 +437,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
 		for _, col := range colIndexes {
 		for _, col := range colIndexes {
 			index.Cols = append(index.Cols, strings.Trim(col, "` []"))
 			index.Cols = append(index.Cols, strings.Trim(col, "` []"))
 		}
 		}
+		index.IsRegular = isRegular
 		indexes[index.Name] = index
 		indexes[index.Name] = index
 	}
 	}
 
 

+ 10 - 3
vendor/github.com/go-xorm/xorm/doc.go

@@ -8,7 +8,7 @@ Package xorm is a simple and powerful ORM for Go.
 
 
 Installation
 Installation
 
 
-Make sure you have installed Go 1.1+ and then:
+Make sure you have installed Go 1.6+ and then:
 
 
     go get github.com/go-xorm/xorm
     go get github.com/go-xorm/xorm
 
 
@@ -51,11 +51,15 @@ There are 8 major ORM methods and many helpful methods to use to operate databas
     // INSERT INTO struct1 () values ()
     // INSERT INTO struct1 () values ()
     // INSERT INTO struct2 () values (),(),()
     // INSERT INTO struct2 () values (),(),()
 
 
-2. Query one record from database
+2. Query one record or one variable from database
 
 
     has, err := engine.Get(&user)
     has, err := engine.Get(&user)
     // SELECT * FROM user LIMIT 1
     // SELECT * FROM user LIMIT 1
 
 
+    var id int64
+    has, err := engine.Table("user").Where("name = ?", name).Get(&id)
+    // SELECT id FROM user WHERE name = ? LIMIT 1
+
 3. Query multiple records from database
 3. Query multiple records from database
 
 
     var sliceOfStructs []Struct
     var sliceOfStructs []Struct
@@ -86,7 +90,7 @@ another is Rows
 
 
 5. Update one or more records
 5. Update one or more records
 
 
-    affected, err := engine.Id(...).Update(&user)
+    affected, err := engine.ID(...).Update(&user)
     // UPDATE user SET ...
     // UPDATE user SET ...
 
 
 6. Delete one or more records, Delete MUST has condition
 6. Delete one or more records, Delete MUST has condition
@@ -99,6 +103,9 @@ another is Rows
     counts, err := engine.Count(&user)
     counts, err := engine.Count(&user)
     // SELECT count(*) AS total FROM user
     // SELECT count(*) AS total FROM user
 
 
+    counts, err := engine.SQL("select count(*) FROM user").Count()
+    // select count(*) FROM user
+
 8. Sum records
 8. Sum records
 
 
     sumFloat64, err := engine.Sum(&user, "id")
     sumFloat64, err := engine.Sum(&user, "id")

+ 284 - 210
vendor/github.com/go-xorm/xorm/engine.go

@@ -19,6 +19,7 @@ import (
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
+	"github.com/go-xorm/builder"
 	"github.com/go-xorm/core"
 	"github.com/go-xorm/core"
 )
 )
 
 
@@ -40,12 +41,29 @@ type Engine struct {
 	showExecTime bool
 	showExecTime bool
 
 
 	logger     core.ILogger
 	logger     core.ILogger
-	TZLocation *time.Location
+	TZLocation *time.Location // The timezone of the application
 	DatabaseTZ *time.Location // The timezone of the database
 	DatabaseTZ *time.Location // The timezone of the database
 
 
 	disableGlobalCache bool
 	disableGlobalCache bool
 
 
 	tagHandlers map[string]tagHandler
 	tagHandlers map[string]tagHandler
+
+	engineGroup *EngineGroup
+}
+
+// BufferSize sets buffer size for iterate
+func (engine *Engine) BufferSize(size int) *Session {
+	session := engine.NewSession()
+	session.isAutoClose = true
+	return session.BufferSize(size)
+}
+
+// CondDeleted returns the conditions whether a record is soft deleted.
+func (engine *Engine) CondDeleted(colName string) builder.Cond {
+	if engine.dialect.DBType() == core.MSSQL {
+		return builder.IsNull{colName}
+	}
+	return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1})
 }
 }
 
 
 // ShowSQL show SQL statement or not on logger if log level is great than INFO
 // ShowSQL show SQL statement or not on logger if log level is great than INFO
@@ -78,6 +96,11 @@ func (engine *Engine) SetLogger(logger core.ILogger) {
 	engine.dialect.SetLogger(logger)
 	engine.dialect.SetLogger(logger)
 }
 }
 
 
+// SetLogLevel sets the logger level
+func (engine *Engine) SetLogLevel(level core.LogLevel) {
+	engine.logger.SetLevel(level)
+}
+
 // SetDisableGlobalCache disable global cache or not
 // SetDisableGlobalCache disable global cache or not
 func (engine *Engine) SetDisableGlobalCache(disable bool) {
 func (engine *Engine) SetDisableGlobalCache(disable bool) {
 	if engine.disableGlobalCache != disable {
 	if engine.disableGlobalCache != disable {
@@ -143,7 +166,6 @@ func (engine *Engine) Quote(value string) string {
 
 
 // QuoteTo quotes string and writes into the buffer
 // QuoteTo quotes string and writes into the buffer
 func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) {
 func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) {
-
 	if buf == nil {
 	if buf == nil {
 		return
 		return
 	}
 	}
@@ -169,7 +191,7 @@ func (engine *Engine) quote(sql string) string {
 	return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr()
 	return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr()
 }
 }
 
 
-// SqlType will be depracated, please use SQLType instead
+// SqlType will be deprecated, please use SQLType instead
 //
 //
 // Deprecated: use SQLType instead
 // Deprecated: use SQLType instead
 func (engine *Engine) SqlType(c *core.Column) string {
 func (engine *Engine) SqlType(c *core.Column) string {
@@ -201,26 +223,36 @@ func (engine *Engine) SetDefaultCacher(cacher core.Cacher) {
 	engine.Cacher = cacher
 	engine.Cacher = cacher
 }
 }
 
 
+// GetDefaultCacher returns the default cacher
+func (engine *Engine) GetDefaultCacher() core.Cacher {
+	return engine.Cacher
+}
+
 // NoCache If you has set default cacher, and you want temporilly stop use cache,
 // NoCache If you has set default cacher, and you want temporilly stop use cache,
 // you can use NoCache()
 // you can use NoCache()
 func (engine *Engine) NoCache() *Session {
 func (engine *Engine) NoCache() *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.NoCache()
 	return session.NoCache()
 }
 }
 
 
 // NoCascade If you do not want to auto cascade load object
 // NoCascade If you do not want to auto cascade load object
 func (engine *Engine) NoCascade() *Session {
 func (engine *Engine) NoCascade() *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.NoCascade()
 	return session.NoCascade()
 }
 }
 
 
 // MapCacher Set a table use a special cacher
 // MapCacher Set a table use a special cacher
-func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
+func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error {
 	v := rValue(bean)
 	v := rValue(bean)
-	tb := engine.autoMapType(v)
+	tb, err := engine.autoMapType(v)
+	if err != nil {
+		return err
+	}
+
 	tb.Cacher = cacher
 	tb.Cacher = cacher
+	return nil
 }
 }
 
 
 // NewDB provides an interface to operate database directly
 // NewDB provides an interface to operate database directly
@@ -240,7 +272,7 @@ func (engine *Engine) Dialect() core.Dialect {
 
 
 // NewSession New a session
 // NewSession New a session
 func (engine *Engine) NewSession() *Session {
 func (engine *Engine) NewSession() *Session {
-	session := &Session{Engine: engine}
+	session := &Session{engine: engine}
 	session.Init()
 	session.Init()
 	return session
 	return session
 }
 }
@@ -254,7 +286,6 @@ func (engine *Engine) Close() error {
 func (engine *Engine) Ping() error {
 func (engine *Engine) Ping() error {
 	session := engine.NewSession()
 	session := engine.NewSession()
 	defer session.Close()
 	defer session.Close()
-	engine.logger.Infof("PING DATABASE %v", engine.DriverName())
 	return session.Ping()
 	return session.Ping()
 }
 }
 
 
@@ -262,43 +293,13 @@ func (engine *Engine) Ping() error {
 func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
 func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
 	if engine.showSQL && !engine.showExecTime {
 	if engine.showSQL && !engine.showExecTime {
 		if len(sqlArgs) > 0 {
 		if len(sqlArgs) > 0 {
-			engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs)
+			engine.logger.Infof("[SQL] %v %#v", sqlStr, sqlArgs)
 		} else {
 		} else {
 			engine.logger.Infof("[SQL] %v", sqlStr)
 			engine.logger.Infof("[SQL] %v", sqlStr)
 		}
 		}
 	}
 	}
 }
 }
 
 
-func (engine *Engine) logSQLQueryTime(sqlStr string, args []interface{}, executionBlock func() (*core.Stmt, *core.Rows, error)) (*core.Stmt, *core.Rows, error) {
-	if engine.showSQL && engine.showExecTime {
-		b4ExecTime := time.Now()
-		stmt, res, err := executionBlock()
-		execDuration := time.Since(b4ExecTime)
-		if len(args) > 0 {
-			engine.logger.Infof("[SQL] %s %v - took: %v", sqlStr, args, execDuration)
-		} else {
-			engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration)
-		}
-		return stmt, res, err
-	}
-	return executionBlock()
-}
-
-func (engine *Engine) logSQLExecutionTime(sqlStr string, args []interface{}, executionBlock func() (sql.Result, error)) (sql.Result, error) {
-	if engine.showSQL && engine.showExecTime {
-		b4ExecTime := time.Now()
-		res, err := executionBlock()
-		execDuration := time.Since(b4ExecTime)
-		if len(args) > 0 {
-			engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration)
-		} else {
-			engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration)
-		}
-		return res, err
-	}
-	return executionBlock()
-}
-
 // Sql provides raw sql input parameter. When you have a complex SQL statement
 // Sql provides raw sql input parameter. When you have a complex SQL statement
 // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
 // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
 //
 //
@@ -315,7 +316,7 @@ func (engine *Engine) Sql(querystring string, args ...interface{}) *Session {
 // This    code will execute "select * from user" and set the records to users
 // This    code will execute "select * from user" and set the records to users
 func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
 func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.SQL(query, args...)
 	return session.SQL(query, args...)
 }
 }
 
 
@@ -324,14 +325,14 @@ func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
 // invoked. Call NoAutoTime if you dont' want to fill automatically.
 // invoked. Call NoAutoTime if you dont' want to fill automatically.
 func (engine *Engine) NoAutoTime() *Session {
 func (engine *Engine) NoAutoTime() *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.NoAutoTime()
 	return session.NoAutoTime()
 }
 }
 
 
 // NoAutoCondition disable auto generate Where condition from bean or not
 // NoAutoCondition disable auto generate Where condition from bean or not
 func (engine *Engine) NoAutoCondition(no ...bool) *Session {
 func (engine *Engine) NoAutoCondition(no ...bool) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.NoAutoCondition(no...)
 	return session.NoAutoCondition(no...)
 }
 }
 
 
@@ -565,56 +566,56 @@ func (engine *Engine) tbName(v reflect.Value) string {
 // Cascade use cascade or not
 // Cascade use cascade or not
 func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
 func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Cascade(trueOrFalse...)
 	return session.Cascade(trueOrFalse...)
 }
 }
 
 
 // Where method provide a condition query
 // Where method provide a condition query
 func (engine *Engine) Where(query interface{}, args ...interface{}) *Session {
 func (engine *Engine) Where(query interface{}, args ...interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Where(query, args...)
 	return session.Where(query, args...)
 }
 }
 
 
-// Id will be depracated, please use ID instead
+// Id will be deprecated, please use ID instead
 func (engine *Engine) Id(id interface{}) *Session {
 func (engine *Engine) Id(id interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Id(id)
 	return session.Id(id)
 }
 }
 
 
 // ID method provoide a condition as (id) = ?
 // ID method provoide a condition as (id) = ?
 func (engine *Engine) ID(id interface{}) *Session {
 func (engine *Engine) ID(id interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.ID(id)
 	return session.ID(id)
 }
 }
 
 
 // Before apply before Processor, affected bean is passed to closure arg
 // Before apply before Processor, affected bean is passed to closure arg
 func (engine *Engine) Before(closures func(interface{})) *Session {
 func (engine *Engine) Before(closures func(interface{})) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Before(closures)
 	return session.Before(closures)
 }
 }
 
 
 // After apply after insert Processor, affected bean is passed to closure arg
 // After apply after insert Processor, affected bean is passed to closure arg
 func (engine *Engine) After(closures func(interface{})) *Session {
 func (engine *Engine) After(closures func(interface{})) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.After(closures)
 	return session.After(closures)
 }
 }
 
 
 // Charset set charset when create table, only support mysql now
 // Charset set charset when create table, only support mysql now
 func (engine *Engine) Charset(charset string) *Session {
 func (engine *Engine) Charset(charset string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Charset(charset)
 	return session.Charset(charset)
 }
 }
 
 
 // StoreEngine set store engine when create table, only support mysql now
 // StoreEngine set store engine when create table, only support mysql now
 func (engine *Engine) StoreEngine(storeEngine string) *Session {
 func (engine *Engine) StoreEngine(storeEngine string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.StoreEngine(storeEngine)
 	return session.StoreEngine(storeEngine)
 }
 }
 
 
@@ -623,35 +624,35 @@ func (engine *Engine) StoreEngine(storeEngine string) *Session {
 // but distinct will not provide id
 // but distinct will not provide id
 func (engine *Engine) Distinct(columns ...string) *Session {
 func (engine *Engine) Distinct(columns ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Distinct(columns...)
 	return session.Distinct(columns...)
 }
 }
 
 
 // Select customerize your select columns or contents
 // Select customerize your select columns or contents
 func (engine *Engine) Select(str string) *Session {
 func (engine *Engine) Select(str string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Select(str)
 	return session.Select(str)
 }
 }
 
 
 // Cols only use the parameters as select or update columns
 // Cols only use the parameters as select or update columns
 func (engine *Engine) Cols(columns ...string) *Session {
 func (engine *Engine) Cols(columns ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Cols(columns...)
 	return session.Cols(columns...)
 }
 }
 
 
 // AllCols indicates that all columns should be use
 // AllCols indicates that all columns should be use
 func (engine *Engine) AllCols() *Session {
 func (engine *Engine) AllCols() *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.AllCols()
 	return session.AllCols()
 }
 }
 
 
 // MustCols specify some columns must use even if they are empty
 // MustCols specify some columns must use even if they are empty
 func (engine *Engine) MustCols(columns ...string) *Session {
 func (engine *Engine) MustCols(columns ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.MustCols(columns...)
 	return session.MustCols(columns...)
 }
 }
 
 
@@ -662,77 +663,84 @@ func (engine *Engine) MustCols(columns ...string) *Session {
 // it will use parameters's columns
 // it will use parameters's columns
 func (engine *Engine) UseBool(columns ...string) *Session {
 func (engine *Engine) UseBool(columns ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.UseBool(columns...)
 	return session.UseBool(columns...)
 }
 }
 
 
 // Omit only not use the parameters as select or update columns
 // Omit only not use the parameters as select or update columns
 func (engine *Engine) Omit(columns ...string) *Session {
 func (engine *Engine) Omit(columns ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Omit(columns...)
 	return session.Omit(columns...)
 }
 }
 
 
 // Nullable set null when column is zero-value and nullable for update
 // Nullable set null when column is zero-value and nullable for update
 func (engine *Engine) Nullable(columns ...string) *Session {
 func (engine *Engine) Nullable(columns ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Nullable(columns...)
 	return session.Nullable(columns...)
 }
 }
 
 
 // In will generate "column IN (?, ?)"
 // In will generate "column IN (?, ?)"
 func (engine *Engine) In(column string, args ...interface{}) *Session {
 func (engine *Engine) In(column string, args ...interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.In(column, args...)
 	return session.In(column, args...)
 }
 }
 
 
+// NotIn will generate "column NOT IN (?, ?)"
+func (engine *Engine) NotIn(column string, args ...interface{}) *Session {
+	session := engine.NewSession()
+	session.isAutoClose = true
+	return session.NotIn(column, args...)
+}
+
 // Incr provides a update string like "column = column + ?"
 // Incr provides a update string like "column = column + ?"
 func (engine *Engine) Incr(column string, arg ...interface{}) *Session {
 func (engine *Engine) Incr(column string, arg ...interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Incr(column, arg...)
 	return session.Incr(column, arg...)
 }
 }
 
 
 // Decr provides a update string like "column = column - ?"
 // Decr provides a update string like "column = column - ?"
 func (engine *Engine) Decr(column string, arg ...interface{}) *Session {
 func (engine *Engine) Decr(column string, arg ...interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Decr(column, arg...)
 	return session.Decr(column, arg...)
 }
 }
 
 
 // SetExpr provides a update string like "column = {expression}"
 // SetExpr provides a update string like "column = {expression}"
 func (engine *Engine) SetExpr(column string, expression string) *Session {
 func (engine *Engine) SetExpr(column string, expression string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.SetExpr(column, expression)
 	return session.SetExpr(column, expression)
 }
 }
 
 
 // Table temporarily change the Get, Find, Update's table
 // Table temporarily change the Get, Find, Update's table
 func (engine *Engine) Table(tableNameOrBean interface{}) *Session {
 func (engine *Engine) Table(tableNameOrBean interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Table(tableNameOrBean)
 	return session.Table(tableNameOrBean)
 }
 }
 
 
 // Alias set the table alias
 // Alias set the table alias
 func (engine *Engine) Alias(alias string) *Session {
 func (engine *Engine) Alias(alias string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Alias(alias)
 	return session.Alias(alias)
 }
 }
 
 
 // Limit will generate "LIMIT start, limit"
 // Limit will generate "LIMIT start, limit"
 func (engine *Engine) Limit(limit int, start ...int) *Session {
 func (engine *Engine) Limit(limit int, start ...int) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Limit(limit, start...)
 	return session.Limit(limit, start...)
 }
 }
 
 
 // Desc will generate "ORDER BY column1 DESC, column2 DESC"
 // Desc will generate "ORDER BY column1 DESC, column2 DESC"
 func (engine *Engine) Desc(colNames ...string) *Session {
 func (engine *Engine) Desc(colNames ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Desc(colNames...)
 	return session.Desc(colNames...)
 }
 }
 
 
@@ -744,39 +752,53 @@ func (engine *Engine) Desc(colNames ...string) *Session {
 //
 //
 func (engine *Engine) Asc(colNames ...string) *Session {
 func (engine *Engine) Asc(colNames ...string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Asc(colNames...)
 	return session.Asc(colNames...)
 }
 }
 
 
 // OrderBy will generate "ORDER BY order"
 // OrderBy will generate "ORDER BY order"
 func (engine *Engine) OrderBy(order string) *Session {
 func (engine *Engine) OrderBy(order string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.OrderBy(order)
 	return session.OrderBy(order)
 }
 }
 
 
+// Prepare enables prepare statement
+func (engine *Engine) Prepare() *Session {
+	session := engine.NewSession()
+	session.isAutoClose = true
+	return session.Prepare()
+}
+
 // Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
 // Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
 func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
 func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Join(joinOperator, tablename, condition, args...)
 	return session.Join(joinOperator, tablename, condition, args...)
 }
 }
 
 
 // GroupBy generate group by statement
 // GroupBy generate group by statement
 func (engine *Engine) GroupBy(keys string) *Session {
 func (engine *Engine) GroupBy(keys string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.GroupBy(keys)
 	return session.GroupBy(keys)
 }
 }
 
 
 // Having generate having statement
 // Having generate having statement
 func (engine *Engine) Having(conditions string) *Session {
 func (engine *Engine) Having(conditions string) *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Having(conditions)
 	return session.Having(conditions)
 }
 }
 
 
-func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
+// UnMapType removes the datbase mapper of a type
+func (engine *Engine) UnMapType(t reflect.Type) {
+	engine.mutex.Lock()
+	defer engine.mutex.Unlock()
+	delete(engine.Tables, t)
+}
+
+func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) {
 	t := v.Type()
 	t := v.Type()
 	engine.mutex.Lock()
 	engine.mutex.Lock()
 	defer engine.mutex.Unlock()
 	defer engine.mutex.Unlock()
@@ -785,24 +807,23 @@ func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
 		var err error
 		var err error
 		table, err = engine.mapType(v)
 		table, err = engine.mapType(v)
 		if err != nil {
 		if err != nil {
-			engine.logger.Error(err)
-		} else {
-			engine.Tables[t] = table
-			if engine.Cacher != nil {
-				if v.CanAddr() {
-					engine.GobRegister(v.Addr().Interface())
-				} else {
-					engine.GobRegister(v.Interface())
-				}
+			return nil, err
+		}
+
+		engine.Tables[t] = table
+		if engine.Cacher != nil {
+			if v.CanAddr() {
+				engine.GobRegister(v.Addr().Interface())
+			} else {
+				engine.GobRegister(v.Interface())
 			}
 			}
 		}
 		}
 	}
 	}
-	return table
+	return table, nil
 }
 }
 
 
 // GobRegister register one struct to gob for cache use
 // GobRegister register one struct to gob for cache use
 func (engine *Engine) GobRegister(v interface{}) *Engine {
 func (engine *Engine) GobRegister(v interface{}) *Engine {
-	//fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v)
 	gob.Register(v)
 	gob.Register(v)
 	return engine
 	return engine
 }
 }
@@ -813,10 +834,19 @@ type Table struct {
 	Name string
 	Name string
 }
 }
 
 
+// IsValid if table is valid
+func (t *Table) IsValid() bool {
+	return t.Table != nil && len(t.Name) > 0
+}
+
 // TableInfo get table info according to bean's content
 // TableInfo get table info according to bean's content
 func (engine *Engine) TableInfo(bean interface{}) *Table {
 func (engine *Engine) TableInfo(bean interface{}) *Table {
 	v := rValue(bean)
 	v := rValue(bean)
-	return &Table{engine.autoMapType(v), engine.tbName(v)}
+	tb, err := engine.autoMapType(v)
+	if err != nil {
+		engine.logger.Error(err)
+	}
+	return &Table{tb, engine.tbName(v)}
 }
 }
 
 
 func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
 func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
@@ -911,6 +941,7 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
 
 
 					k := strings.ToUpper(key)
 					k := strings.ToUpper(key)
 					ctx.tagName = k
 					ctx.tagName = k
+					ctx.params = []string{}
 
 
 					pStart := strings.Index(k, "(")
 					pStart := strings.Index(k, "(")
 					if pStart == 0 {
 					if pStart == 0 {
@@ -918,18 +949,18 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
 					}
 					}
 					if pStart > -1 {
 					if pStart > -1 {
 						if !strings.HasSuffix(k, ")") {
 						if !strings.HasSuffix(k, ")") {
-							return nil, errors.New("cannot match ) charactor")
+							return nil, fmt.Errorf("field %s tag %s cannot match ) charactor", col.FieldName, key)
 						}
 						}
 
 
 						ctx.tagName = k[:pStart]
 						ctx.tagName = k[:pStart]
-						ctx.params = strings.Split(k[pStart+1:len(k)-1], ",")
+						ctx.params = strings.Split(key[pStart+1:len(k)-1], ",")
 					}
 					}
 
 
 					if j > 0 {
 					if j > 0 {
 						ctx.preTag = strings.ToUpper(tags[j-1])
 						ctx.preTag = strings.ToUpper(tags[j-1])
 					}
 					}
 					if j < len(tags)-1 {
 					if j < len(tags)-1 {
-						ctx.nextTag = strings.ToUpper(tags[j+1])
+						ctx.nextTag = tags[j+1]
 					} else {
 					} else {
 						ctx.nextTag = ""
 						ctx.nextTag = ""
 					}
 					}
@@ -993,6 +1024,10 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
 			col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
 			col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
 				t.Field(i).Name, sqlType, sqlType.DefaultLength,
 				t.Field(i).Name, sqlType, sqlType.DefaultLength,
 				sqlType.DefaultLength2, true)
 				sqlType.DefaultLength2, true)
+
+			if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) {
+				idFieldColName = col.Name
+			}
 		}
 		}
 		if col.IsAutoIncrement {
 		if col.IsAutoIncrement {
 			col.Nullable = false
 			col.Nullable = false
@@ -1000,9 +1035,6 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
 
 
 		table.AddColumn(col)
 		table.AddColumn(col)
 
 
-		if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) {
-			idFieldColName = col.Name
-		}
 	} // end for
 	} // end for
 
 
 	if idFieldColName != "" && len(table.PrimaryKeys) == 0 {
 	if idFieldColName != "" && len(table.PrimaryKeys) == 0 {
@@ -1066,21 +1098,54 @@ func (engine *Engine) IdOfV(rv reflect.Value) core.PK {
 
 
 // IDOfV get id from one value of struct
 // IDOfV get id from one value of struct
 func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
 func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
+	pk, err := engine.idOfV(rv)
+	if err != nil {
+		engine.logger.Error(err)
+		return nil
+	}
+	return pk
+}
+
+func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) {
 	v := reflect.Indirect(rv)
 	v := reflect.Indirect(rv)
-	table := engine.autoMapType(v)
+	table, err := engine.autoMapType(v)
+	if err != nil {
+		return nil, err
+	}
+
 	pk := make([]interface{}, len(table.PrimaryKeys))
 	pk := make([]interface{}, len(table.PrimaryKeys))
 	for i, col := range table.PKColumns() {
 	for i, col := range table.PKColumns() {
+		var err error
 		pkField := v.FieldByName(col.FieldName)
 		pkField := v.FieldByName(col.FieldName)
 		switch pkField.Kind() {
 		switch pkField.Kind() {
 		case reflect.String:
 		case reflect.String:
-			pk[i] = pkField.String()
+			pk[i], err = engine.idTypeAssertion(col, pkField.String())
 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-			pk[i] = pkField.Int()
+			pk[i], err = engine.idTypeAssertion(col, strconv.FormatInt(pkField.Int(), 10))
 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-			pk[i] = pkField.Uint()
+			// id of uint will be converted to int64
+			pk[i], err = engine.idTypeAssertion(col, strconv.FormatUint(pkField.Uint(), 10))
 		}
 		}
+
+		if err != nil {
+			return nil, err
+		}
+	}
+	return core.PK(pk), nil
+}
+
+func (engine *Engine) idTypeAssertion(col *core.Column, sid string) (interface{}, error) {
+	if col.SQLType.IsNumeric() {
+		n, err := strconv.ParseInt(sid, 10, 64)
+		if err != nil {
+			return nil, err
+		}
+		return n, nil
+	} else if col.SQLType.IsText() {
+		return sid, nil
+	} else {
+		return nil, errors.New("not supported")
 	}
 	}
-	return core.PK(pk)
 }
 }
 
 
 // CreateIndexes create indexes
 // CreateIndexes create indexes
@@ -1101,13 +1166,6 @@ func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
 	return table.Cacher
 	return table.Cacher
 }
 }
 
 
-func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
-	if table := engine.autoMapType(v); table != nil {
-		return table.Cacher
-	}
-	return engine.Cacher
-}
-
 // ClearCacheBean if enabled cache, clear the cache bean
 // ClearCacheBean if enabled cache, clear the cache bean
 func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
 func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
 	v := rValue(bean)
 	v := rValue(bean)
@@ -1116,7 +1174,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
 		return errors.New("error params")
 		return errors.New("error params")
 	}
 	}
 	tableName := engine.tbName(v)
 	tableName := engine.tbName(v)
-	table := engine.autoMapType(v)
+	table, err := engine.autoMapType(v)
+	if err != nil {
+		return err
+	}
 	cacher := table.Cacher
 	cacher := table.Cacher
 	if cacher == nil {
 	if cacher == nil {
 		cacher = engine.Cacher
 		cacher = engine.Cacher
@@ -1137,7 +1198,11 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
 			return errors.New("error params")
 			return errors.New("error params")
 		}
 		}
 		tableName := engine.tbName(v)
 		tableName := engine.tbName(v)
-		table := engine.autoMapType(v)
+		table, err := engine.autoMapType(v)
+		if err != nil {
+			return err
+		}
+
 		cacher := table.Cacher
 		cacher := table.Cacher
 		if cacher == nil {
 		if cacher == nil {
 			cacher = engine.Cacher
 			cacher = engine.Cacher
@@ -1154,19 +1219,23 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
 // table, column, index, unique. but will not delete or change anything.
 // table, column, index, unique. but will not delete or change anything.
 // If you change some field, you should change the database manually.
 // If you change some field, you should change the database manually.
 func (engine *Engine) Sync(beans ...interface{}) error {
 func (engine *Engine) Sync(beans ...interface{}) error {
+	session := engine.NewSession()
+	defer session.Close()
+
 	for _, bean := range beans {
 	for _, bean := range beans {
 		v := rValue(bean)
 		v := rValue(bean)
 		tableName := engine.tbName(v)
 		tableName := engine.tbName(v)
-		table := engine.autoMapType(v)
+		table, err := engine.autoMapType(v)
+		if err != nil {
+			return err
+		}
 
 
-		s := engine.NewSession()
-		defer s.Close()
-		isExist, err := s.Table(bean).isTableExist(tableName)
+		isExist, err := session.Table(bean).isTableExist(tableName)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 		if !isExist {
 		if !isExist {
-			err = engine.CreateTables(bean)
+			err = session.createTable(bean)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
@@ -1177,11 +1246,11 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 		  }*/
 		  }*/
 		var isEmpty bool
 		var isEmpty bool
 		if isEmpty {
 		if isEmpty {
-			err = engine.DropTables(bean)
+			err = session.dropTable(bean)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
-			err = engine.CreateTables(bean)
+			err = session.createTable(bean)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
@@ -1192,9 +1261,9 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 					return err
 					return err
 				}
 				}
 				if !isExist {
 				if !isExist {
-					session := engine.NewSession()
-					session.Statement.setRefValue(v)
-					defer session.Close()
+					if err := session.statement.setRefValue(v); err != nil {
+						return err
+					}
 					err = session.addColumn(col.Name)
 					err = session.addColumn(col.Name)
 					if err != nil {
 					if err != nil {
 						return err
 						return err
@@ -1203,19 +1272,19 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 			}
 			}
 
 
 			for name, index := range table.Indexes {
 			for name, index := range table.Indexes {
-				session := engine.NewSession()
-				session.Statement.setRefValue(v)
-				defer session.Close()
+				if err := session.statement.setRefValue(v); err != nil {
+					return err
+				}
 				if index.Type == core.UniqueType {
 				if index.Type == core.UniqueType {
-					//isExist, err := session.isIndexExist(table.Name, name, true)
 					isExist, err := session.isIndexExist2(tableName, index.Cols, true)
 					isExist, err := session.isIndexExist2(tableName, index.Cols, true)
 					if err != nil {
 					if err != nil {
 						return err
 						return err
 					}
 					}
 					if !isExist {
 					if !isExist {
-						session := engine.NewSession()
-						session.Statement.setRefValue(v)
-						defer session.Close()
+						if err := session.statement.setRefValue(v); err != nil {
+							return err
+						}
+
 						err = session.addUnique(tableName, name)
 						err = session.addUnique(tableName, name)
 						if err != nil {
 						if err != nil {
 							return err
 							return err
@@ -1227,9 +1296,10 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 						return err
 						return err
 					}
 					}
 					if !isExist {
 					if !isExist {
-						session := engine.NewSession()
-						session.Statement.setRefValue(v)
-						defer session.Close()
+						if err := session.statement.setRefValue(v); err != nil {
+							return err
+						}
+
 						err = session.addIndex(tableName, name)
 						err = session.addIndex(tableName, name)
 						if err != nil {
 						if err != nil {
 							return err
 							return err
@@ -1251,35 +1321,6 @@ func (engine *Engine) Sync2(beans ...interface{}) error {
 	return s.Sync2(beans...)
 	return s.Sync2(beans...)
 }
 }
 
 
-func (engine *Engine) unMap(beans ...interface{}) (e error) {
-	engine.mutex.Lock()
-	defer engine.mutex.Unlock()
-	for _, bean := range beans {
-		t := rType(bean)
-		if _, ok := engine.Tables[t]; ok {
-			delete(engine.Tables, t)
-		}
-	}
-	return
-}
-
-// Drop all mapped table
-func (engine *Engine) dropAll() error {
-	session := engine.NewSession()
-	defer session.Close()
-
-	err := session.Begin()
-	if err != nil {
-		return err
-	}
-	err = session.dropAll()
-	if err != nil {
-		session.Rollback()
-		return err
-	}
-	return session.Commit()
-}
-
 // CreateTables create tabls according bean
 // CreateTables create tabls according bean
 func (engine *Engine) CreateTables(beans ...interface{}) error {
 func (engine *Engine) CreateTables(beans ...interface{}) error {
 	session := engine.NewSession()
 	session := engine.NewSession()
@@ -1291,7 +1332,7 @@ func (engine *Engine) CreateTables(beans ...interface{}) error {
 	}
 	}
 
 
 	for _, bean := range beans {
 	for _, bean := range beans {
-		err = session.CreateTable(bean)
+		err = session.createTable(bean)
 		if err != nil {
 		if err != nil {
 			session.Rollback()
 			session.Rollback()
 			return err
 			return err
@@ -1311,7 +1352,7 @@ func (engine *Engine) DropTables(beans ...interface{}) error {
 	}
 	}
 
 
 	for _, bean := range beans {
 	for _, bean := range beans {
-		err = session.DropTable(bean)
+		err = session.dropTable(bean)
 		if err != nil {
 		if err != nil {
 			session.Rollback()
 			session.Rollback()
 			return err
 			return err
@@ -1320,10 +1361,11 @@ func (engine *Engine) DropTables(beans ...interface{}) error {
 	return session.Commit()
 	return session.Commit()
 }
 }
 
 
-func (engine *Engine) createAll() error {
+// DropIndexes drop indexes of a table
+func (engine *Engine) DropIndexes(bean interface{}) error {
 	session := engine.NewSession()
 	session := engine.NewSession()
 	defer session.Close()
 	defer session.Close()
-	return session.createAll()
+	return session.DropIndexes(bean)
 }
 }
 
 
 // Exec raw sql
 // Exec raw sql
@@ -1334,10 +1376,24 @@ func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error)
 }
 }
 
 
 // Query a raw sql and return records as []map[string][]byte
 // Query a raw sql and return records as []map[string][]byte
-func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
+func (engine *Engine) Query(sqlorArgs ...interface{}) (resultsSlice []map[string][]byte, err error) {
+	session := engine.NewSession()
+	defer session.Close()
+	return session.Query(sqlorArgs...)
+}
+
+// QueryString runs a raw sql and return records as []map[string]string
+func (engine *Engine) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) {
 	session := engine.NewSession()
 	session := engine.NewSession()
 	defer session.Close()
 	defer session.Close()
-	return session.Query(sql, paramStr...)
+	return session.QueryString(sqlorArgs...)
+}
+
+// QueryInterface runs a raw sql and return records as []map[string]interface{}
+func (engine *Engine) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) {
+	session := engine.NewSession()
+	defer session.Close()
+	return session.QueryInterface(sqlorArgs...)
 }
 }
 
 
 // Insert one or more records
 // Insert one or more records
@@ -1381,6 +1437,13 @@ func (engine *Engine) Get(bean interface{}) (bool, error) {
 	return session.Get(bean)
 	return session.Get(bean)
 }
 }
 
 
+// Exist returns true if the record exist otherwise return false
+func (engine *Engine) Exist(bean ...interface{}) (bool, error) {
+	session := engine.NewSession()
+	defer session.Close()
+	return session.Exist(bean...)
+}
+
 // Find retrieve records from table, condiBeans's non-empty fields
 // Find retrieve records from table, condiBeans's non-empty fields
 // are conditions. beans could be []Struct, []*Struct, map[int64]Struct
 // are conditions. beans could be []Struct, []*Struct, map[int64]Struct
 // map[int64]*Struct
 // map[int64]*Struct
@@ -1406,10 +1469,10 @@ func (engine *Engine) Rows(bean interface{}) (*Rows, error) {
 }
 }
 
 
 // Count counts the records. bean's non-empty fields are conditions.
 // Count counts the records. bean's non-empty fields are conditions.
-func (engine *Engine) Count(bean interface{}) (int64, error) {
+func (engine *Engine) Count(bean ...interface{}) (int64, error) {
 	session := engine.NewSession()
 	session := engine.NewSession()
 	defer session.Close()
 	defer session.Close()
-	return session.Count(bean)
+	return session.Count(bean...)
 }
 }
 
 
 // Sum sum the records by some column. bean's non-empty fields are conditions.
 // Sum sum the records by some column. bean's non-empty fields are conditions.
@@ -1419,6 +1482,13 @@ func (engine *Engine) Sum(bean interface{}, colName string) (float64, error) {
 	return session.Sum(bean, colName)
 	return session.Sum(bean, colName)
 }
 }
 
 
+// SumInt sum the records by some column. bean's non-empty fields are conditions.
+func (engine *Engine) SumInt(bean interface{}, colName string) (int64, error) {
+	session := engine.NewSession()
+	defer session.Close()
+	return session.SumInt(bean, colName)
+}
+
 // Sums sum the records by some columns. bean's non-empty fields are conditions.
 // Sums sum the records by some columns. bean's non-empty fields are conditions.
 func (engine *Engine) Sums(bean interface{}, colNames ...string) ([]float64, error) {
 func (engine *Engine) Sums(bean interface{}, colNames ...string) ([]float64, error) {
 	session := engine.NewSession()
 	session := engine.NewSession()
@@ -1474,7 +1544,6 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
 			results = append(results, result)
 			results = append(results, result)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
-				//lastError = err
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1482,49 +1551,32 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
 	return results, lastError
 	return results, lastError
 }
 }
 
 
-// TZTime change one time to xorm time location
-func (engine *Engine) TZTime(t time.Time) time.Time {
-	if !t.IsZero() { // if time is not initialized it's not suitable for Time.In()
-		return t.In(engine.TZLocation)
-	}
-	return t
-}
-
-// NowTime return current time
-func (engine *Engine) NowTime(sqlTypeName string) interface{} {
-	t := time.Now()
-	return engine.FormatTime(sqlTypeName, t)
-}
-
-// NowTime2 return current time
-func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) {
+// nowTime return current time
+func (engine *Engine) nowTime(col *core.Column) (interface{}, time.Time) {
 	t := time.Now()
 	t := time.Now()
-	return engine.FormatTime(sqlTypeName, t), t
-}
-
-// FormatTime format time
-func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) {
-	return engine.formatTime(engine.TZLocation, sqlTypeName, t)
+	var tz = engine.DatabaseTZ
+	if !col.DisableTimeZone && col.TimeZone != nil {
+		tz = col.TimeZone
+	}
+	return engine.formatTime(col.SQLType.Name, t.In(tz)), t.In(engine.TZLocation)
 }
 }
 
 
 func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
 func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
-	if col.DisableTimeZone {
-		return engine.formatTime(nil, col.SQLType.Name, t)
-	} else if col.TimeZone != nil {
-		return engine.formatTime(col.TimeZone, col.SQLType.Name, t)
+	if t.IsZero() {
+		if col.Nullable {
+			return nil
+		}
+		return ""
 	}
 	}
-	return engine.formatTime(engine.TZLocation, col.SQLType.Name, t)
-}
 
 
-func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.Time) (v interface{}) {
-	if engine.dialect.DBType() == core.ORACLE {
-		return t
-	}
-	if tz != nil {
-		t = t.In(tz)
-	} else {
-		t = engine.TZTime(t)
+	if col.TimeZone != nil {
+		return engine.formatTime(col.SQLType.Name, t.In(col.TimeZone))
 	}
 	}
+	return engine.formatTime(col.SQLType.Name, t.In(engine.DatabaseTZ))
+}
+
+// formatTime format time as column type
+func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{}) {
 	switch sqlTypeName {
 	switch sqlTypeName {
 	case core.Time:
 	case core.Time:
 		s := t.Format("2006-01-02 15:04:05") //time.RFC3339
 		s := t.Format("2006-01-02 15:04:05") //time.RFC3339
@@ -1532,18 +1584,10 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T
 	case core.Date:
 	case core.Date:
 		v = t.Format("2006-01-02")
 		v = t.Format("2006-01-02")
 	case core.DateTime, core.TimeStamp:
 	case core.DateTime, core.TimeStamp:
-		if engine.dialect.DBType() == "ql" {
-			v = t
-		} else if engine.dialect.DBType() == "sqlite3" {
-			v = t.UTC().Format("2006-01-02 15:04:05")
-		} else {
-			v = t.Format("2006-01-02 15:04:05")
-		}
+		v = t.Format("2006-01-02 15:04:05")
 	case core.TimeStampz:
 	case core.TimeStampz:
 		if engine.dialect.DBType() == core.MSSQL {
 		if engine.dialect.DBType() == core.MSSQL {
 			v = t.Format("2006-01-02T15:04:05.9999999Z07:00")
 			v = t.Format("2006-01-02T15:04:05.9999999Z07:00")
-		} else if engine.DriverName() == "mssql" {
-			v = t
 		} else {
 		} else {
 			v = t.Format(time.RFC3339Nano)
 			v = t.Format(time.RFC3339Nano)
 		}
 		}
@@ -1555,9 +1599,39 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T
 	return
 	return
 }
 }
 
 
+// GetColumnMapper returns the column name mapper
+func (engine *Engine) GetColumnMapper() core.IMapper {
+	return engine.ColumnMapper
+}
+
+// GetTableMapper returns the table name mapper
+func (engine *Engine) GetTableMapper() core.IMapper {
+	return engine.TableMapper
+}
+
+// GetTZLocation returns time zone of the application
+func (engine *Engine) GetTZLocation() *time.Location {
+	return engine.TZLocation
+}
+
+// SetTZLocation sets time zone of the application
+func (engine *Engine) SetTZLocation(tz *time.Location) {
+	engine.TZLocation = tz
+}
+
+// GetTZDatabase returns time zone of the database
+func (engine *Engine) GetTZDatabase() *time.Location {
+	return engine.DatabaseTZ
+}
+
+// SetTZDatabase sets time zone of the database
+func (engine *Engine) SetTZDatabase(tz *time.Location) {
+	engine.DatabaseTZ = tz
+}
+
 // Unscoped always disable struct tag "deleted"
 // Unscoped always disable struct tag "deleted"
 func (engine *Engine) Unscoped() *Session {
 func (engine *Engine) Unscoped() *Session {
 	session := engine.NewSession()
 	session := engine.NewSession()
-	session.IsAutoClose = true
+	session.isAutoClose = true
 	return session.Unscoped()
 	return session.Unscoped()
 }
 }

+ 230 - 0
vendor/github.com/go-xorm/xorm/engine_cond.go

@@ -0,0 +1,230 @@
+// 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.
+
+package xorm
+
+import (
+	"database/sql/driver"
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"time"
+
+	"github.com/go-xorm/builder"
+	"github.com/go-xorm/core"
+)
+
+func (engine *Engine) buildConds(table *core.Table, bean interface{},
+	includeVersion bool, includeUpdated bool, includeNil bool,
+	includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
+	mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) {
+	var conds []builder.Cond
+	for _, col := range table.Columns() {
+		if !includeVersion && col.IsVersion {
+			continue
+		}
+		if !includeUpdated && col.IsUpdated {
+			continue
+		}
+		if !includeAutoIncr && col.IsAutoIncrement {
+			continue
+		}
+
+		if engine.dialect.DBType() == core.MSSQL && (col.SQLType.Name == core.Text || col.SQLType.IsBlob() || col.SQLType.Name == core.TimeStampz) {
+			continue
+		}
+		if col.SQLType.IsJson() {
+			continue
+		}
+
+		var colName string
+		if addedTableName {
+			var nm = tableName
+			if len(aliasName) > 0 {
+				nm = aliasName
+			}
+			colName = engine.Quote(nm) + "." + engine.Quote(col.Name)
+		} else {
+			colName = engine.Quote(col.Name)
+		}
+
+		fieldValuePtr, err := col.ValueOf(bean)
+		if err != nil {
+			engine.logger.Error(err)
+			continue
+		}
+
+		if col.IsDeleted && !unscoped { // tag "deleted" is enabled
+			conds = append(conds, engine.CondDeleted(colName))
+		}
+
+		fieldValue := *fieldValuePtr
+		if fieldValue.Interface() == nil {
+			continue
+		}
+
+		fieldType := reflect.TypeOf(fieldValue.Interface())
+		requiredField := useAllCols
+
+		if b, ok := getFlagForColumn(mustColumnMap, col); ok {
+			if b {
+				requiredField = true
+			} else {
+				continue
+			}
+		}
+
+		if fieldType.Kind() == reflect.Ptr {
+			if fieldValue.IsNil() {
+				if includeNil {
+					conds = append(conds, builder.Eq{colName: nil})
+				}
+				continue
+			} else if !fieldValue.IsValid() {
+				continue
+			} else {
+				// dereference ptr type to instance type
+				fieldValue = fieldValue.Elem()
+				fieldType = reflect.TypeOf(fieldValue.Interface())
+				requiredField = true
+			}
+		}
+
+		var val interface{}
+		switch fieldType.Kind() {
+		case reflect.Bool:
+			if allUseBool || requiredField {
+				val = fieldValue.Interface()
+			} else {
+				// if a bool in a struct, it will not be as a condition because it default is false,
+				// please use Where() instead
+				continue
+			}
+		case reflect.String:
+			if !requiredField && fieldValue.String() == "" {
+				continue
+			}
+			// for MyString, should convert to string or panic
+			if fieldType.String() != reflect.String.String() {
+				val = fieldValue.String()
+			} else {
+				val = fieldValue.Interface()
+			}
+		case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
+			if !requiredField && fieldValue.Int() == 0 {
+				continue
+			}
+			val = fieldValue.Interface()
+		case reflect.Float32, reflect.Float64:
+			if !requiredField && fieldValue.Float() == 0.0 {
+				continue
+			}
+			val = fieldValue.Interface()
+		case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
+			if !requiredField && fieldValue.Uint() == 0 {
+				continue
+			}
+			t := int64(fieldValue.Uint())
+			val = reflect.ValueOf(&t).Interface()
+		case reflect.Struct:
+			if fieldType.ConvertibleTo(core.TimeType) {
+				t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+				if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
+					continue
+				}
+				val = engine.formatColTime(col, t)
+			} else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok {
+				continue
+			} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
+				val, _ = valNul.Value()
+				if val == nil {
+					continue
+				}
+			} else {
+				if col.SQLType.IsJson() {
+					if col.SQLType.IsText() {
+						bytes, err := json.Marshal(fieldValue.Interface())
+						if err != nil {
+							engine.logger.Error(err)
+							continue
+						}
+						val = string(bytes)
+					} else if col.SQLType.IsBlob() {
+						var bytes []byte
+						var err error
+						bytes, err = json.Marshal(fieldValue.Interface())
+						if err != nil {
+							engine.logger.Error(err)
+							continue
+						}
+						val = bytes
+					}
+				} else {
+					engine.autoMapType(fieldValue)
+					if table, ok := engine.Tables[fieldValue.Type()]; ok {
+						if len(table.PrimaryKeys) == 1 {
+							pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
+							// fix non-int pk issues
+							//if pkField.Int() != 0 {
+							if pkField.IsValid() && !isZero(pkField.Interface()) {
+								val = pkField.Interface()
+							} else {
+								continue
+							}
+						} else {
+							//TODO: how to handler?
+							return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys)
+						}
+					} else {
+						val = fieldValue.Interface()
+					}
+				}
+			}
+		case reflect.Array:
+			continue
+		case reflect.Slice, reflect.Map:
+			if fieldValue == reflect.Zero(fieldType) {
+				continue
+			}
+			if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
+				continue
+			}
+
+			if col.SQLType.IsText() {
+				bytes, err := json.Marshal(fieldValue.Interface())
+				if err != nil {
+					engine.logger.Error(err)
+					continue
+				}
+				val = string(bytes)
+			} else if col.SQLType.IsBlob() {
+				var bytes []byte
+				var err error
+				if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
+					fieldType.Elem().Kind() == reflect.Uint8 {
+					if fieldValue.Len() > 0 {
+						val = fieldValue.Bytes()
+					} else {
+						continue
+					}
+				} else {
+					bytes, err = json.Marshal(fieldValue.Interface())
+					if err != nil {
+						engine.logger.Error(err)
+						continue
+					}
+					val = bytes
+				}
+			} else {
+				continue
+			}
+		default:
+			val = fieldValue.Interface()
+		}
+
+		conds = append(conds, builder.Eq{colName: val})
+	}
+
+	return builder.And(conds...), nil
+}

+ 194 - 0
vendor/github.com/go-xorm/xorm/engine_group.go

@@ -0,0 +1,194 @@
+// 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.
+
+package xorm
+
+import (
+	"github.com/go-xorm/core"
+)
+
+// EngineGroup defines an engine group
+type EngineGroup struct {
+	*Engine
+	slaves []*Engine
+	policy GroupPolicy
+}
+
+// NewEngineGroup creates a new engine group
+func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) {
+	var eg EngineGroup
+	if len(policies) > 0 {
+		eg.policy = policies[0]
+	} else {
+		eg.policy = RoundRobinPolicy()
+	}
+
+	driverName, ok1 := args1.(string)
+	conns, ok2 := args2.([]string)
+	if ok1 && ok2 {
+		engines := make([]*Engine, len(conns))
+		for i, conn := range conns {
+			engine, err := NewEngine(driverName, conn)
+			if err != nil {
+				return nil, err
+			}
+			engine.engineGroup = &eg
+			engines[i] = engine
+		}
+
+		eg.Engine = engines[0]
+		eg.slaves = engines[1:]
+		return &eg, nil
+	}
+
+	master, ok3 := args1.(*Engine)
+	slaves, ok4 := args2.([]*Engine)
+	if ok3 && ok4 {
+		master.engineGroup = &eg
+		for i := 0; i < len(slaves); i++ {
+			slaves[i].engineGroup = &eg
+		}
+		eg.Engine = master
+		eg.slaves = slaves
+		return &eg, nil
+	}
+	return nil, ErrParamsType
+}
+
+// Close the engine
+func (eg *EngineGroup) Close() error {
+	err := eg.Engine.Close()
+	if err != nil {
+		return err
+	}
+
+	for i := 0; i < len(eg.slaves); i++ {
+		err := eg.slaves[i].Close()
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Master returns the master engine
+func (eg *EngineGroup) Master() *Engine {
+	return eg.Engine
+}
+
+// Ping tests if database is alive
+func (eg *EngineGroup) Ping() error {
+	if err := eg.Engine.Ping(); err != nil {
+		return err
+	}
+
+	for _, slave := range eg.slaves {
+		if err := slave.Ping(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// SetColumnMapper set the column name mapping rule
+func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) {
+	eg.Engine.ColumnMapper = mapper
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].ColumnMapper = mapper
+	}
+}
+
+// SetDefaultCacher set the default cacher
+func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) {
+	eg.Engine.SetDefaultCacher(cacher)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].SetDefaultCacher(cacher)
+	}
+}
+
+// SetLogger set the new logger
+func (eg *EngineGroup) SetLogger(logger core.ILogger) {
+	eg.Engine.SetLogger(logger)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].SetLogger(logger)
+	}
+}
+
+// SetLogLevel sets the logger level
+func (eg *EngineGroup) SetLogLevel(level core.LogLevel) {
+	eg.Engine.SetLogLevel(level)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].SetLogLevel(level)
+	}
+}
+
+// SetMapper set the name mapping rules
+func (eg *EngineGroup) SetMapper(mapper core.IMapper) {
+	eg.Engine.SetMapper(mapper)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].SetMapper(mapper)
+	}
+}
+
+// SetMaxIdleConns set the max idle connections on pool, default is 2
+func (eg *EngineGroup) SetMaxIdleConns(conns int) {
+	eg.Engine.db.SetMaxIdleConns(conns)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].db.SetMaxIdleConns(conns)
+	}
+}
+
+// SetMaxOpenConns is only available for go 1.2+
+func (eg *EngineGroup) SetMaxOpenConns(conns int) {
+	eg.Engine.db.SetMaxOpenConns(conns)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].db.SetMaxOpenConns(conns)
+	}
+}
+
+// SetPolicy set the group policy
+func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup {
+	eg.policy = policy
+	return eg
+}
+
+// SetTableMapper set the table name mapping rule
+func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) {
+	eg.Engine.TableMapper = mapper
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].TableMapper = mapper
+	}
+}
+
+// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
+func (eg *EngineGroup) ShowExecTime(show ...bool) {
+	eg.Engine.ShowExecTime(show...)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].ShowExecTime(show...)
+	}
+}
+
+// ShowSQL show SQL statement or not on logger if log level is great than INFO
+func (eg *EngineGroup) ShowSQL(show ...bool) {
+	eg.Engine.ShowSQL(show...)
+	for i := 0; i < len(eg.slaves); i++ {
+		eg.slaves[i].ShowSQL(show...)
+	}
+}
+
+// Slave returns one of the physical databases which is a slave according the policy
+func (eg *EngineGroup) Slave() *Engine {
+	switch len(eg.slaves) {
+	case 0:
+		return eg.Engine
+	case 1:
+		return eg.slaves[0]
+	}
+	return eg.policy.Slave(eg)
+}
+
+// Slaves returns all the slaves
+func (eg *EngineGroup) Slaves() []*Engine {
+	return eg.slaves
+}

+ 116 - 0
vendor/github.com/go-xorm/xorm/engine_group_policy.go

@@ -0,0 +1,116 @@
+// 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.
+
+package xorm
+
+import (
+	"math/rand"
+	"sync"
+	"time"
+)
+
+// GroupPolicy is be used by chosing the current slave from slaves
+type GroupPolicy interface {
+	Slave(*EngineGroup) *Engine
+}
+
+// GroupPolicyHandler should be used when a function is a GroupPolicy
+type GroupPolicyHandler func(*EngineGroup) *Engine
+
+// Slave implements the chosen of slaves
+func (h GroupPolicyHandler) Slave(eg *EngineGroup) *Engine {
+	return h(eg)
+}
+
+// RandomPolicy implmentes randomly chose the slave of slaves
+func RandomPolicy() GroupPolicyHandler {
+	var r = rand.New(rand.NewSource(time.Now().UnixNano()))
+	return func(g *EngineGroup) *Engine {
+		return g.Slaves()[r.Intn(len(g.Slaves()))]
+	}
+}
+
+// WeightRandomPolicy implmentes randomly chose the slave of slaves
+func WeightRandomPolicy(weights []int) GroupPolicyHandler {
+	var rands = make([]int, 0, len(weights))
+	for i := 0; i < len(weights); i++ {
+		for n := 0; n < weights[i]; n++ {
+			rands = append(rands, i)
+		}
+	}
+	var r = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	return func(g *EngineGroup) *Engine {
+		var slaves = g.Slaves()
+		idx := rands[r.Intn(len(rands))]
+		if idx >= len(slaves) {
+			idx = len(slaves) - 1
+		}
+		return slaves[idx]
+	}
+}
+
+func RoundRobinPolicy() GroupPolicyHandler {
+	var pos = -1
+	var lock sync.Mutex
+	return func(g *EngineGroup) *Engine {
+		var slaves = g.Slaves()
+
+		lock.Lock()
+		defer lock.Unlock()
+		pos++
+		if pos >= len(slaves) {
+			pos = 0
+		}
+
+		return slaves[pos]
+	}
+}
+
+func WeightRoundRobinPolicy(weights []int) GroupPolicyHandler {
+	var rands = make([]int, 0, len(weights))
+	for i := 0; i < len(weights); i++ {
+		for n := 0; n < weights[i]; n++ {
+			rands = append(rands, i)
+		}
+	}
+	var pos = -1
+	var lock sync.Mutex
+
+	return func(g *EngineGroup) *Engine {
+		var slaves = g.Slaves()
+		lock.Lock()
+		defer lock.Unlock()
+		pos++
+		if pos >= len(rands) {
+			pos = 0
+		}
+
+		idx := rands[pos]
+		if idx >= len(slaves) {
+			idx = len(slaves) - 1
+		}
+		return slaves[idx]
+	}
+}
+
+// LeastConnPolicy implements GroupPolicy, every time will get the least connections slave
+func LeastConnPolicy() GroupPolicyHandler {
+	return func(g *EngineGroup) *Engine {
+		var slaves = g.Slaves()
+		connections := 0
+		idx := 0
+		for i := 0; i < len(slaves); i++ {
+			openConnections := slaves[i].DB().Stats().OpenConnections
+			if i == 0 {
+				connections = openConnections
+				idx = i
+			} else if openConnections <= connections {
+				connections = openConnections
+				idx = i
+			}
+		}
+		return slaves[idx]
+	}
+}

+ 22 - 0
vendor/github.com/go-xorm/xorm/engine_maxlife.go

@@ -0,0 +1,22 @@
+// 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)
+	}
+}

+ 2 - 0
vendor/github.com/go-xorm/xorm/error.go

@@ -23,4 +23,6 @@ var (
 	ErrNeedDeletedCond = errors.New("Delete need at least one condition")
 	ErrNeedDeletedCond = errors.New("Delete need at least one condition")
 	// ErrNotImplemented not implemented
 	// ErrNotImplemented not implemented
 	ErrNotImplemented = errors.New("Not implemented")
 	ErrNotImplemented = errors.New("Not implemented")
+	// ErrConditionType condition type unsupported
+	ErrConditionType = errors.New("Unsupported conditon type")
 )
 )

+ 46 - 195
vendor/github.com/go-xorm/xorm/helpers.go

@@ -196,25 +196,43 @@ func isArrayValueZero(v reflect.Value) bool {
 
 
 func int64ToIntValue(id int64, tp reflect.Type) reflect.Value {
 func int64ToIntValue(id int64, tp reflect.Type) reflect.Value {
 	var v interface{}
 	var v interface{}
-	switch tp.Kind() {
+	kind := tp.Kind()
+
+	if kind == reflect.Ptr {
+		kind = tp.Elem().Kind()
+	}
+
+	switch kind {
 	case reflect.Int16:
 	case reflect.Int16:
-		v = int16(id)
+		temp := int16(id)
+		v = &temp
 	case reflect.Int32:
 	case reflect.Int32:
-		v = int32(id)
+		temp := int32(id)
+		v = &temp
 	case reflect.Int:
 	case reflect.Int:
-		v = int(id)
+		temp := int(id)
+		v = &temp
 	case reflect.Int64:
 	case reflect.Int64:
-		v = id
+		temp := id
+		v = &temp
 	case reflect.Uint16:
 	case reflect.Uint16:
-		v = uint16(id)
+		temp := uint16(id)
+		v = &temp
 	case reflect.Uint32:
 	case reflect.Uint32:
-		v = uint32(id)
+		temp := uint32(id)
+		v = &temp
 	case reflect.Uint64:
 	case reflect.Uint64:
-		v = uint64(id)
+		temp := uint64(id)
+		v = &temp
 	case reflect.Uint:
 	case reflect.Uint:
-		v = uint(id)
+		temp := uint(id)
+		v = &temp
+	}
+
+	if tp.Kind() == reflect.Ptr {
+		return reflect.ValueOf(v).Convert(tp)
 	}
 	}
-	return reflect.ValueOf(v).Convert(tp)
+	return reflect.ValueOf(v).Elem().Convert(tp)
 }
 }
 
 
 func int64ToInt(id int64, tp reflect.Type) interface{} {
 func int64ToInt(id int64, tp reflect.Type) interface{} {
@@ -302,180 +320,6 @@ func sliceEq(left, right []string) bool {
 	return true
 	return true
 }
 }
 
 
-func reflect2value(rawValue *reflect.Value) (str string, err error) {
-	aa := reflect.TypeOf((*rawValue).Interface())
-	vv := reflect.ValueOf((*rawValue).Interface())
-	switch aa.Kind() {
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		str = strconv.FormatInt(vv.Int(), 10)
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-		str = strconv.FormatUint(vv.Uint(), 10)
-	case reflect.Float32, reflect.Float64:
-		str = strconv.FormatFloat(vv.Float(), 'f', -1, 64)
-	case reflect.String:
-		str = vv.String()
-	case reflect.Array, reflect.Slice:
-		switch aa.Elem().Kind() {
-		case reflect.Uint8:
-			data := rawValue.Interface().([]byte)
-			str = string(data)
-		default:
-			err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
-		}
-	// time type
-	case reflect.Struct:
-		if aa.ConvertibleTo(core.TimeType) {
-			str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano)
-		} else {
-			err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
-		}
-	case reflect.Bool:
-		str = strconv.FormatBool(vv.Bool())
-	case reflect.Complex128, reflect.Complex64:
-		str = fmt.Sprintf("%v", vv.Complex())
-	/* TODO: unsupported types below
-	   case reflect.Map:
-	   case reflect.Ptr:
-	   case reflect.Uintptr:
-	   case reflect.UnsafePointer:
-	   case reflect.Chan, reflect.Func, reflect.Interface:
-	*/
-	default:
-		err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
-	}
-	return
-}
-
-func value2Bytes(rawValue *reflect.Value) (data []byte, err error) {
-	var str string
-	str, err = reflect2value(rawValue)
-	if err != nil {
-		return
-	}
-	data = []byte(str)
-	return
-}
-
-func value2String(rawValue *reflect.Value) (data string, err error) {
-	data, err = reflect2value(rawValue)
-	if err != nil {
-		return
-	}
-	return
-}
-
-func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) {
-	fields, err := rows.Columns()
-	if err != nil {
-		return nil, err
-	}
-	for rows.Next() {
-		result, err := row2mapStr(rows, fields)
-		if err != nil {
-			return nil, err
-		}
-		resultsSlice = append(resultsSlice, result)
-	}
-
-	return resultsSlice, nil
-}
-
-func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) {
-	fields, err := rows.Columns()
-	if err != nil {
-		return nil, err
-	}
-	for rows.Next() {
-		result, err := row2map(rows, fields)
-		if err != nil {
-			return nil, err
-		}
-		resultsSlice = append(resultsSlice, result)
-	}
-
-	return resultsSlice, nil
-}
-
-func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) {
-	result := make(map[string][]byte)
-	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 ii, key := range fields {
-		rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
-		//if row is null then ignore
-		if rawValue.Interface() == nil {
-			//fmt.Println("ignore ...", key, rawValue)
-			continue
-		}
-
-		if data, err := value2Bytes(&rawValue); err == nil {
-			result[key] = data
-		} else {
-			return nil, err // !nashtsai! REVIEW, should return err or just error log?
-		}
-	}
-	return result, nil
-}
-
-func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) {
-	result := make(map[string]string)
-	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 ii, key := range fields {
-		rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
-		//if row is null then ignore
-		if rawValue.Interface() == nil {
-			//fmt.Println("ignore ...", key, rawValue)
-			continue
-		}
-
-		if data, err := value2String(&rawValue); err == nil {
-			result[key] = data
-		} else {
-			return nil, err // !nashtsai! REVIEW, should return err or just error log?
-		}
-	}
-	return result, nil
-}
-
-func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
-	rows, err := tx.Query(sqlStr, params...)
-	if err != nil {
-		return nil, err
-	}
-	defer rows.Close()
-
-	return rows2Strings(rows)
-}
-
-func query2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
-	s, err := db.Prepare(sqlStr)
-	if err != nil {
-		return nil, err
-	}
-	defer s.Close()
-	rows, err := s.Query(params...)
-	if err != nil {
-		return nil, err
-	}
-	defer rows.Close()
-	return rows2Strings(rows)
-}
-
 func setColumnInt(bean interface{}, col *core.Column, t int64) {
 func setColumnInt(bean interface{}, col *core.Column, t int64) {
 	v, err := col.ValueOf(bean)
 	v, err := col.ValueOf(bean)
 	if err != nil {
 	if err != nil {
@@ -514,7 +358,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
 
 
 	for _, col := range table.Columns() {
 	for _, col := range table.Columns() {
 		if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated {
 		if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated {
-			if _, ok := getFlagForColumn(session.Statement.columnMap, col); !ok {
+			if _, ok := getFlagForColumn(session.statement.columnMap, col); !ok {
 				continue
 				continue
 			}
 			}
 		}
 		}
@@ -542,6 +386,10 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
 				if len(fieldValue.String()) == 0 {
 				if len(fieldValue.String()) == 0 {
 					continue
 					continue
 				}
 				}
+			case reflect.Ptr:
+				if fieldValue.Pointer() == 0 {
+					continue
+				}
 			}
 			}
 		}
 		}
 
 
@@ -549,28 +397,32 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
 			continue
 			continue
 		}
 		}
 
 
-		if session.Statement.ColumnStr != "" {
-			if _, ok := getFlagForColumn(session.Statement.columnMap, col); !ok {
+		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
 				continue
 			}
 			}
 		}
 		}
-		if session.Statement.OmitStr != "" {
-			if _, ok := getFlagForColumn(session.Statement.columnMap, col); ok {
+		if session.statement.OmitStr != "" {
+			if _, ok := getFlagForColumn(session.statement.columnMap, col); ok {
 				continue
 				continue
 			}
 			}
 		}
 		}
 
 
 		// !evalphobia! set fieldValue as nil when column is nullable and zero-value
 		// !evalphobia! set fieldValue as nil when column is nullable and zero-value
-		if _, ok := getFlagForColumn(session.Statement.nullableMap, col); ok {
+		if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok {
 			if col.Nullable && isZero(fieldValue.Interface()) {
 			if col.Nullable && isZero(fieldValue.Interface()) {
 				var nilValue *int
 				var nilValue *int
 				fieldValue = reflect.ValueOf(nilValue)
 				fieldValue = reflect.ValueOf(nilValue)
 			}
 			}
 		}
 		}
 
 
-		if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
+		if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
 			// if time is non-empty, then set to auto time
 			// if time is non-empty, then set to auto time
-			val, t := session.Engine.NowTime2(col.SQLType.Name)
+			val, t := session.engine.nowTime(col)
 			args = append(args, val)
 			args = append(args, val)
 
 
 			var colName = col.Name
 			var colName = col.Name
@@ -578,7 +430,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
 				col := table.GetColumn(colName)
 				col := table.GetColumn(colName)
 				setColumnTime(bean, col, t)
 				setColumnTime(bean, col, t)
 			})
 			})
-		} else if col.IsVersion && session.Statement.checkVersion {
+		} else if col.IsVersion && session.statement.checkVersion {
 			args = append(args, 1)
 			args = append(args, 1)
 		} else {
 		} else {
 			arg, err := session.value2Interface(col, fieldValue)
 			arg, err := session.value2Interface(col, fieldValue)
@@ -589,7 +441,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
 		}
 		}
 
 
 		if includeQuote {
 		if includeQuote {
-			colNames = append(colNames, session.Engine.Quote(col.Name)+" = ?")
+			colNames = append(colNames, session.engine.Quote(col.Name)+" = ?")
 		} else {
 		} else {
 			colNames = append(colNames, col.Name)
 			colNames = append(colNames, col.Name)
 		}
 		}
@@ -602,7 +454,6 @@ func indexName(tableName, idxName string) string {
 }
 }
 
 
 func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) {
 func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) {
-
 	if len(m) == 0 {
 	if len(m) == 0 {
 		return false, false
 		return false, false
 	}
 	}

+ 21 - 0
vendor/github.com/go-xorm/xorm/helpler_time.go

@@ -0,0 +1,21 @@
+// 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.
+
+package xorm
+
+import "time"
+
+const (
+	zeroTime0 = "0000-00-00 00:00:00"
+	zeroTime1 = "0001-01-01 00:00:00"
+)
+
+func formatTime(t time.Time) string {
+	return t.Format("2006-01-02 15:04:05")
+}
+
+func isTimeZero(t time.Time) bool {
+	return t.IsZero() || formatTime(t) == zeroTime0 ||
+		formatTime(t) == zeroTime1
+}

+ 103 - 0
vendor/github.com/go-xorm/xorm/interface.go

@@ -0,0 +1,103 @@
+// 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.
+
+package xorm
+
+import (
+	"database/sql"
+	"reflect"
+	"time"
+
+	"github.com/go-xorm/core"
+)
+
+// Interface defines the interface which Engine, EngineGroup and Session will implementate.
+type Interface interface {
+	AllCols() *Session
+	Alias(alias string) *Session
+	Asc(colNames ...string) *Session
+	BufferSize(size int) *Session
+	Cols(columns ...string) *Session
+	Count(...interface{}) (int64, error)
+	CreateIndexes(bean interface{}) error
+	CreateUniques(bean interface{}) error
+	Decr(column string, arg ...interface{}) *Session
+	Desc(...string) *Session
+	Delete(interface{}) (int64, error)
+	Distinct(columns ...string) *Session
+	DropIndexes(bean interface{}) error
+	Exec(string, ...interface{}) (sql.Result, error)
+	Exist(bean ...interface{}) (bool, error)
+	Find(interface{}, ...interface{}) error
+	Get(interface{}) (bool, error)
+	GroupBy(keys string) *Session
+	ID(interface{}) *Session
+	In(string, ...interface{}) *Session
+	Incr(column string, arg ...interface{}) *Session
+	Insert(...interface{}) (int64, error)
+	InsertOne(interface{}) (int64, error)
+	IsTableEmpty(bean interface{}) (bool, error)
+	IsTableExist(beanOrTableName interface{}) (bool, error)
+	Iterate(interface{}, IterFunc) error
+	Limit(int, ...int) *Session
+	NoAutoCondition(...bool) *Session
+	NotIn(string, ...interface{}) *Session
+	Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session
+	Omit(columns ...string) *Session
+	OrderBy(order string) *Session
+	Ping() error
+	Query(sqlOrAgrs ...interface{}) (resultsSlice []map[string][]byte, err error)
+	QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error)
+	QueryString(sqlorArgs ...interface{}) ([]map[string]string, error)
+	Rows(bean interface{}) (*Rows, error)
+	SetExpr(string, string) *Session
+	SQL(interface{}, ...interface{}) *Session
+	Sum(bean interface{}, colName string) (float64, error)
+	SumInt(bean interface{}, colName string) (int64, error)
+	Sums(bean interface{}, colNames ...string) ([]float64, error)
+	SumsInt(bean interface{}, colNames ...string) ([]int64, error)
+	Table(tableNameOrBean interface{}) *Session
+	Unscoped() *Session
+	Update(bean interface{}, condiBeans ...interface{}) (int64, error)
+	UseBool(...string) *Session
+	Where(interface{}, ...interface{}) *Session
+}
+
+// EngineInterface defines the interface which Engine, EngineGroup will implementate.
+type EngineInterface interface {
+	Interface
+
+	Before(func(interface{})) *Session
+	Charset(charset string) *Session
+	CreateTables(...interface{}) error
+	DBMetas() ([]*core.Table, error)
+	Dialect() core.Dialect
+	DropTables(...interface{}) error
+	DumpAllToFile(fp string, tp ...core.DbType) error
+	GetColumnMapper() core.IMapper
+	GetDefaultCacher() core.Cacher
+	GetTableMapper() core.IMapper
+	GetTZDatabase() *time.Location
+	GetTZLocation() *time.Location
+	NewSession() *Session
+	NoAutoTime() *Session
+	Quote(string) string
+	SetDefaultCacher(core.Cacher)
+	SetLogLevel(core.LogLevel)
+	SetMapper(core.IMapper)
+	SetTZDatabase(tz *time.Location)
+	SetTZLocation(tz *time.Location)
+	ShowSQL(show ...bool)
+	Sync(...interface{}) error
+	Sync2(...interface{}) error
+	StoreEngine(storeEngine string) *Session
+	TableInfo(bean interface{}) *Table
+	UnMapType(reflect.Type)
+}
+
+var (
+	_ Interface       = &Session{}
+	_ EngineInterface = &Engine{}
+	_ EngineInterface = &EngineGroup{}
+)

+ 33 - 7
vendor/github.com/go-xorm/xorm/processors.go

@@ -29,13 +29,6 @@ type AfterSetProcessor interface {
 	AfterSet(string, Cell)
 	AfterSet(string, Cell)
 }
 }
 
 
-// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
-//// Executed before an object is validated
-//type BeforeValidateProcessor interface {
-//    BeforeValidate()
-//}
-// --
-
 // AfterInsertProcessor executed after an object is persisted to the database
 // AfterInsertProcessor executed after an object is persisted to the database
 type AfterInsertProcessor interface {
 type AfterInsertProcessor interface {
 	AfterInsert()
 	AfterInsert()
@@ -50,3 +43,36 @@ type AfterUpdateProcessor interface {
 type AfterDeleteProcessor interface {
 type AfterDeleteProcessor interface {
 	AfterDelete()
 	AfterDelete()
 }
 }
+
+// AfterLoadProcessor executed after an ojbect has been loaded from database
+type AfterLoadProcessor interface {
+	AfterLoad()
+}
+
+// AfterLoadSessionProcessor executed after an ojbect has been loaded from database with session parameter
+type AfterLoadSessionProcessor interface {
+	AfterLoad(*Session)
+}
+
+type executedProcessorFunc func(*Session, interface{}) error
+
+type executedProcessor struct {
+	fun     executedProcessorFunc
+	session *Session
+	bean    interface{}
+}
+
+func (executor *executedProcessor) execute() error {
+	return executor.fun(executor.session, executor.bean)
+}
+
+func (session *Session) executeProcessors() error {
+	processors := session.afterProcessors
+	session.afterProcessors = make([]executedProcessor, 0)
+	for _, processor := range processors {
+		if err := processor.execute(); err != nil {
+			return err
+		}
+	}
+	return nil
+}

+ 31 - 45
vendor/github.com/go-xorm/xorm/rows.go

@@ -17,7 +17,6 @@ type Rows struct {
 	NoTypeCheck bool
 	NoTypeCheck bool
 
 
 	session   *Session
 	session   *Session
-	stmt      *core.Stmt
 	rows      *core.Rows
 	rows      *core.Rows
 	fields    []string
 	fields    []string
 	beanType  reflect.Type
 	beanType  reflect.Type
@@ -29,50 +28,33 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
 	rows.session = session
 	rows.session = session
 	rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type()
 	rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type()
 
 
-	defer rows.session.resetStatement()
-
 	var sqlStr string
 	var sqlStr string
 	var args []interface{}
 	var args []interface{}
+	var err error
 
 
-	rows.session.Statement.setRefValue(rValue(bean))
-	if len(session.Statement.TableName()) <= 0 {
-		return nil, ErrTableNotFound
-	}
-
-	if rows.session.Statement.RawSQL == "" {
-		sqlStr, args = rows.session.Statement.genGetSQL(bean)
-	} else {
-		sqlStr = rows.session.Statement.RawSQL
-		args = rows.session.Statement.RawParams
+	if err = rows.session.statement.setRefValue(rValue(bean)); err != nil {
+		return nil, err
 	}
 	}
 
 
-	for _, filter := range rows.session.Engine.dialect.Filters() {
-		sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable)
+	if len(session.statement.TableName()) <= 0 {
+		return nil, ErrTableNotFound
 	}
 	}
 
 
-	rows.session.saveLastSQL(sqlStr, args...)
-	var err error
-	if rows.session.prepareStmt {
-		rows.stmt, err = rows.session.DB().Prepare(sqlStr)
-		if err != nil {
-			rows.lastError = err
-			rows.Close()
-			return nil, err
-		}
-
-		rows.rows, err = rows.stmt.Query(args...)
+	if rows.session.statement.RawSQL == "" {
+		sqlStr, args, err = rows.session.statement.genGetSQL(bean)
 		if err != nil {
 		if err != nil {
-			rows.lastError = err
-			rows.Close()
 			return nil, err
 			return nil, err
 		}
 		}
 	} else {
 	} else {
-		rows.rows, err = rows.session.DB().Query(sqlStr, args...)
-		if err != nil {
-			rows.lastError = err
-			rows.Close()
-			return nil, err
-		}
+		sqlStr = rows.session.statement.RawSQL
+		args = rows.session.statement.RawParams
+	}
+
+	rows.rows, err = rows.session.queryRows(sqlStr, args...)
+	if err != nil {
+		rows.lastError = err
+		rows.Close()
+		return nil, err
 	}
 	}
 
 
 	rows.fields, err = rows.rows.Columns()
 	rows.fields, err = rows.rows.Columns()
@@ -113,15 +95,26 @@ func (rows *Rows) Scan(bean interface{}) error {
 	}
 	}
 
 
 	dataStruct := rValue(bean)
 	dataStruct := rValue(bean)
-	rows.session.Statement.setRefValue(dataStruct)
-	_, err := rows.session.row2Bean(rows.rows, rows.fields, len(rows.fields), bean, &dataStruct, rows.session.Statement.RefTable)
+	if err := rows.session.statement.setRefValue(dataStruct); err != nil {
+		return err
+	}
 
 
-	return err
+	scanResults, err := rows.session.row2Slice(rows.rows, rows.fields, bean)
+	if err != nil {
+		return err
+	}
+
+	_, err = rows.session.slice2Bean(scanResults, rows.fields, bean, &dataStruct, rows.session.statement.RefTable)
+	if err != nil {
+		return err
+	}
+
+	return rows.session.executeProcessors()
 }
 }
 
 
 // Close session if session.IsAutoClose is true, and claimed any opened resources
 // Close session if session.IsAutoClose is true, and claimed any opened resources
 func (rows *Rows) Close() error {
 func (rows *Rows) Close() error {
-	if rows.session.IsAutoClose {
+	if rows.session.isAutoClose {
 		defer rows.session.Close()
 		defer rows.session.Close()
 	}
 	}
 
 
@@ -129,17 +122,10 @@ func (rows *Rows) Close() error {
 		if rows.rows != nil {
 		if rows.rows != nil {
 			rows.lastError = rows.rows.Close()
 			rows.lastError = rows.rows.Close()
 			if rows.lastError != nil {
 			if rows.lastError != nil {
-				defer rows.stmt.Close()
 				return rows.lastError
 				return rows.lastError
 			}
 			}
 		}
 		}
-		if rows.stmt != nil {
-			rows.lastError = rows.stmt.Close()
-		}
 	} else {
 	} else {
-		if rows.stmt != nil {
-			defer rows.stmt.Close()
-		}
 		if rows.rows != nil {
 		if rows.rows != nil {
 			defer rows.rows.Close()
 			defer rows.rows.Close()
 		}
 		}

+ 187 - 184
vendor/github.com/go-xorm/xorm/session.go

@@ -11,7 +11,6 @@ import (
 	"fmt"
 	"fmt"
 	"hash/crc32"
 	"hash/crc32"
 	"reflect"
 	"reflect"
-	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -22,17 +21,16 @@ import (
 // kind of database operations.
 // kind of database operations.
 type Session struct {
 type Session struct {
 	db                     *core.DB
 	db                     *core.DB
-	Engine                 *Engine
-	Tx                     *core.Tx
-	Statement              Statement
-	IsAutoCommit           bool
-	IsCommitedOrRollbacked bool
-	TransType              string
-	IsAutoClose            bool
+	engine                 *Engine
+	tx                     *core.Tx
+	statement              Statement
+	isAutoCommit           bool
+	isCommitedOrRollbacked bool
+	isAutoClose            bool
 
 
 	// Automatically reset the statement after operations that execute a SQL
 	// Automatically reset the statement after operations that execute a SQL
 	// query such as Count(), Find(), Get(), ...
 	// query such as Count(), Find(), Get(), ...
-	AutoResetStatement bool
+	autoResetStatement bool
 
 
 	// !nashtsai! storing these beans due to yet committed tx
 	// !nashtsai! storing these beans due to yet committed tx
 	afterInsertBeans map[interface{}]*[]func(interface{})
 	afterInsertBeans map[interface{}]*[]func(interface{})
@@ -43,14 +41,17 @@ type Session struct {
 	beforeClosures []func(interface{})
 	beforeClosures []func(interface{})
 	afterClosures  []func(interface{})
 	afterClosures  []func(interface{})
 
 
+	afterProcessors []executedProcessor
+
 	prepareStmt bool
 	prepareStmt bool
 	stmtCache   map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
 	stmtCache   map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
-	cascadeDeep int
 
 
 	// !evalphobia! stored the last executed query on this session
 	// !evalphobia! stored the last executed query on this session
 	//beforeSQLExec func(string, ...interface{})
 	//beforeSQLExec func(string, ...interface{})
 	lastSQL     string
 	lastSQL     string
 	lastSQLArgs []interface{}
 	lastSQLArgs []interface{}
+
+	err error
 }
 }
 
 
 // Clone copy all the session's content and return a new session
 // Clone copy all the session's content and return a new session
@@ -61,12 +62,12 @@ func (session *Session) Clone() *Session {
 
 
 // Init reset the session as the init status.
 // Init reset the session as the init status.
 func (session *Session) Init() {
 func (session *Session) Init() {
-	session.Statement.Init()
-	session.Statement.Engine = session.Engine
-	session.IsAutoCommit = true
-	session.IsCommitedOrRollbacked = false
-	session.IsAutoClose = false
-	session.AutoResetStatement = true
+	session.statement.Init()
+	session.statement.Engine = session.engine
+	session.isAutoCommit = true
+	session.isCommitedOrRollbacked = false
+	session.isAutoClose = false
+	session.autoResetStatement = true
 	session.prepareStmt = false
 	session.prepareStmt = false
 
 
 	// !nashtsai! is lazy init better?
 	// !nashtsai! is lazy init better?
@@ -75,6 +76,9 @@ func (session *Session) Init() {
 	session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0)
 	session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0)
 	session.beforeClosures = make([]func(interface{}), 0)
 	session.beforeClosures = make([]func(interface{}), 0)
 	session.afterClosures = make([]func(interface{}), 0)
 	session.afterClosures = make([]func(interface{}), 0)
+	session.stmtCache = make(map[uint32]*core.Stmt)
+
+	session.afterProcessors = make([]executedProcessor, 0)
 
 
 	session.lastSQL = ""
 	session.lastSQL = ""
 	session.lastSQLArgs = []interface{}{}
 	session.lastSQLArgs = []interface{}{}
@@ -89,19 +93,23 @@ func (session *Session) Close() {
 	if session.db != nil {
 	if session.db != nil {
 		// When Close be called, if session is a transaction and do not call
 		// When Close be called, if session is a transaction and do not call
 		// Commit or Rollback, then call Rollback.
 		// Commit or Rollback, then call Rollback.
-		if session.Tx != nil && !session.IsCommitedOrRollbacked {
+		if session.tx != nil && !session.isCommitedOrRollbacked {
 			session.Rollback()
 			session.Rollback()
 		}
 		}
-		session.Tx = nil
+		session.tx = nil
 		session.stmtCache = nil
 		session.stmtCache = nil
-		session.Init()
 		session.db = nil
 		session.db = nil
 	}
 	}
 }
 }
 
 
+// IsClosed returns if session is closed
+func (session *Session) IsClosed() bool {
+	return session.db == nil
+}
+
 func (session *Session) resetStatement() {
 func (session *Session) resetStatement() {
-	if session.AutoResetStatement {
-		session.Statement.Init()
+	if session.autoResetStatement {
+		session.statement.Init()
 	}
 	}
 }
 }
 
 
@@ -129,75 +137,75 @@ func (session *Session) After(closures func(interface{})) *Session {
 
 
 // Table can input a string or pointer to struct for special a table to operate.
 // Table can input a string or pointer to struct for special a table to operate.
 func (session *Session) Table(tableNameOrBean interface{}) *Session {
 func (session *Session) Table(tableNameOrBean interface{}) *Session {
-	session.Statement.Table(tableNameOrBean)
+	session.statement.Table(tableNameOrBean)
 	return session
 	return session
 }
 }
 
 
 // Alias set the table alias
 // Alias set the table alias
 func (session *Session) Alias(alias string) *Session {
 func (session *Session) Alias(alias string) *Session {
-	session.Statement.Alias(alias)
+	session.statement.Alias(alias)
 	return session
 	return session
 }
 }
 
 
 // NoCascade indicate that no cascade load child object
 // NoCascade indicate that no cascade load child object
 func (session *Session) NoCascade() *Session {
 func (session *Session) NoCascade() *Session {
-	session.Statement.UseCascade = false
+	session.statement.UseCascade = false
 	return session
 	return session
 }
 }
 
 
 // ForUpdate Set Read/Write locking for UPDATE
 // ForUpdate Set Read/Write locking for UPDATE
 func (session *Session) ForUpdate() *Session {
 func (session *Session) ForUpdate() *Session {
-	session.Statement.IsForUpdate = true
+	session.statement.IsForUpdate = true
 	return session
 	return session
 }
 }
 
 
 // NoAutoCondition disable generate SQL condition from beans
 // NoAutoCondition disable generate SQL condition from beans
 func (session *Session) NoAutoCondition(no ...bool) *Session {
 func (session *Session) NoAutoCondition(no ...bool) *Session {
-	session.Statement.NoAutoCondition(no...)
+	session.statement.NoAutoCondition(no...)
 	return session
 	return session
 }
 }
 
 
 // Limit provide limit and offset query condition
 // Limit provide limit and offset query condition
 func (session *Session) Limit(limit int, start ...int) *Session {
 func (session *Session) Limit(limit int, start ...int) *Session {
-	session.Statement.Limit(limit, start...)
+	session.statement.Limit(limit, start...)
 	return session
 	return session
 }
 }
 
 
 // OrderBy provide order by query condition, the input parameter is the content
 // OrderBy provide order by query condition, the input parameter is the content
 // after order by on a sql statement.
 // after order by on a sql statement.
 func (session *Session) OrderBy(order string) *Session {
 func (session *Session) OrderBy(order string) *Session {
-	session.Statement.OrderBy(order)
+	session.statement.OrderBy(order)
 	return session
 	return session
 }
 }
 
 
 // Desc provide desc order by query condition, the input parameters are columns.
 // Desc provide desc order by query condition, the input parameters are columns.
 func (session *Session) Desc(colNames ...string) *Session {
 func (session *Session) Desc(colNames ...string) *Session {
-	session.Statement.Desc(colNames...)
+	session.statement.Desc(colNames...)
 	return session
 	return session
 }
 }
 
 
 // Asc provide asc order by query condition, the input parameters are columns.
 // Asc provide asc order by query condition, the input parameters are columns.
 func (session *Session) Asc(colNames ...string) *Session {
 func (session *Session) Asc(colNames ...string) *Session {
-	session.Statement.Asc(colNames...)
+	session.statement.Asc(colNames...)
 	return session
 	return session
 }
 }
 
 
 // StoreEngine is only avialble mysql dialect currently
 // StoreEngine is only avialble mysql dialect currently
 func (session *Session) StoreEngine(storeEngine string) *Session {
 func (session *Session) StoreEngine(storeEngine string) *Session {
-	session.Statement.StoreEngine = storeEngine
+	session.statement.StoreEngine = storeEngine
 	return session
 	return session
 }
 }
 
 
 // Charset is only avialble mysql dialect currently
 // Charset is only avialble mysql dialect currently
 func (session *Session) Charset(charset string) *Session {
 func (session *Session) Charset(charset string) *Session {
-	session.Statement.Charset = charset
+	session.statement.Charset = charset
 	return session
 	return session
 }
 }
 
 
 // Cascade indicates if loading sub Struct
 // Cascade indicates if loading sub Struct
 func (session *Session) Cascade(trueOrFalse ...bool) *Session {
 func (session *Session) Cascade(trueOrFalse ...bool) *Session {
 	if len(trueOrFalse) >= 1 {
 	if len(trueOrFalse) >= 1 {
-		session.Statement.UseCascade = trueOrFalse[0]
+		session.statement.UseCascade = trueOrFalse[0]
 	}
 	}
 	return session
 	return session
 }
 }
@@ -205,32 +213,32 @@ func (session *Session) Cascade(trueOrFalse ...bool) *Session {
 // NoCache ask this session do not retrieve data from cache system and
 // NoCache ask this session do not retrieve data from cache system and
 // get data from database directly.
 // get data from database directly.
 func (session *Session) NoCache() *Session {
 func (session *Session) NoCache() *Session {
-	session.Statement.UseCache = false
+	session.statement.UseCache = false
 	return session
 	return session
 }
 }
 
 
 // Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
 // Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
 func (session *Session) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
 func (session *Session) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
-	session.Statement.Join(joinOperator, tablename, condition, args...)
+	session.statement.Join(joinOperator, tablename, condition, args...)
 	return session
 	return session
 }
 }
 
 
 // GroupBy Generate Group By statement
 // GroupBy Generate Group By statement
 func (session *Session) GroupBy(keys string) *Session {
 func (session *Session) GroupBy(keys string) *Session {
-	session.Statement.GroupBy(keys)
+	session.statement.GroupBy(keys)
 	return session
 	return session
 }
 }
 
 
 // Having Generate Having statement
 // Having Generate Having statement
 func (session *Session) Having(conditions string) *Session {
 func (session *Session) Having(conditions string) *Session {
-	session.Statement.Having(conditions)
+	session.statement.Having(conditions)
 	return session
 	return session
 }
 }
 
 
 // DB db return the wrapper of sql.DB
 // DB db return the wrapper of sql.DB
 func (session *Session) DB() *core.DB {
 func (session *Session) DB() *core.DB {
 	if session.db == nil {
 	if session.db == nil {
-		session.db = session.Engine.db
+		session.db = session.engine.db
 		session.stmtCache = make(map[uint32]*core.Stmt, 0)
 		session.stmtCache = make(map[uint32]*core.Stmt, 0)
 	}
 	}
 	return session.db
 	return session.db
@@ -243,25 +251,25 @@ func cleanupProcessorsClosures(slices *[]func(interface{})) {
 }
 }
 
 
 func (session *Session) canCache() bool {
 func (session *Session) canCache() bool {
-	if session.Statement.RefTable == nil ||
-		session.Statement.JoinStr != "" ||
-		session.Statement.RawSQL != "" ||
-		!session.Statement.UseCache ||
-		session.Statement.IsForUpdate ||
-		session.Tx != nil ||
-		len(session.Statement.selectStr) > 0 {
+	if session.statement.RefTable == nil ||
+		session.statement.JoinStr != "" ||
+		session.statement.RawSQL != "" ||
+		!session.statement.UseCache ||
+		session.statement.IsForUpdate ||
+		session.tx != nil ||
+		len(session.statement.selectStr) > 0 {
 		return false
 		return false
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) {
+func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, err error) {
 	crc := crc32.ChecksumIEEE([]byte(sqlStr))
 	crc := crc32.ChecksumIEEE([]byte(sqlStr))
 	// TODO try hash(sqlStr+len(sqlStr))
 	// TODO try hash(sqlStr+len(sqlStr))
 	var has bool
 	var has bool
 	stmt, has = session.stmtCache[crc]
 	stmt, has = session.stmtCache[crc]
 	if !has {
 	if !has {
-		stmt, err = session.DB().Prepare(sqlStr)
+		stmt, err = db.Prepare(sqlStr)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -273,18 +281,18 @@ func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) {
 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 {
 	var col *core.Column
 	var col *core.Column
 	if col = table.GetColumnIdx(key, idx); col == nil {
 	if col = table.GetColumnIdx(key, idx); col == nil {
-		//session.Engine.logger.Warnf("table %v has no column %v. %v", table.Name, key, table.ColumnsSeq())
+		//session.engine.logger.Warnf("table %v has no column %v. %v", table.Name, key, table.ColumnsSeq())
 		return nil
 		return nil
 	}
 	}
 
 
 	fieldValue, err := col.ValueOfV(dataStruct)
 	fieldValue, err := col.ValueOfV(dataStruct)
 	if err != nil {
 	if err != nil {
-		session.Engine.logger.Error(err)
+		session.engine.logger.Error(err)
 		return nil
 		return nil
 	}
 	}
 
 
 	if !fieldValue.IsValid() || !fieldValue.CanSet() {
 	if !fieldValue.IsValid() || !fieldValue.CanSet() {
-		session.Engine.logger.Warnf("table %v's column %v is not valid or cannot set", table.Name, key)
+		session.engine.logger.Warnf("table %v's column %v is not valid or cannot set", table.Name, key)
 		return nil
 		return nil
 	}
 	}
 	return fieldValue
 	return fieldValue
@@ -293,28 +301,40 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
 // Cell cell is a result of one column field
 // Cell cell is a result of one column field
 type Cell *interface{}
 type Cell *interface{}
 
 
-func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount int,
+func (session *Session) rows2Beans(rows *core.Rows, fields []string,
 	table *core.Table, newElemFunc func([]string) reflect.Value,
 	table *core.Table, newElemFunc func([]string) reflect.Value,
 	sliceValueSetFunc func(*reflect.Value, core.PK) error) error {
 	sliceValueSetFunc func(*reflect.Value, core.PK) error) error {
 	for rows.Next() {
 	for rows.Next() {
 		var newValue = newElemFunc(fields)
 		var newValue = newElemFunc(fields)
 		bean := newValue.Interface()
 		bean := newValue.Interface()
-		dataStruct := rValue(bean)
-		pk, err := session.row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table)
+		dataStruct := newValue.Elem()
+
+		// handle beforeClosures
+		scanResults, err := session.row2Slice(rows, fields, bean)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-
-		err = sliceValueSetFunc(&newValue, pk)
+		pk, err := session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
+		session.afterProcessors = append(session.afterProcessors, executedProcessor{
+			fun: func(*Session, interface{}) error {
+				return sliceValueSetFunc(&newValue, pk)
+			},
+			session: session,
+			bean:    bean,
+		})
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) {
-	scanResults := make([]interface{}, fieldsCount)
+func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interface{}) ([]interface{}, error) {
+	for _, closure := range session.beforeClosures {
+		closure(bean)
+	}
+
+	scanResults := make([]interface{}, len(fields))
 	for i := 0; i < len(fields); i++ {
 	for i := 0; i < len(fields); i++ {
 		var cell interface{}
 		var cell interface{}
 		scanResults[i] = &cell
 		scanResults[i] = &cell
@@ -328,7 +348,10 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 			b.BeforeSet(key, Cell(scanResults[ii].(*interface{})))
 			b.BeforeSet(key, Cell(scanResults[ii].(*interface{})))
 		}
 		}
 	}
 	}
+	return scanResults, nil
+}
 
 
+func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) {
 	defer func() {
 	defer func() {
 		if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet {
 		if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet {
 			for ii, key := range fields {
 			for ii, key := range fields {
@@ -337,6 +360,40 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 		}
 		}
 	}()
 	}()
 
 
+	// handle afterClosures
+	for _, closure := range session.afterClosures {
+		session.afterProcessors = append(session.afterProcessors, executedProcessor{
+			fun: func(sess *Session, bean interface{}) error {
+				closure(bean)
+				return nil
+			},
+			session: session,
+			bean:    bean,
+		})
+	}
+
+	if a, has := bean.(AfterLoadProcessor); has {
+		session.afterProcessors = append(session.afterProcessors, executedProcessor{
+			fun: func(sess *Session, bean interface{}) error {
+				a.AfterLoad()
+				return nil
+			},
+			session: session,
+			bean:    bean,
+		})
+	}
+
+	if a, has := bean.(AfterLoadSessionProcessor); has {
+		session.afterProcessors = append(session.afterProcessors, executedProcessor{
+			fun: func(sess *Session, bean interface{}) error {
+				a.AfterLoad(sess)
+				return nil
+			},
+			session: session,
+			bean:    bean,
+		})
+	}
+
 	var tempMap = make(map[string]int)
 	var tempMap = make(map[string]int)
 	var pk core.PK
 	var pk core.PK
 	for ii, key := range fields {
 	for ii, key := range fields {
@@ -361,9 +418,11 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 			if fieldValue.CanAddr() {
 			if fieldValue.CanAddr() {
 				if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
 				if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
 					if data, err := value2Bytes(&rawValue); err == nil {
 					if data, err := value2Bytes(&rawValue); err == nil {
-						structConvert.FromDB(data)
+						if err := structConvert.FromDB(data); err != nil {
+							return nil, err
+						}
 					} else {
 					} else {
-						session.Engine.logger.Error(err)
+						return nil, err
 					}
 					}
 					continue
 					continue
 				}
 				}
@@ -376,7 +435,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 					}
 					}
 					fieldValue.Interface().(core.Conversion).FromDB(data)
 					fieldValue.Interface().(core.Conversion).FromDB(data)
 				} else {
 				} else {
-					session.Engine.logger.Error(err)
+					return nil, err
 				}
 				}
 				continue
 				continue
 			}
 			}
@@ -403,17 +462,19 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 				hasAssigned = true
 				hasAssigned = true
 
 
 				if len(bs) > 0 {
 				if len(bs) > 0 {
+					if fieldType.Kind() == reflect.String {
+						fieldValue.SetString(string(bs))
+						continue
+					}
 					if fieldValue.CanAddr() {
 					if fieldValue.CanAddr() {
 						err := json.Unmarshal(bs, fieldValue.Addr().Interface())
 						err := json.Unmarshal(bs, fieldValue.Addr().Interface())
 						if err != nil {
 						if err != nil {
-							session.Engine.logger.Error(key, err)
 							return nil, err
 							return nil, err
 						}
 						}
 					} else {
 					} else {
 						x := reflect.New(fieldType)
 						x := reflect.New(fieldType)
 						err := json.Unmarshal(bs, x.Interface())
 						err := json.Unmarshal(bs, x.Interface())
 						if err != nil {
 						if err != nil {
-							session.Engine.logger.Error(key, err)
 							return nil, err
 							return nil, err
 						}
 						}
 						fieldValue.Set(x.Elem())
 						fieldValue.Set(x.Elem())
@@ -438,14 +499,12 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 					if fieldValue.CanAddr() {
 					if fieldValue.CanAddr() {
 						err := json.Unmarshal(bs, fieldValue.Addr().Interface())
 						err := json.Unmarshal(bs, fieldValue.Addr().Interface())
 						if err != nil {
 						if err != nil {
-							session.Engine.logger.Error(err)
 							return nil, err
 							return nil, err
 						}
 						}
 					} else {
 					} else {
 						x := reflect.New(fieldType)
 						x := reflect.New(fieldType)
 						err := json.Unmarshal(bs, x.Interface())
 						err := json.Unmarshal(bs, x.Interface())
 						if err != nil {
 						if err != nil {
-							session.Engine.logger.Error(err)
 							return nil, err
 							return nil, err
 						}
 						}
 						fieldValue.Set(x.Elem())
 						fieldValue.Set(x.Elem())
@@ -462,14 +521,19 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 								x := reflect.New(fieldType)
 								x := reflect.New(fieldType)
 								err := json.Unmarshal(vv.Bytes(), x.Interface())
 								err := json.Unmarshal(vv.Bytes(), x.Interface())
 								if err != nil {
 								if err != nil {
-									session.Engine.logger.Error(err)
 									return nil, err
 									return nil, err
 								}
 								}
 								fieldValue.Set(x.Elem())
 								fieldValue.Set(x.Elem())
 							} else {
 							} else {
-								for i := 0; i < fieldValue.Len(); i++ {
-									if i < vv.Len() {
-										fieldValue.Index(i).Set(vv.Index(i))
+								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)))
 									}
 									}
 								}
 								}
 							}
 							}
@@ -509,57 +573,38 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 				}
 				}
 			case reflect.Struct:
 			case reflect.Struct:
 				if fieldType.ConvertibleTo(core.TimeType) {
 				if fieldType.ConvertibleTo(core.TimeType) {
+					dbTZ := session.engine.DatabaseTZ
+					if col.TimeZone != nil {
+						dbTZ = col.TimeZone
+					}
+
 					if rawValueType == core.TimeType {
 					if rawValueType == core.TimeType {
 						hasAssigned = true
 						hasAssigned = true
 
 
 						t := vv.Convert(core.TimeType).Interface().(time.Time)
 						t := vv.Convert(core.TimeType).Interface().(time.Time)
 
 
 						z, _ := t.Zone()
 						z, _ := t.Zone()
-						dbTZ := session.Engine.DatabaseTZ
-						if dbTZ == nil {
-							if session.Engine.dialect.DBType() == core.SQLITE {
-								dbTZ = time.UTC
-							} else {
-								dbTZ = time.Local
-							}
-						}
-
 						// set new location if database don't save timezone or give an incorrect timezone
 						// 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
 						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())
+							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 = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
 								t.Minute(), t.Second(), t.Nanosecond(), dbTZ)
 								t.Minute(), t.Second(), t.Nanosecond(), dbTZ)
 						}
 						}
 
 
-						// !nashtsai! convert to engine location
-						if col.TimeZone == nil {
-							t = t.In(session.Engine.TZLocation)
-						} else {
-							t = t.In(col.TimeZone)
-						}
+						t = t.In(session.engine.TZLocation)
 						fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 						fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
-
-						// t = fieldValue.Interface().(time.Time)
-						// z, _ = t.Zone()
-						// session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
 					} else if rawValueType == core.IntType || rawValueType == core.Int64Type ||
 					} else if rawValueType == core.IntType || rawValueType == core.Int64Type ||
 						rawValueType == core.Int32Type {
 						rawValueType == core.Int32Type {
 						hasAssigned = true
 						hasAssigned = true
-						var tz *time.Location
-						if col.TimeZone == nil {
-							tz = session.Engine.TZLocation
-						} else {
-							tz = col.TimeZone
-						}
-						t := time.Unix(vv.Int(), 0).In(tz)
-						//vv = reflect.ValueOf(t)
+
+						t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation)
 						fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 						fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 					} else {
 					} else {
 						if d, ok := vv.Interface().([]uint8); ok {
 						if d, ok := vv.Interface().([]uint8); ok {
 							hasAssigned = true
 							hasAssigned = true
 							t, err := session.byte2Time(col, d)
 							t, err := session.byte2Time(col, d)
 							if err != nil {
 							if err != nil {
-								session.Engine.logger.Error("byte2Time error:", err.Error())
+								session.engine.logger.Error("byte2Time error:", err.Error())
 								hasAssigned = false
 								hasAssigned = false
 							} else {
 							} else {
 								fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 								fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
@@ -568,20 +613,20 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 							hasAssigned = true
 							hasAssigned = true
 							t, err := session.str2Time(col, d)
 							t, err := session.str2Time(col, d)
 							if err != nil {
 							if err != nil {
-								session.Engine.logger.Error("byte2Time error:", err.Error())
+								session.engine.logger.Error("byte2Time error:", err.Error())
 								hasAssigned = false
 								hasAssigned = false
 							} else {
 							} else {
 								fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 								fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 							}
 							}
 						} else {
 						} else {
-							panic(fmt.Sprintf("rawValueType is %v, value is %v", rawValueType, vv.Interface()))
+							return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface())
 						}
 						}
 					}
 					}
 				} else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
 				} else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
 					// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
 					// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
 					hasAssigned = true
 					hasAssigned = true
 					if err := nulVal.Scan(vv.Interface()); err != nil {
 					if err := nulVal.Scan(vv.Interface()); err != nil {
-						session.Engine.logger.Error("sql.Sanner error:", err.Error())
+						session.engine.logger.Error("sql.Sanner error:", err.Error())
 						hasAssigned = false
 						hasAssigned = false
 					}
 					}
 				} else if col.SQLType.IsJson() {
 				} else if col.SQLType.IsJson() {
@@ -591,7 +636,6 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 						if len([]byte(vv.String())) > 0 {
 						if len([]byte(vv.String())) > 0 {
 							err := json.Unmarshal([]byte(vv.String()), x.Interface())
 							err := json.Unmarshal([]byte(vv.String()), x.Interface())
 							if err != nil {
 							if err != nil {
-								session.Engine.logger.Error(err)
 								return nil, err
 								return nil, err
 							}
 							}
 							fieldValue.Set(x.Elem())
 							fieldValue.Set(x.Elem())
@@ -602,76 +646,45 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 						if len(vv.Bytes()) > 0 {
 						if len(vv.Bytes()) > 0 {
 							err := json.Unmarshal(vv.Bytes(), x.Interface())
 							err := json.Unmarshal(vv.Bytes(), x.Interface())
 							if err != nil {
 							if err != nil {
-								session.Engine.logger.Error(err)
 								return nil, err
 								return nil, err
 							}
 							}
 							fieldValue.Set(x.Elem())
 							fieldValue.Set(x.Elem())
 						}
 						}
 					}
 					}
-				} else if session.Statement.UseCascade {
-					table := session.Engine.autoMapType(*fieldValue)
-					if table != nil {
-						hasAssigned = true
-						if len(table.PrimaryKeys) != 1 {
-							panic("unsupported non or composited primary key cascade")
-						}
-						var pk = make(core.PK, len(table.PrimaryKeys))
-
-						switch rawValueType.Kind() {
-						case reflect.Int64:
-							pk[0] = vv.Int()
-						case reflect.Int:
-							pk[0] = int(vv.Int())
-						case reflect.Int32:
-							pk[0] = int32(vv.Int())
-						case reflect.Int16:
-							pk[0] = int16(vv.Int())
-						case reflect.Int8:
-							pk[0] = int8(vv.Int())
-						case reflect.Uint64:
-							pk[0] = vv.Uint()
-						case reflect.Uint:
-							pk[0] = uint(vv.Uint())
-						case reflect.Uint32:
-							pk[0] = uint32(vv.Uint())
-						case reflect.Uint16:
-							pk[0] = uint16(vv.Uint())
-						case reflect.Uint8:
-							pk[0] = uint8(vv.Uint())
-						case reflect.String:
-							pk[0] = vv.String()
-						case reflect.Slice:
-							pk[0], _ = strconv.ParseInt(string(rawValue.Interface().([]byte)), 10, 64)
-						default:
-							panic(fmt.Sprintf("unsupported primary key type: %v, %v", rawValueType, fieldValue))
-						}
+				} else if session.statement.UseCascade {
+					table, err := session.engine.autoMapType(*fieldValue)
+					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())
-							newsession := session.Engine.NewSession()
-							defer newsession.Close()
-							has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
-							if err != nil {
-								return nil, err
-							}
-							if has {
-								//v := structInter.Elem().Interface()
-								//fieldValue.Set(reflect.ValueOf(v))
-								fieldValue.Set(structInter.Elem())
-							} else {
-								return nil, errors.New("cascade obj is not exist")
-							}
+					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")
 						}
 						}
-					} else {
-						session.Engine.logger.Error("unsupported struct type in Scan: ", fieldValue.Type().String())
 					}
 					}
 				}
 				}
 			case reflect.Ptr:
 			case reflect.Ptr:
 				// !nashtsai! TODO merge duplicated codes above
 				// !nashtsai! TODO merge duplicated codes above
-				//typeStr := fieldType.String()
 				switch fieldType {
 				switch fieldType {
 				// following types case matching ptr's native type, therefore assign ptr directly
 				// following types case matching ptr's native type, therefore assign ptr directly
 				case core.PtrStringType:
 				case core.PtrStringType:
@@ -769,10 +782,9 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 					if len([]byte(vv.String())) > 0 {
 					if len([]byte(vv.String())) > 0 {
 						err := json.Unmarshal([]byte(vv.String()), &x)
 						err := json.Unmarshal([]byte(vv.String()), &x)
 						if err != nil {
 						if err != nil {
-							session.Engine.logger.Error(err)
-						} else {
-							fieldValue.Set(reflect.ValueOf(&x))
+							return nil, err
 						}
 						}
+						fieldValue.Set(reflect.ValueOf(&x))
 					}
 					}
 					hasAssigned = true
 					hasAssigned = true
 				case core.Complex128Type:
 				case core.Complex128Type:
@@ -780,24 +792,23 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 					if len([]byte(vv.String())) > 0 {
 					if len([]byte(vv.String())) > 0 {
 						err := json.Unmarshal([]byte(vv.String()), &x)
 						err := json.Unmarshal([]byte(vv.String()), &x)
 						if err != nil {
 						if err != nil {
-							session.Engine.logger.Error(err)
-						} else {
-							fieldValue.Set(reflect.ValueOf(&x))
+							return nil, err
 						}
 						}
+						fieldValue.Set(reflect.ValueOf(&x))
 					}
 					}
 					hasAssigned = true
 					hasAssigned = true
 				} // switch fieldType
 				} // switch fieldType
-				// default:
-				// 	session.Engine.LogError("unsupported type in Scan: ", reflect.TypeOf(v).String())
 			} // switch fieldType.Kind()
 			} // switch fieldType.Kind()
 
 
 			// !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
 			// !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
 			if !hasAssigned {
 			if !hasAssigned {
 				data, err := value2Bytes(&rawValue)
 				data, err := value2Bytes(&rawValue)
-				if err == nil {
-					session.bytes2Value(col, fieldValue, data)
-				} else {
-					session.Engine.logger.Error(err.Error())
+				if err != nil {
+					return nil, err
+				}
+
+				if err = session.bytes2Value(col, fieldValue, data); err != nil {
+					return nil, err
 				}
 				}
 			}
 			}
 		}
 		}
@@ -805,19 +816,11 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 	return pk, nil
 	return pk, nil
 }
 }
 
 
-func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) {
-	for _, filter := range session.Engine.dialect.Filters() {
-		*sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable)
-	}
-
-	session.saveLastSQL(*sqlStr, paramStr...)
-}
-
 // saveLastSQL stores executed query information
 // saveLastSQL stores executed query information
 func (session *Session) saveLastSQL(sql string, args ...interface{}) {
 func (session *Session) saveLastSQL(sql string, args ...interface{}) {
 	session.lastSQL = sql
 	session.lastSQL = sql
 	session.lastSQLArgs = args
 	session.lastSQLArgs = args
-	session.Engine.logSQL(sql, args...)
+	session.engine.logSQL(sql, args...)
 }
 }
 
 
 // LastSQL returns last query information
 // LastSQL returns last query information
@@ -827,8 +830,8 @@ func (session *Session) LastSQL() (string, []interface{}) {
 
 
 // tbName get some table's table name
 // tbName get some table's table name
 func (session *Session) tbNameNoSchema(table *core.Table) string {
 func (session *Session) tbNameNoSchema(table *core.Table) string {
-	if len(session.Statement.AltTableName) > 0 {
-		return session.Statement.AltTableName
+	if len(session.statement.AltTableName) > 0 {
+		return session.statement.AltTableName
 	}
 	}
 
 
 	return table.Name
 	return table.Name
@@ -836,6 +839,6 @@ func (session *Session) tbNameNoSchema(table *core.Table) string {
 
 
 // Unscoped always disable struct tag "deleted"
 // Unscoped always disable struct tag "deleted"
 func (session *Session) Unscoped() *Session {
 func (session *Session) Unscoped() *Session {
-	session.Statement.Unscoped()
+	session.statement.Unscoped()
 	return session
 	return session
 }
 }

+ 12 - 12
vendor/github.com/go-xorm/xorm/session_cols.go

@@ -6,43 +6,43 @@ package xorm
 
 
 // Incr provides a query string like "count = count + 1"
 // Incr provides a query string like "count = count + 1"
 func (session *Session) Incr(column string, arg ...interface{}) *Session {
 func (session *Session) Incr(column string, arg ...interface{}) *Session {
-	session.Statement.Incr(column, arg...)
+	session.statement.Incr(column, arg...)
 	return session
 	return session
 }
 }
 
 
 // Decr provides a query string like "count = count - 1"
 // Decr provides a query string like "count = count - 1"
 func (session *Session) Decr(column string, arg ...interface{}) *Session {
 func (session *Session) Decr(column string, arg ...interface{}) *Session {
-	session.Statement.Decr(column, arg...)
+	session.statement.Decr(column, arg...)
 	return session
 	return session
 }
 }
 
 
 // SetExpr provides a query string like "column = {expression}"
 // SetExpr provides a query string like "column = {expression}"
 func (session *Session) SetExpr(column string, expression string) *Session {
 func (session *Session) SetExpr(column string, expression string) *Session {
-	session.Statement.SetExpr(column, expression)
+	session.statement.SetExpr(column, expression)
 	return session
 	return session
 }
 }
 
 
 // Select provides some columns to special
 // Select provides some columns to special
 func (session *Session) Select(str string) *Session {
 func (session *Session) Select(str string) *Session {
-	session.Statement.Select(str)
+	session.statement.Select(str)
 	return session
 	return session
 }
 }
 
 
 // Cols provides some columns to special
 // Cols provides some columns to special
 func (session *Session) Cols(columns ...string) *Session {
 func (session *Session) Cols(columns ...string) *Session {
-	session.Statement.Cols(columns...)
+	session.statement.Cols(columns...)
 	return session
 	return session
 }
 }
 
 
 // AllCols ask all columns
 // AllCols ask all columns
 func (session *Session) AllCols() *Session {
 func (session *Session) AllCols() *Session {
-	session.Statement.AllCols()
+	session.statement.AllCols()
 	return session
 	return session
 }
 }
 
 
 // MustCols specify some columns must use even if they are empty
 // MustCols specify some columns must use even if they are empty
 func (session *Session) MustCols(columns ...string) *Session {
 func (session *Session) MustCols(columns ...string) *Session {
-	session.Statement.MustCols(columns...)
+	session.statement.MustCols(columns...)
 	return session
 	return session
 }
 }
 
 
@@ -52,7 +52,7 @@ func (session *Session) MustCols(columns ...string) *Session {
 // If no parameters, it will use all the bool field of struct, or
 // If no parameters, it will use all the bool field of struct, or
 // it will use parameters's columns
 // it will use parameters's columns
 func (session *Session) UseBool(columns ...string) *Session {
 func (session *Session) UseBool(columns ...string) *Session {
-	session.Statement.UseBool(columns...)
+	session.statement.UseBool(columns...)
 	return session
 	return session
 }
 }
 
 
@@ -60,25 +60,25 @@ func (session *Session) UseBool(columns ...string) *Session {
 // distinct will not be cached because cache system need id,
 // distinct will not be cached because cache system need id,
 // but distinct will not provide id
 // but distinct will not provide id
 func (session *Session) Distinct(columns ...string) *Session {
 func (session *Session) Distinct(columns ...string) *Session {
-	session.Statement.Distinct(columns...)
+	session.statement.Distinct(columns...)
 	return session
 	return session
 }
 }
 
 
 // Omit Only not use the parameters as select or update columns
 // Omit Only not use the parameters as select or update columns
 func (session *Session) Omit(columns ...string) *Session {
 func (session *Session) Omit(columns ...string) *Session {
-	session.Statement.Omit(columns...)
+	session.statement.Omit(columns...)
 	return session
 	return session
 }
 }
 
 
 // Nullable Set null when column is zero-value and nullable for update
 // Nullable Set null when column is zero-value and nullable for update
 func (session *Session) Nullable(columns ...string) *Session {
 func (session *Session) Nullable(columns ...string) *Session {
-	session.Statement.Nullable(columns...)
+	session.statement.Nullable(columns...)
 	return session
 	return session
 }
 }
 
 
 // NoAutoTime means do not automatically give created field and updated field
 // NoAutoTime means do not automatically give created field and updated field
 // the current time on the current session temporarily
 // the current time on the current session temporarily
 func (session *Session) NoAutoTime() *Session {
 func (session *Session) NoAutoTime() *Session {
-	session.Statement.UseAutoTime = false
+	session.statement.UseAutoTime = false
 	return session
 	return session
 }
 }

+ 9 - 9
vendor/github.com/go-xorm/xorm/session_cond.go

@@ -17,25 +17,25 @@ func (session *Session) Sql(query string, args ...interface{}) *Session {
 // SQL provides raw sql input parameter. When you have a complex SQL statement
 // SQL provides raw sql input parameter. When you have a complex SQL statement
 // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
 // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
 func (session *Session) SQL(query interface{}, args ...interface{}) *Session {
 func (session *Session) SQL(query interface{}, args ...interface{}) *Session {
-	session.Statement.SQL(query, args...)
+	session.statement.SQL(query, args...)
 	return session
 	return session
 }
 }
 
 
 // Where provides custom query condition.
 // Where provides custom query condition.
 func (session *Session) Where(query interface{}, args ...interface{}) *Session {
 func (session *Session) Where(query interface{}, args ...interface{}) *Session {
-	session.Statement.Where(query, args...)
+	session.statement.Where(query, args...)
 	return session
 	return session
 }
 }
 
 
 // And provides custom query condition.
 // And provides custom query condition.
 func (session *Session) And(query interface{}, args ...interface{}) *Session {
 func (session *Session) And(query interface{}, args ...interface{}) *Session {
-	session.Statement.And(query, args...)
+	session.statement.And(query, args...)
 	return session
 	return session
 }
 }
 
 
 // Or provides custom query condition.
 // Or provides custom query condition.
 func (session *Session) Or(query interface{}, args ...interface{}) *Session {
 func (session *Session) Or(query interface{}, args ...interface{}) *Session {
-	session.Statement.Or(query, args...)
+	session.statement.Or(query, args...)
 	return session
 	return session
 }
 }
 
 
@@ -48,23 +48,23 @@ func (session *Session) Id(id interface{}) *Session {
 
 
 // ID provides converting id as a query condition
 // ID provides converting id as a query condition
 func (session *Session) ID(id interface{}) *Session {
 func (session *Session) ID(id interface{}) *Session {
-	session.Statement.ID(id)
+	session.statement.ID(id)
 	return session
 	return session
 }
 }
 
 
 // In provides a query string like "id in (1, 2, 3)"
 // In provides a query string like "id in (1, 2, 3)"
 func (session *Session) In(column string, args ...interface{}) *Session {
 func (session *Session) In(column string, args ...interface{}) *Session {
-	session.Statement.In(column, args...)
+	session.statement.In(column, args...)
 	return session
 	return session
 }
 }
 
 
 // NotIn provides a query string like "id in (1, 2, 3)"
 // NotIn provides a query string like "id in (1, 2, 3)"
 func (session *Session) NotIn(column string, args ...interface{}) *Session {
 func (session *Session) NotIn(column string, args ...interface{}) *Session {
-	session.Statement.NotIn(column, args...)
+	session.statement.NotIn(column, args...)
 	return session
 	return session
 }
 }
 
 
-// Conds returns session query conditions
+// Conds returns session query conditions except auto bean conditions
 func (session *Session) Conds() builder.Cond {
 func (session *Session) Conds() builder.Cond {
-	return session.Statement.cond
+	return session.statement.cond
 }
 }

+ 101 - 109
vendor/github.com/go-xorm/xorm/session_convert.go

@@ -23,41 +23,38 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
 	var x time.Time
 	var x time.Time
 	var err error
 	var err error
 
 
-	if sdata == "0000-00-00 00:00:00" ||
-		sdata == "0001-01-01 00:00:00" {
+	var parseLoc = session.engine.DatabaseTZ
+	if col.TimeZone != nil {
+		parseLoc = col.TimeZone
+	}
+
+	if sdata == zeroTime0 || sdata == zeroTime1 {
 	} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
 	} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
 		// time stamp
 		// time stamp
 		sd, err := strconv.ParseInt(sdata, 10, 64)
 		sd, err := strconv.ParseInt(sdata, 10, 64)
 		if err == nil {
 		if err == nil {
 			x = time.Unix(sd, 0)
 			x = time.Unix(sd, 0)
-			// !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion
-			if col.TimeZone == nil {
-				x = x.In(session.Engine.TZLocation)
-			} else {
-				x = x.In(col.TimeZone)
-			}
-			session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+			//session.engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 		} else {
 		} else {
-			session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+			//session.engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 		}
 		}
 	} else if len(sdata) > 19 && strings.Contains(sdata, "-") {
 	} else if len(sdata) > 19 && strings.Contains(sdata, "-") {
-		x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		x, err = time.ParseInLocation(time.RFC3339Nano, sdata, parseLoc)
+		session.engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 		if err != nil {
 		if err != nil {
-			x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation)
-			session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+			x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, parseLoc)
+			//session.engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 		}
 		}
 		if err != nil {
 		if err != nil {
-			x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation)
-			session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+			x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, parseLoc)
+			//session.engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 		}
 		}
-
 	} else if len(sdata) == 19 && strings.Contains(sdata, "-") {
 	} else if len(sdata) == 19 && strings.Contains(sdata, "-") {
-		x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, parseLoc)
+		//session.engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 	} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
 	} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
-		x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		x, err = time.ParseInLocation("2006-01-02", sdata, parseLoc)
+		//session.engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 	} else if col.SQLType.Name == core.Time {
 	} else if col.SQLType.Name == core.Time {
 		if strings.Contains(sdata, " ") {
 		if strings.Contains(sdata, " ") {
 			ssd := strings.Split(sdata, " ")
 			ssd := strings.Split(sdata, " ")
@@ -65,13 +62,13 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
 		}
 		}
 
 
 		sdata = strings.TrimSpace(sdata)
 		sdata = strings.TrimSpace(sdata)
-		if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
+		if session.engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
 			sdata = sdata[len(sdata)-8:]
 			sdata = sdata[len(sdata)-8:]
 		}
 		}
 
 
 		st := fmt.Sprintf("2006-01-02 %v", sdata)
 		st := fmt.Sprintf("2006-01-02 %v", sdata)
-		x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		x, err = time.ParseInLocation("2006-01-02 15:04:05", st, parseLoc)
+		//session.engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
 	} else {
 	} else {
 		outErr = fmt.Errorf("unsupported time format %v", sdata)
 		outErr = fmt.Errorf("unsupported time format %v", sdata)
 		return
 		return
@@ -80,7 +77,7 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
 		outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
 		outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
 		return
 		return
 	}
 	}
-	outTime = x
+	outTime = x.In(session.engine.TZLocation)
 	return
 	return
 }
 }
 
 
@@ -108,7 +105,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 		if len(data) > 0 {
 		if len(data) > 0 {
 			err := json.Unmarshal(data, x.Interface())
 			err := json.Unmarshal(data, x.Interface())
 			if err != nil {
 			if err != nil {
-				session.Engine.logger.Error(err)
+				session.engine.logger.Error(err)
 				return err
 				return err
 			}
 			}
 			fieldValue.Set(x.Elem())
 			fieldValue.Set(x.Elem())
@@ -122,7 +119,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			if len(data) > 0 {
 			if len(data) > 0 {
 				err := json.Unmarshal(data, x.Interface())
 				err := json.Unmarshal(data, x.Interface())
 				if err != nil {
 				if err != nil {
-					session.Engine.logger.Error(err)
+					session.engine.logger.Error(err)
 					return err
 					return err
 				}
 				}
 				fieldValue.Set(x.Elem())
 				fieldValue.Set(x.Elem())
@@ -135,7 +132,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 				if len(data) > 0 {
 				if len(data) > 0 {
 					err := json.Unmarshal(data, x.Interface())
 					err := json.Unmarshal(data, x.Interface())
 					if err != nil {
 					if err != nil {
-						session.Engine.logger.Error(err)
+						session.engine.logger.Error(err)
 						return err
 						return err
 					}
 					}
 					fieldValue.Set(x.Elem())
 					fieldValue.Set(x.Elem())
@@ -147,8 +144,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 	case reflect.String:
 	case reflect.String:
 		fieldValue.SetString(string(data))
 		fieldValue.SetString(string(data))
 	case reflect.Bool:
 	case reflect.Bool:
-		d := string(data)
-		v, err := strconv.ParseBool(d)
+		v, err := asBool(data)
 		if err != nil {
 		if err != nil {
 			return fmt.Errorf("arg %v as bool: %s", key, err.Error())
 			return fmt.Errorf("arg %v as bool: %s", key, err.Error())
 		}
 		}
@@ -159,7 +155,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 		var err error
 		var err error
 		// for mysql, when use bit, it returned \x01
 		// for mysql, when use bit, it returned \x01
 		if col.SQLType.Name == core.Bit &&
 		if col.SQLType.Name == core.Bit &&
-			session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
+			session.engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
 			if len(data) == 1 {
 			if len(data) == 1 {
 				x = int64(data[0])
 				x = int64(data[0])
 			} else {
 			} else {
@@ -207,41 +203,39 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 				}
 				}
 				v = x
 				v = x
 				fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
 				fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
-			} else if session.Statement.UseCascade {
-				table := session.Engine.autoMapType(*fieldValue)
-				if table != nil {
-					// TODO: current only support 1 primary key
-					if len(table.PrimaryKeys) > 1 {
-						panic("unsupported composited primary key cascade")
-					}
-					var pk = make(core.PK, len(table.PrimaryKeys))
-					rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
-					var err error
-					pk[0], err = str2PK(string(data), rawValueType)
+			} else if session.statement.UseCascade {
+				table, err := session.engine.autoMapType(*fieldValue)
+				if err != nil {
+					return err
+				}
+
+				// TODO: current only support 1 primary key
+				if len(table.PrimaryKeys) > 1 {
+					return errors.New("unsupported composited primary key cascade")
+				}
+
+				var pk = make(core.PK, len(table.PrimaryKeys))
+				rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+				pk[0], err = str2PK(string(data), rawValueType)
+				if err != nil {
+					return 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 {
 					if err != nil {
 						return err
 						return 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())
-						newsession := session.Engine.NewSession()
-						defer newsession.Close()
-						has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
-						if err != nil {
-							return err
-						}
-						if has {
-							v = structInter.Elem().Interface()
-							fieldValue.Set(reflect.ValueOf(v))
-						} else {
-							return errors.New("cascade obj is not exist")
-						}
+					if has {
+						v = structInter.Elem().Interface()
+						fieldValue.Set(reflect.ValueOf(v))
+					} else {
+						return errors.New("cascade obj is not exist")
 					}
 					}
-				} else {
-					return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
 				}
 				}
 			}
 			}
 		}
 		}
@@ -267,7 +261,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			if len(data) > 0 {
 			if len(data) > 0 {
 				err := json.Unmarshal(data, &x)
 				err := json.Unmarshal(data, &x)
 				if err != nil {
 				if err != nil {
-					session.Engine.logger.Error(err)
+					session.engine.logger.Error(err)
 					return err
 					return err
 				}
 				}
 				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
 				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
@@ -278,7 +272,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			if len(data) > 0 {
 			if len(data) > 0 {
 				err := json.Unmarshal(data, &x)
 				err := json.Unmarshal(data, &x)
 				if err != nil {
 				if err != nil {
-					session.Engine.logger.Error(err)
+					session.engine.logger.Error(err)
 					return err
 					return err
 				}
 				}
 				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
 				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
@@ -350,7 +344,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			var err error
 			var err error
 			// for mysql, when use bit, it returned \x01
 			// for mysql, when use bit, it returned \x01
 			if col.SQLType.Name == core.Bit &&
 			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
+				strings.Contains(session.engine.DriverName(), "mysql") {
 				if len(data) == 1 {
 				if len(data) == 1 {
 					x = int64(data[0])
 					x = int64(data[0])
 				} else {
 				} else {
@@ -375,7 +369,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			var err error
 			var err error
 			// for mysql, when use bit, it returned \x01
 			// for mysql, when use bit, it returned \x01
 			if col.SQLType.Name == core.Bit &&
 			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
+				strings.Contains(session.engine.DriverName(), "mysql") {
 				if len(data) == 1 {
 				if len(data) == 1 {
 					x = int(data[0])
 					x = int(data[0])
 				} else {
 				} else {
@@ -403,7 +397,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			var err error
 			var err error
 			// for mysql, when use bit, it returned \x01
 			// for mysql, when use bit, it returned \x01
 			if col.SQLType.Name == core.Bit &&
 			if col.SQLType.Name == core.Bit &&
-				session.Engine.dialect.DBType() == core.MYSQL {
+				session.engine.dialect.DBType() == core.MYSQL {
 				if len(data) == 1 {
 				if len(data) == 1 {
 					x = int32(data[0])
 					x = int32(data[0])
 				} else {
 				} else {
@@ -431,7 +425,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			var err error
 			var err error
 			// for mysql, when use bit, it returned \x01
 			// for mysql, when use bit, it returned \x01
 			if col.SQLType.Name == core.Bit &&
 			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
+				strings.Contains(session.engine.DriverName(), "mysql") {
 				if len(data) == 1 {
 				if len(data) == 1 {
 					x = int8(data[0])
 					x = int8(data[0])
 				} else {
 				} else {
@@ -459,7 +453,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 			var err error
 			var err error
 			// for mysql, when use bit, it returned \x01
 			// for mysql, when use bit, it returned \x01
 			if col.SQLType.Name == core.Bit &&
 			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
+				strings.Contains(session.engine.DriverName(), "mysql") {
 				if len(data) == 1 {
 				if len(data) == 1 {
 					x = int16(data[0])
 					x = int16(data[0])
 				} else {
 				} else {
@@ -491,37 +485,37 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
 				v = x
 				v = x
 				fieldValue.Set(reflect.ValueOf(&x))
 				fieldValue.Set(reflect.ValueOf(&x))
 			default:
 			default:
-				if session.Statement.UseCascade {
+				if session.statement.UseCascade {
 					structInter := reflect.New(fieldType.Elem())
 					structInter := reflect.New(fieldType.Elem())
-					table := session.Engine.autoMapType(structInter.Elem())
-					if table != nil {
-						if len(table.PrimaryKeys) > 1 {
-							panic("unsupported composited primary key cascade")
-						}
-						var pk = make(core.PK, len(table.PrimaryKeys))
-						var err error
-						rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
-						pk[0], err = str2PK(string(data), rawValueType)
+					table, err := session.engine.autoMapType(structInter.Elem())
+					if err != nil {
+						return err
+					}
+
+					if len(table.PrimaryKeys) > 1 {
+						return errors.New("unsupported composited primary key cascade")
+					}
+
+					var pk = make(core.PK, len(table.PrimaryKeys))
+					rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+					pk[0], err = str2PK(string(data), rawValueType)
+					if err != nil {
+						return 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
+						has, err := session.ID(pk).NoCascade().get(structInter.Interface())
 						if err != nil {
 						if err != nil {
 							return err
 							return 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
-							newsession := session.Engine.NewSession()
-							defer newsession.Close()
-							has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
-							if err != nil {
-								return err
-							}
-							if has {
-								v = structInter.Interface()
-								fieldValue.Set(reflect.ValueOf(v))
-							} else {
-								return errors.New("cascade obj is not exist")
-							}
+						if has {
+							v = structInter.Interface()
+							fieldValue.Set(reflect.ValueOf(v))
+						} else {
+							return errors.New("cascade obj is not exist")
 						}
 						}
 					}
 					}
 				} else {
 				} else {
@@ -570,7 +564,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 		if fieldValue.IsNil() {
 		if fieldValue.IsNil() {
 			return nil, nil
 			return nil, nil
 		} else if !fieldValue.IsValid() {
 		} else if !fieldValue.IsValid() {
-			session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid")
+			session.engine.logger.Warn("the field[", col.FieldName, "] is invalid")
 			return nil, nil
 			return nil, nil
 		} else {
 		} else {
 			// !nashtsai! deference pointer type to instance type
 			// !nashtsai! deference pointer type to instance type
@@ -588,12 +582,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 	case reflect.Struct:
 	case reflect.Struct:
 		if fieldType.ConvertibleTo(core.TimeType) {
 		if fieldType.ConvertibleTo(core.TimeType) {
 			t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
 			t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
-			if session.Engine.dialect.DBType() == core.MSSQL {
-				if t.IsZero() {
-					return nil, nil
-				}
-			}
-			tf := session.Engine.FormatTime(col.SQLType.Name, t)
+			tf := session.engine.formatColTime(col, t)
 			return tf, nil
 			return tf, nil
 		}
 		}
 
 
@@ -603,7 +592,10 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 				return v.Value()
 				return v.Value()
 			}
 			}
 
 
-			fieldTable := session.Engine.autoMapType(fieldValue)
+			fieldTable, err := session.engine.autoMapType(fieldValue)
+			if err != nil {
+				return nil, err
+			}
 			if len(fieldTable.PrimaryKeys) == 1 {
 			if len(fieldTable.PrimaryKeys) == 1 {
 				pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
 				pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
 				return pkField.Interface(), nil
 				return pkField.Interface(), nil
@@ -614,14 +606,14 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 		if col.SQLType.IsText() {
 		if col.SQLType.IsText() {
 			bytes, err := json.Marshal(fieldValue.Interface())
 			bytes, err := json.Marshal(fieldValue.Interface())
 			if err != nil {
 			if err != nil {
-				session.Engine.logger.Error(err)
+				session.engine.logger.Error(err)
 				return 0, err
 				return 0, err
 			}
 			}
 			return string(bytes), nil
 			return string(bytes), nil
 		} else if col.SQLType.IsBlob() {
 		} else if col.SQLType.IsBlob() {
 			bytes, err := json.Marshal(fieldValue.Interface())
 			bytes, err := json.Marshal(fieldValue.Interface())
 			if err != nil {
 			if err != nil {
-				session.Engine.logger.Error(err)
+				session.engine.logger.Error(err)
 				return 0, err
 				return 0, err
 			}
 			}
 			return bytes, nil
 			return bytes, nil
@@ -630,7 +622,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 	case reflect.Complex64, reflect.Complex128:
 	case reflect.Complex64, reflect.Complex128:
 		bytes, err := json.Marshal(fieldValue.Interface())
 		bytes, err := json.Marshal(fieldValue.Interface())
 		if err != nil {
 		if err != nil {
-			session.Engine.logger.Error(err)
+			session.engine.logger.Error(err)
 			return 0, err
 			return 0, err
 		}
 		}
 		return string(bytes), nil
 		return string(bytes), nil
@@ -642,7 +634,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 		if col.SQLType.IsText() {
 		if col.SQLType.IsText() {
 			bytes, err := json.Marshal(fieldValue.Interface())
 			bytes, err := json.Marshal(fieldValue.Interface())
 			if err != nil {
 			if err != nil {
-				session.Engine.logger.Error(err)
+				session.engine.logger.Error(err)
 				return 0, err
 				return 0, err
 			}
 			}
 			return string(bytes), nil
 			return string(bytes), nil
@@ -655,7 +647,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 			} else {
 			} else {
 				bytes, err = json.Marshal(fieldValue.Interface())
 				bytes, err = json.Marshal(fieldValue.Interface())
 				if err != nil {
 				if err != nil {
-					session.Engine.logger.Error(err)
+					session.engine.logger.Error(err)
 					return 0, err
 					return 0, err
 				}
 				}
 			}
 			}

+ 40 - 38
vendor/github.com/go-xorm/xorm/session_delete.go

@@ -12,26 +12,26 @@ import (
 	"github.com/go-xorm/core"
 	"github.com/go-xorm/core"
 )
 )
 
 
-func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
-	if session.Statement.RefTable == nil ||
-		session.Tx != nil {
+func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error {
+	if table == nil ||
+		session.tx != nil {
 		return ErrCacheFailed
 		return ErrCacheFailed
 	}
 	}
 
 
-	for _, filter := range session.Engine.dialect.Filters() {
-		sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
+	for _, filter := range session.engine.dialect.Filters() {
+		sqlStr = filter.Do(sqlStr, session.engine.dialect, table)
 	}
 	}
 
 
-	newsql := session.Statement.convertIDSQL(sqlStr)
+	newsql := session.statement.convertIDSQL(sqlStr)
 	if newsql == "" {
 	if newsql == "" {
 		return ErrCacheFailed
 		return ErrCacheFailed
 	}
 	}
 
 
-	cacher := session.Engine.getCacher2(session.Statement.RefTable)
-	tableName := session.Statement.TableName()
+	cacher := session.engine.getCacher2(table)
+	pkColumns := table.PKColumns()
 	ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
 	ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
 	if err != nil {
 	if err != nil {
-		resultsSlice, err := session.query(newsql, args...)
+		resultsSlice, err := session.queryBytes(newsql, args...)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -40,7 +40,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
 			for _, data := range resultsSlice {
 			for _, data := range resultsSlice {
 				var id int64
 				var id int64
 				var pk core.PK = make([]interface{}, 0)
 				var pk core.PK = make([]interface{}, 0)
-				for _, col := range session.Statement.RefTable.PKColumns() {
+				for _, col := range pkColumns {
 					if v, ok := data[col.Name]; !ok {
 					if v, ok := data[col.Name]; !ok {
 						return errors.New("no id")
 						return errors.New("no id")
 					} else if col.SQLType.IsText() {
 					} else if col.SQLType.IsText() {
@@ -58,33 +58,30 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
 				ids = append(ids, pk)
 				ids = append(ids, pk)
 			}
 			}
 		}
 		}
-	} /*else {
-	    session.Engine.LogDebug("delete cache sql %v", newsql)
-	    cacher.DelIds(tableName, genSqlKey(newsql, args))
-	}*/
+	}
 
 
 	for _, id := range ids {
 	for _, id := range ids {
-		session.Engine.logger.Debug("[cacheDelete] delete cache obj", tableName, id)
+		session.engine.logger.Debug("[cacheDelete] delete cache obj:", tableName, id)
 		sid, err := id.ToString()
 		sid, err := id.ToString()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 		cacher.DelBean(tableName, sid)
 		cacher.DelBean(tableName, sid)
 	}
 	}
-	session.Engine.logger.Debug("[cacheDelete] clear cache sql", tableName)
+	session.engine.logger.Debug("[cacheDelete] clear cache table:", tableName)
 	cacher.ClearIds(tableName)
 	cacher.ClearIds(tableName)
 	return nil
 	return nil
 }
 }
 
 
 // Delete records, bean's non-empty fields are conditions
 // Delete records, bean's non-empty fields are conditions
 func (session *Session) Delete(bean interface{}) (int64, error) {
 func (session *Session) Delete(bean interface{}) (int64, error) {
-	defer session.resetStatement()
-	if session.IsAutoClose {
+	if session.isAutoClose {
 		defer session.Close()
 		defer session.Close()
 	}
 	}
 
 
-	session.Statement.setRefValue(rValue(bean))
-	var table = session.Statement.RefTable
+	if err := session.statement.setRefValue(rValue(bean)); err != nil {
+		return 0, err
+	}
 
 
 	// handle before delete processors
 	// handle before delete processors
 	for _, closure := range session.beforeClosures {
 	for _, closure := range session.beforeClosures {
@@ -96,13 +93,17 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 		processor.BeforeDelete()
 		processor.BeforeDelete()
 	}
 	}
 
 
-	// --
-	condSQL, condArgs, _ := session.Statement.genConds(bean)
-	if len(condSQL) == 0 && session.Statement.LimitN == 0 {
+	condSQL, condArgs, err := session.statement.genConds(bean)
+	if err != nil {
+		return 0, err
+	}
+	if len(condSQL) == 0 && session.statement.LimitN == 0 {
 		return 0, ErrNeedDeletedCond
 		return 0, ErrNeedDeletedCond
 	}
 	}
 
 
-	var tableName = session.Engine.Quote(session.Statement.TableName())
+	var tableNameNoQuote = session.statement.TableName()
+	var tableName = session.engine.Quote(tableNameNoQuote)
+	var table = session.statement.RefTable
 	var deleteSQL string
 	var deleteSQL string
 	if len(condSQL) > 0 {
 	if len(condSQL) > 0 {
 		deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
 		deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
@@ -111,15 +112,15 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 	}
 	}
 
 
 	var orderSQL string
 	var orderSQL string
-	if len(session.Statement.OrderStr) > 0 {
-		orderSQL += fmt.Sprintf(" ORDER BY %s", session.Statement.OrderStr)
+	if len(session.statement.OrderStr) > 0 {
+		orderSQL += fmt.Sprintf(" ORDER BY %s", session.statement.OrderStr)
 	}
 	}
-	if session.Statement.LimitN > 0 {
-		orderSQL += fmt.Sprintf(" LIMIT %d", session.Statement.LimitN)
+	if session.statement.LimitN > 0 {
+		orderSQL += fmt.Sprintf(" LIMIT %d", session.statement.LimitN)
 	}
 	}
 
 
 	if len(orderSQL) > 0 {
 	if len(orderSQL) > 0 {
-		switch session.Engine.dialect.DBType() {
+		switch session.engine.dialect.DBType() {
 		case core.POSTGRES:
 		case core.POSTGRES:
 			inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
 			inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
 			if len(condSQL) > 0 {
 			if len(condSQL) > 0 {
@@ -144,7 +145,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 
 
 	var realSQL string
 	var realSQL string
 	argsForCache := make([]interface{}, 0, len(condArgs)*2)
 	argsForCache := make([]interface{}, 0, len(condArgs)*2)
-	if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled
+	if session.statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled
 		realSQL = deleteSQL
 		realSQL = deleteSQL
 		copy(argsForCache, condArgs)
 		copy(argsForCache, condArgs)
 		argsForCache = append(condArgs, argsForCache...)
 		argsForCache = append(condArgs, argsForCache...)
@@ -155,12 +156,12 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 
 
 		deletedColumn := table.DeletedColumn()
 		deletedColumn := table.DeletedColumn()
 		realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
 		realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
-			session.Engine.Quote(session.Statement.TableName()),
-			session.Engine.Quote(deletedColumn.Name),
+			session.engine.Quote(session.statement.TableName()),
+			session.engine.Quote(deletedColumn.Name),
 			condSQL)
 			condSQL)
 
 
 		if len(orderSQL) > 0 {
 		if len(orderSQL) > 0 {
-			switch session.Engine.dialect.DBType() {
+			switch session.engine.dialect.DBType() {
 			case core.POSTGRES:
 			case core.POSTGRES:
 				inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
 				inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
 				if len(condSQL) > 0 {
 				if len(condSQL) > 0 {
@@ -183,12 +184,12 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 			}
 			}
 		}
 		}
 
 
-		// !oinume! Insert NowTime to the head of session.Statement.Params
+		// !oinume! Insert nowTime to the head of session.statement.Params
 		condArgs = append(condArgs, "")
 		condArgs = append(condArgs, "")
 		paramsLen := len(condArgs)
 		paramsLen := len(condArgs)
 		copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
 		copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
 
 
-		val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name)
+		val, t := session.engine.nowTime(deletedColumn)
 		condArgs[0] = val
 		condArgs[0] = val
 
 
 		var colName = deletedColumn.Name
 		var colName = deletedColumn.Name
@@ -198,17 +199,18 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 		})
 		})
 	}
 	}
 
 
-	if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
-		session.cacheDelete(deleteSQL, argsForCache...)
+	if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
+		session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...)
 	}
 	}
 
 
+	session.statement.RefTable = table
 	res, err := session.exec(realSQL, condArgs...)
 	res, err := session.exec(realSQL, condArgs...)
 	if err != nil {
 	if err != nil {
 		return 0, err
 		return 0, err
 	}
 	}
 
 
 	// handle after delete processors
 	// handle after delete processors
-	if session.IsAutoCommit {
+	if session.isAutoCommit {
 		for _, closure := range session.afterClosures {
 		for _, closure := range session.afterClosures {
 			closure(bean)
 			closure(bean)
 		}
 		}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä