From 4711bd9087f9074e15f388172d56626f03f83952 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Mon, 27 Sep 2021 11:15:34 +0800 Subject: [PATCH] fix(region): read only capability --- go.mod | 4 +- go.sum | 8 +- pkg/cloudprovider/consts.go | 2 + pkg/compute/models/capabilities.go | 189 ++++++++++++----- pkg/compute/models/cloudaccounts.go | 81 ++++---- pkg/compute/models/cloudprovidercapacities.go | 1 + pkg/multicloud/aliyun/aliyun.go | 6 +- pkg/multicloud/aws/aws.go | 4 +- pkg/multicloud/azure/azure.go | 10 +- pkg/multicloud/cloudpods/cloudpods.go | 2 +- pkg/multicloud/ecloud/client.go | 4 +- pkg/multicloud/jdcloud/provider/provider.go | 10 +- pkg/multicloud/jdcloud/region.go | 5 +- pkg/multicloud/qcloud/qcloud.go | 8 +- vendor/modules.txt | 4 +- .../yunion.io/x/pkg/util/compare/compare.go | 23 ++- vendor/yunion.io/x/pkg/utils/tags.go | 18 ++ vendor/yunion.io/x/sqlchemy/README.md | 192 ++++++++++-------- vendor/yunion.io/x/sqlchemy/case.go | 6 + vendor/yunion.io/x/sqlchemy/column.go | 186 ++++++++++++++--- vendor/yunion.io/x/sqlchemy/conditions.go | 97 ++++++++- vendor/yunion.io/x/sqlchemy/const.go | 83 +++++--- vendor/yunion.io/x/sqlchemy/constraint.go | 14 +- vendor/yunion.io/x/sqlchemy/debug.go | 32 ++- vendor/yunion.io/x/sqlchemy/errors.go | 31 ++- vendor/yunion.io/x/sqlchemy/fetch.go | 10 +- vendor/yunion.io/x/sqlchemy/field_update.go | 2 + vendor/yunion.io/x/sqlchemy/filter.go | 158 ++++++++------ vendor/yunion.io/x/sqlchemy/functions.go | 54 ++++- vendor/yunion.io/x/sqlchemy/inc.go | 9 +- vendor/yunion.io/x/sqlchemy/index.go | 20 +- vendor/yunion.io/x/sqlchemy/insert.go | 6 +- vendor/yunion.io/x/sqlchemy/parser.go | 17 +- vendor/yunion.io/x/sqlchemy/query.go | 184 ++++++++++++----- vendor/yunion.io/x/sqlchemy/rawquery.go | 10 + vendor/yunion.io/x/sqlchemy/reflect.go | 3 +- vendor/yunion.io/x/sqlchemy/sql.go | 6 + vendor/yunion.io/x/sqlchemy/sync.go | 42 ++-- vendor/yunion.io/x/sqlchemy/table.go | 59 +++++- vendor/yunion.io/x/sqlchemy/tablealias.go | 2 +- vendor/yunion.io/x/sqlchemy/union.go | 33 ++- vendor/yunion.io/x/sqlchemy/update.go | 8 + 42 files changed, 1167 insertions(+), 476 deletions(-) diff --git a/go.mod b/go.mod index 3f18a25516..99ed78afd9 100644 --- a/go.mod +++ b/go.mod @@ -153,9 +153,9 @@ require ( yunion.io/x/jsonutils v0.0.0-20210709075951-798a67800349 yunion.io/x/log v0.0.0-20201210064738-43181789dc74 yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7 - yunion.io/x/pkg v0.0.0-20210721081124-55078288ca4c + yunion.io/x/pkg v0.0.0-20210918114143-ce839f862c5f yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e - yunion.io/x/sqlchemy v0.0.0-20210619142628-653684d2c4f8 + yunion.io/x/sqlchemy v0.0.0-20210918113031-c1c107f37ada yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce ) diff --git a/go.sum b/go.sum index 6bcbbe69c7..02e8949799 100644 --- a/go.sum +++ b/go.sum @@ -923,11 +923,11 @@ yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7/go.mod h1:0vLkNEhlmA64HViPB yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= -yunion.io/x/pkg v0.0.0-20210721081124-55078288ca4c h1:/ejlt78/0xod4bdkEThUuYsQ7vLajd48wpL2GMMSaXs= -yunion.io/x/pkg v0.0.0-20210721081124-55078288ca4c/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= +yunion.io/x/pkg v0.0.0-20210918114143-ce839f862c5f h1:maply0f3AsvoXqkpTKkb6/tZFbRAT/rdkk1Cw3FkoB8= +yunion.io/x/pkg v0.0.0-20210918114143-ce839f862c5f/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo= yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo= -yunion.io/x/sqlchemy v0.0.0-20210619142628-653684d2c4f8 h1:+kywPlOttXAZvbxsVqR73B5GbSVgG3Xdo4socLWRkcg= -yunion.io/x/sqlchemy v0.0.0-20210619142628-653684d2c4f8/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI= +yunion.io/x/sqlchemy v0.0.0-20210918113031-c1c107f37ada h1:ZIhSSwFHfnzPMKSyiN0nkGwvC6RUeuCIAnWuTtDS900= +yunion.io/x/sqlchemy v0.0.0-20210918113031-c1c107f37ada/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI= yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce h1:kU8xE7O5uZ1GSJVMZHoJ+jrNL7csUQHYGyAPW9QfNpE= yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng= diff --git a/pkg/cloudprovider/consts.go b/pkg/cloudprovider/consts.go index 039faf9759..e8120b1566 100644 --- a/pkg/cloudprovider/consts.go +++ b/pkg/cloudprovider/consts.go @@ -41,6 +41,8 @@ const ( ) const ( + READ_ONLY_SUFFIX = ".readonly" + CLOUD_CAPABILITY_PROJECT = "project" CLOUD_CAPABILITY_COMPUTE = "compute" CLOUD_CAPABILITY_NETWORK = "network" diff --git a/pkg/compute/models/capabilities.go b/pkg/compute/models/capabilities.go index 51858abbe9..85cba31c2e 100644 --- a/pkg/compute/models/capabilities.go +++ b/pkg/compute/models/capabilities.go @@ -18,6 +18,7 @@ import ( "context" "database/sql" "fmt" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -37,17 +38,20 @@ import ( ) type SCapabilities struct { - Hypervisors []string `json:",allowempty"` - Brands []string `json:",allowempty"` - DisabledBrands []string `json:",allowempty"` - ComputeEngineBrands []string `json:",allowempty"` - DisabledComputeEngineBrands []string `json:",allowempty"` - RdsEngineBrands []string `json:",allowempty"` - RedisEngineBrands []string `json:",allowempty"` - LoadbalancerEngineBrands []string `json:",allowempty"` - DisabledRdsEngineBrands []string `json:",allowempty"` - CloudIdBrands []string `json:",allowempty"` - DisabledCloudIdBrands []string `json:",allowempty"` + Hypervisors []string `json:",allowempty"` + + Brands []string `json:",allowempty"` + DisabledBrands []string `json:",allowempty"` + ComputeEngineBrands []string `json:",allowempty"` + DisabledComputeEngineBrands []string `json:",allowempty"` + RdsEngineBrands []string `json:",allowempty"` + DisabledRdsEngineBrands []string `json:",allowempty"` + RedisEngineBrands []string `json:",allowempty"` + DisabledRedisEngineBrands []string `json:",allowempty"` + LoadbalancerEngineBrands []string `json:",allowempty"` + DisabledLoadbalancerEngineBrands []string `json:",allowempty"` + CloudIdBrands []string `json:",allowempty"` + DisabledCloudIdBrands []string `json:",allowempty"` // 支持SAML 2.0 SamlAuthBrands []string `json:",allowempty"` DisabledSamlAuthBrands []string `json:",allowempty"` @@ -60,21 +64,53 @@ type SCapabilities struct { CdnBrands []string `json:",allowempty"` DisabledCdnBrands []string `json:",allowempty"` PublicIpBrands []string `json:",allowempty"` + DisabledPublicIpBrands []string `json:",allowempty"` NetworkManageBrands []string `json:",allowempty"` DisabledNetworkManageBrands []string `json:",allowempty"` ObjectStorageBrands []string `json:",allowempty"` DisabledObjectStorageBrands []string `json:",allowempty"` - ResourceTypes []string `json:",allowempty"` - StorageTypes []string `json:",allowempty"` // going to remove on 2.14 - DataStorageTypes []string `json:",allowempty"` // going to remove on 2.14 - GPUModels []string `json:",allowempty"` - HostCpuArchs []string `json:",allowempty"` // x86_64 aarch64 - MinNicCount int - MaxNicCount int - MinDataDiskCount int - MaxDataDiskCount int - SchedPolicySupport bool - Usable bool + + ReadOnlyBrands []string `json:",allowempty"` + ReadOnlyDisabledBrands []string `json:",allowempty"` + ReadOnlyComputeEngineBrands []string `json:",allowempty"` + ReadOnlyDisabledComputeEngineBrands []string `json:",allowempty"` + ReadOnlyRdsEngineBrands []string `json:",allowempty"` + ReadOnlyDisabledRdsEngineBrands []string `json:",allowempty"` + ReadOnlyRedisEngineBrands []string `json:",allowempty"` + ReadOnlyDisabledRedisEngineBrands []string `json:",allowempty"` + ReadOnlyLoadbalancerEngineBrands []string `json:",allowempty"` + ReadOnlyDisabledLoadbalancerEngineBrands []string `json:",allowempty"` + ReadOnlyCloudIdBrands []string `json:",allowempty"` + ReadOnlyDisabledCloudIdBrands []string `json:",allowempty"` + // 支持SAML 2.0 + ReadOnlySamlAuthBrands []string `json:",allowempty"` + ReadOnlyDisabledSamlAuthBrands []string `json:",allowempty"` + ReadOnlyNatBrands []string `json:",allowempty"` + ReadOnlyDisabledNatBrands []string `json:",allowempty"` + ReadOnlyNasBrands []string `json:",allowempty"` + ReadOnlyDisabledNasBrands []string `json:",allowempty"` + ReadOnlyWafBrands []string `json:",allowempty"` + ReadOnlyDisabledWafBrands []string `json:",allowempty"` + ReadOnlyCdnBrands []string `json:",allowempty"` + ReadOnlyDisabledCdnBrands []string `json:",allowempty"` + ReadOnlyPublicIpBrands []string `json:",allowempty"` + ReadOnlyDisabledPublicIpBrands []string `json:",allowempty"` + ReadOnlyNetworkManageBrands []string `json:",allowempty"` + ReadOnlyDisabledNetworkManageBrands []string `json:",allowempty"` + ReadOnlyObjectStorageBrands []string `json:",allowempty"` + ReadOnlyDisabledObjectStorageBrands []string `json:",allowempty"` + + ResourceTypes []string `json:",allowempty"` + StorageTypes []string `json:",allowempty"` // going to remove on 2.14 + DataStorageTypes []string `json:",allowempty"` // going to remove on 2.14 + GPUModels []string `json:",allowempty"` + HostCpuArchs []string `json:",allowempty"` // x86_64 aarch64 + MinNicCount int + MaxNicCount int + MinDataDiskCount int + MaxDataDiskCount int + SchedPolicySupport bool + Usable bool // Deprecated PublicNetworkCount int @@ -295,20 +331,24 @@ func getDBInstanceInfo(region *SCloudregion, zone *SZone) map[string]map[string] // set all brands, compute engine brands, network manage brands, object storage brands func getBrands(region *SCloudregion, zone *SZone, domainId string, capa *SCapabilities) { - capa.Brands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, "") - capa.ComputeEngineBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_COMPUTE) - capa.RdsEngineBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_RDS) - capa.RedisEngineBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_CACHE) - capa.NetworkManageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_NETWORK) - capa.ObjectStorageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE) - capa.CloudIdBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_CLOUDID) - capa.PublicIpBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP) - capa.LoadbalancerEngineBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_LOADBALANCER) - capa.SamlAuthBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_SAML_AUTH) - capa.NatBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_NAT) - capa.NasBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_NAS) - capa.WafBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_WAF) - capa.CdnBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_CDN) + brands, err := CloudaccountManager.getBrandsOfCapability(region, zone, domainId) + if err != nil { + log.Errorf("getBrandsOfCapability: %v", err) + } + brandMaps := map[string]map[string]bool{} + for _, brand := range brands { + _, ok := brandMaps[brand.Brand] + if !ok { + brandMaps[brand.Brand] = map[string]bool{} + } + _, ok = brandMaps[brand.Brand][brand.Capability] + if !ok { + brandMaps[brand.Brand][brand.Capability] = brand.Enabled + } + if brand.Enabled { + brandMaps[brand.Brand][brand.Capability] = true + } + } if utils.IsInStringArray(api.HYPERVISOR_KVM, capa.Hypervisors) || utils.IsInStringArray(api.HYPERVISOR_BAREMETAL, capa.Hypervisors) { capa.Brands = append(capa.Brands, api.ONECLOUD_BRAND_ONECLOUD) @@ -319,19 +359,72 @@ func getBrands(region *SCloudregion, zone *SZone, domainId string, capa *SCapabi capa.LoadbalancerEngineBrands = append(capa.LoadbalancerEngineBrands, api.ONECLOUD_BRAND_ONECLOUD) } - capa.NetworkManageBrands = append(capa.NetworkManageBrands, api.ONECLOUD_BRAND_ONECLOUD) + var appendBrand = func(enabled *[]string, disabled *[]string, readOnlyEnabled *[]string, readOnlyDisabled *[]string, brand, capability string, isEnable, readOnly bool) { + if readOnly { + if isEnable { + *readOnlyEnabled = append(*readOnlyEnabled, brand) + if capability == cloudprovider.CLOUD_CAPABILITY_COMPUTE && !utils.IsInStringArray(brand, capa.ReadOnlyBrands) { + capa.ReadOnlyBrands = append(capa.ReadOnlyBrands, brand) + } + } else { + *readOnlyDisabled = append(*readOnlyDisabled, brand) + if capability == cloudprovider.CLOUD_CAPABILITY_COMPUTE && !utils.IsInStringArray(brand, capa.ReadOnlyDisabledBrands) { + capa.ReadOnlyDisabledBrands = append(capa.ReadOnlyDisabledBrands, brand) + } + } + } else { + if isEnable { + *enabled = append(*enabled, brand) + if capability == cloudprovider.CLOUD_CAPABILITY_COMPUTE && !utils.IsInStringArray(brand, capa.Brands) { + capa.Brands = append(capa.Brands, brand) + } + } else { + *disabled = append(*disabled, brand) + if capability == cloudprovider.CLOUD_CAPABILITY_COMPUTE && !utils.IsInStringArray(brand, capa.DisabledBrands) { + capa.DisabledBrands = append(capa.DisabledBrands, brand) + } + } + } + } - capa.DisabledBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, "") - capa.DisabledComputeEngineBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_COMPUTE) - capa.DisabledRdsEngineBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_RDS) - capa.DisabledNetworkManageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_NETWORK) - capa.DisabledObjectStorageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE) - capa.DisabledCloudIdBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_CLOUDID) - capa.DisabledSamlAuthBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_SAML_AUTH) - capa.DisabledNatBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_NAT) - capa.DisabledNasBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_NAS) - capa.DisabledNasBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_WAF) - capa.DisabledCdnBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.False, cloudprovider.CLOUD_CAPABILITY_CDN) + for brand, info := range brandMaps { + for capability, enabled := range info { + readOnly := false + if strings.HasSuffix(capability, cloudprovider.READ_ONLY_SUFFIX) { + readOnly = true + capability = strings.TrimSuffix(capability, cloudprovider.READ_ONLY_SUFFIX) + } + switch capability { + case cloudprovider.CLOUD_CAPABILITY_COMPUTE: + appendBrand(&capa.ComputeEngineBrands, &capa.DisabledComputeEngineBrands, &capa.ReadOnlyComputeEngineBrands, &capa.ReadOnlyDisabledComputeEngineBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_RDS: + appendBrand(&capa.RdsEngineBrands, &capa.DisabledRdsEngineBrands, &capa.ReadOnlyRdsEngineBrands, &capa.ReadOnlyDisabledRdsEngineBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_CACHE: + appendBrand(&capa.RedisEngineBrands, &capa.DisabledRedisEngineBrands, &capa.ReadOnlyRedisEngineBrands, &capa.ReadOnlyDisabledRedisEngineBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_NETWORK: + appendBrand(&capa.NetworkManageBrands, &capa.DisabledNetworkManageBrands, &capa.ReadOnlyNetworkManageBrands, &capa.ReadOnlyDisabledNetworkManageBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE: + appendBrand(&capa.ObjectStorageBrands, &capa.DisabledObjectStorageBrands, &capa.ReadOnlyObjectStorageBrands, &capa.ReadOnlyDisabledObjectStorageBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_CLOUDID: + appendBrand(&capa.CloudIdBrands, &capa.DisabledCloudIdBrands, &capa.ReadOnlyCloudIdBrands, &capa.ReadOnlyDisabledCloudIdBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP: + appendBrand(&capa.PublicIpBrands, &capa.DisabledPublicIpBrands, &capa.ReadOnlyPublicIpBrands, &capa.ReadOnlyDisabledPublicIpBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_LOADBALANCER: + appendBrand(&capa.LoadbalancerEngineBrands, &capa.DisabledLoadbalancerEngineBrands, &capa.ReadOnlyLoadbalancerEngineBrands, &capa.ReadOnlyDisabledLoadbalancerEngineBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_SAML_AUTH: + appendBrand(&capa.SamlAuthBrands, &capa.DisabledSamlAuthBrands, &capa.ReadOnlySamlAuthBrands, &capa.ReadOnlyDisabledSamlAuthBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_NAT: + appendBrand(&capa.NatBrands, &capa.DisabledNatBrands, &capa.ReadOnlyNatBrands, &capa.ReadOnlyDisabledNatBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_NAS: + appendBrand(&capa.NasBrands, &capa.DisabledNasBrands, &capa.ReadOnlyNasBrands, &capa.ReadOnlyDisabledNasBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_WAF: + appendBrand(&capa.WafBrands, &capa.DisabledWafBrands, &capa.ReadOnlyWafBrands, &capa.ReadOnlyDisabledWafBrands, brand, capability, enabled, readOnly) + case cloudprovider.CLOUD_CAPABILITY_CDN: + appendBrand(&capa.CdnBrands, &capa.DisabledCdnBrands, &capa.ReadOnlyCdnBrands, &capa.ReadOnlyDisabledCdnBrands, brand, capability, enabled, readOnly) + default: + } + } + } return } diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index ed53506dc6..ac872398f4 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -2509,16 +2509,9 @@ func (manager *SCloudaccountManager) queryCloudAccountByCapability(region *SClou providers := CloudproviderManager.Query().SubQuery() q := manager.Query() q = q.Join(providers, sqlchemy.Equals(q.Field("id"), providers.Field("cloudaccount_id"))) - if len(capability) > 0 { - cloudproviderCapabilities := CloudproviderCapabilityManager.Query().SubQuery() - q = q.Join(cloudproviderCapabilities, sqlchemy.Equals(providers.Field("id"), cloudproviderCapabilities.Field("cloudprovider_id"))) - q = q.Filter(sqlchemy.Equals(cloudproviderCapabilities.Field("capability"), capability)) - } - if enabled.IsTrue() { - q = q.IsTrue("enabled") - } else if enabled.IsFalse() { - q = q.IsFalse("enabled") - } + cloudproviderCapabilities := CloudproviderCapabilityManager.Query().SubQuery() + q = q.Join(cloudproviderCapabilities, sqlchemy.Equals(providers.Field("id"), cloudproviderCapabilities.Field("cloudprovider_id"))) + q = q.Filter(sqlchemy.Equals(cloudproviderCapabilities.Field("capability"), capability)) if zone != nil { region, _ = zone.GetRegion() } @@ -2529,42 +2522,54 @@ func (manager *SCloudaccountManager) queryCloudAccountByCapability(region *SClou } if len(domainId) > 0 { q = manager.filterByDomainId(q, domainId) - /*q = q.Filter(sqlchemy.OR( - sqlchemy.AND( - sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN), - sqlchemy.Equals(q.Field("domain_id"), domainId), - ), - sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM), - sqlchemy.AND( - sqlchemy.Equals(q.Field("share_mode"), api.CLOUD_ACCOUNT_SHARE_MODE_PROVIDER_DOMAIN), - sqlchemy.Equals(providers.Field("domain_id"), domainId), - ), - ))*/ } return q } -func (manager *SCloudaccountManager) getBrandsOfCapability(region *SCloudregion, zone *SZone, domainId string, enabled tristate.TriState, capability string) ([]string, error) { - subq := manager.queryCloudAccountByCapability(region, zone, domainId, enabled, capability).SubQuery() - q := subq.Query(subq.Field("brand")).Distinct() - rows, err := q.Rows() - if err != nil { - if errors.Cause(err) != sql.ErrNoRows { - return nil, errors.Wrap(err, "rows") - } - return []string{}, nil +type sBrandCapability struct { + Brand string + Enabled bool + Capability string +} + +func (manager *SCloudaccountManager) getBrandsOfCapability(region *SCloudregion, zone *SZone, domainId string) ([]sBrandCapability, error) { + accounts := manager.Query("id", "enabled", "brand") + if len(domainId) > 0 { + accounts = manager.filterByDomainId(accounts, domainId) } - ret := make([]string, 0) - defer rows.Close() - for rows.Next() { - var brand string - err := rows.Scan(&brand) + + accountSQ := accounts.SubQuery() + providers := CloudproviderManager.Query().SubQuery() + q := CloudproviderCapabilityManager.Query("capability") + + q.AppendField(accountSQ.Field("enabled")) + q.AppendField(accountSQ.Field("brand")) + + q = q.Join(providers, sqlchemy.Equals(q.Field("cloudprovider_id"), providers.Field("id"))) + q = q.Join(accountSQ, sqlchemy.Equals(providers.Field("cloudaccount_id"), accountSQ.Field("id"))) + + if zone != nil { + var err error + region, err = zone.GetRegion() if err != nil { - return nil, errors.Wrap(err, "rows.Scan") + return nil, errors.Wrapf(err, "GetRegion") } - ret = append(ret, brand) } - return ret, nil + if region != nil { + providerregions := CloudproviderRegionManager.Query().SubQuery() + q = q.Join(providerregions, sqlchemy.Equals(q.Field("cloudprovider_id"), providerregions.Field("cloudprovider_id"))).Filter( + sqlchemy.Equals(providerregions.Field("cloudregion_id"), region.Id), + ) + } + + q = q.Distinct() + + result := []sBrandCapability{} + err := q.All(&result) + if err != nil { + return nil, errors.Wrapf(err, "q.All") + } + return result, nil } func (account *SCloudaccount) getAccountShareInfo() apis.SAccountShareInfo { diff --git a/pkg/compute/models/cloudprovidercapacities.go b/pkg/compute/models/cloudprovidercapacities.go index cda6762c1f..5ae21a3f8f 100644 --- a/pkg/compute/models/cloudprovidercapacities.go +++ b/pkg/compute/models/cloudprovidercapacities.go @@ -70,6 +70,7 @@ func (manager *SCloudproviderCapabilityManager) setRegionCapabilities(ctx contex if err != nil { return errors.Wrap(err, "manager.getCapabilities") } + oldCapas := stringutils2.NewSortedStrings(oldCapabilities) newCapas := stringutils2.NewSortedStrings(capabilities) deleted, _, added := stringutils2.Split(oldCapas, newCapas) diff --git a/pkg/multicloud/aliyun/aliyun.go b/pkg/multicloud/aliyun/aliyun.go index d5ae484e66..39432fcc81 100644 --- a/pkg/multicloud/aliyun/aliyun.go +++ b/pkg/multicloud/aliyun/aliyun.go @@ -689,9 +689,9 @@ func (region *SAliyunClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_NAT, cloudprovider.CLOUD_CAPABILITY_NAS, cloudprovider.CLOUD_CAPABILITY_WAF, - cloudprovider.CLOUD_CAPABILITY_MONGO_DB, - cloudprovider.CLOUD_CAPABILITY_ES, - cloudprovider.CLOUD_CAPABILITY_KAFKA, + cloudprovider.CLOUD_CAPABILITY_MONGO_DB + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_ES + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_KAFKA + cloudprovider.READ_ONLY_SUFFIX, } return caps } diff --git a/pkg/multicloud/aws/aws.go b/pkg/multicloud/aws/aws.go index b64990bb5b..05071d8d9d 100644 --- a/pkg/multicloud/aws/aws.go +++ b/pkg/multicloud/aws/aws.go @@ -600,8 +600,8 @@ func (self *SAwsClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_NETWORK, cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, - cloudprovider.CLOUD_CAPABILITY_RDS, - cloudprovider.CLOUD_CAPABILITY_CACHE, + cloudprovider.CLOUD_CAPABILITY_RDS + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_CACHE + cloudprovider.READ_ONLY_SUFFIX, // cloudprovider.CLOUD_CAPABILITY_EVENT, cloudprovider.CLOUD_CAPABILITY_CLOUDID, cloudprovider.CLOUD_CAPABILITY_DNSZONE, diff --git a/pkg/multicloud/azure/azure.go b/pkg/multicloud/azure/azure.go index c20c6e86bd..0a59426505 100644 --- a/pkg/multicloud/azure/azure.go +++ b/pkg/multicloud/azure/azure.go @@ -985,16 +985,16 @@ func (self *SAzureClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_PROJECT, cloudprovider.CLOUD_CAPABILITY_COMPUTE, cloudprovider.CLOUD_CAPABILITY_NETWORK, - cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, + cloudprovider.CLOUD_CAPABILITY_LOADBALANCER + cloudprovider.READ_ONLY_SUFFIX, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, - cloudprovider.CLOUD_CAPABILITY_RDS, - cloudprovider.CLOUD_CAPABILITY_CACHE, + cloudprovider.CLOUD_CAPABILITY_RDS + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_CACHE + cloudprovider.READ_ONLY_SUFFIX, cloudprovider.CLOUD_CAPABILITY_EVENT, cloudprovider.CLOUD_CAPABILITY_CLOUDID, cloudprovider.CLOUD_CAPABILITY_SAML_AUTH, cloudprovider.CLOUD_CAPABILITY_WAF, - cloudprovider.CLOUD_CAPABILITY_CACHE, - cloudprovider.CLOUD_CAPABILITY_APP, + cloudprovider.CLOUD_CAPABILITY_CACHE + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_APP + cloudprovider.READ_ONLY_SUFFIX, } return caps } diff --git a/pkg/multicloud/cloudpods/cloudpods.go b/pkg/multicloud/cloudpods/cloudpods.go index 1a94403ba6..04a5b83092 100644 --- a/pkg/multicloud/cloudpods/cloudpods.go +++ b/pkg/multicloud/cloudpods/cloudpods.go @@ -237,7 +237,7 @@ func (self *SCloudpodsClient) GetRegions() ([]SRegion, error) { func (self *SCloudpodsClient) GetCapabilities() []string { return []string{ - cloudprovider.CLOUD_CAPABILITY_PROJECT, + cloudprovider.CLOUD_CAPABILITY_PROJECT + cloudprovider.READ_ONLY_SUFFIX, cloudprovider.CLOUD_CAPABILITY_COMPUTE, cloudprovider.CLOUD_CAPABILITY_NETWORK, } diff --git a/pkg/multicloud/ecloud/client.go b/pkg/multicloud/ecloud/client.go index df158a758b..19277f1069 100644 --- a/pkg/multicloud/ecloud/client.go +++ b/pkg/multicloud/ecloud/client.go @@ -144,8 +144,8 @@ func (ec *SEcloudClient) GetRegionById(id string) (*SRegion, error) { func (ec *SEcloudClient) GetCapabilities() []string { caps := []string{ - cloudprovider.CLOUD_CAPABILITY_COMPUTE, - cloudprovider.CLOUD_CAPABILITY_NETWORK, + cloudprovider.CLOUD_CAPABILITY_COMPUTE + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_NETWORK + cloudprovider.READ_ONLY_SUFFIX, } return caps } diff --git a/pkg/multicloud/jdcloud/provider/provider.go b/pkg/multicloud/jdcloud/provider/provider.go index f05f56e549..164df52fdd 100644 --- a/pkg/multicloud/jdcloud/provider/provider.go +++ b/pkg/multicloud/jdcloud/provider/provider.go @@ -211,10 +211,14 @@ func (p *SJdcloudProvider) GetCloudRegionExternalIdPrefix() string { } func (p *SJdcloudProvider) GetCapabilities() []string { + iRegions := p.GetIRegions() + if len(iRegions) > 0 { + return iRegions[0].GetCapabilities() + } caps := []string{ - cloudprovider.CLOUD_CAPABILITY_COMPUTE, - cloudprovider.CLOUD_CAPABILITY_NETWORK, - cloudprovider.CLOUD_CAPABILITY_RDS, + cloudprovider.CLOUD_CAPABILITY_COMPUTE + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_NETWORK + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_RDS + cloudprovider.READ_ONLY_SUFFIX, } return caps } diff --git a/pkg/multicloud/jdcloud/region.go b/pkg/multicloud/jdcloud/region.go index 114ec31193..71508c5485 100644 --- a/pkg/multicloud/jdcloud/region.go +++ b/pkg/multicloud/jdcloud/region.go @@ -337,7 +337,8 @@ func (r *SRegion) GetProvider() string { func (r *SRegion) GetCapabilities() []string { return []string{ - cloudprovider.CLOUD_CAPABILITY_COMPUTE, - cloudprovider.CLOUD_CAPABILITY_NETWORK, + cloudprovider.CLOUD_CAPABILITY_COMPUTE + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_NETWORK + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_RDS + cloudprovider.READ_ONLY_SUFFIX, } } diff --git a/pkg/multicloud/qcloud/qcloud.go b/pkg/multicloud/qcloud/qcloud.go index 32fd908553..6a9cebfd57 100644 --- a/pkg/multicloud/qcloud/qcloud.go +++ b/pkg/multicloud/qcloud/qcloud.go @@ -1097,10 +1097,10 @@ func (self *SQcloudClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP, cloudprovider.CLOUD_CAPABILITY_INTERVPCNETWORK, cloudprovider.CLOUD_CAPABILITY_SAML_AUTH, - cloudprovider.CLOUD_CAPABILITY_MONGO_DB, - cloudprovider.CLOUD_CAPABILITY_ES, - cloudprovider.CLOUD_CAPABILITY_KAFKA, - cloudprovider.CLOUD_CAPABILITY_CDN, + cloudprovider.CLOUD_CAPABILITY_MONGO_DB + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_ES + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_KAFKA + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_CDN + cloudprovider.READ_ONLY_SUFFIX, } return caps } diff --git a/vendor/modules.txt b/vendor/modules.txt index 036cb81f70..09039c13d7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1177,7 +1177,7 @@ yunion.io/x/log/hooks yunion.io/x/ovsdb/cli_util yunion.io/x/ovsdb/schema/ovn_nb yunion.io/x/ovsdb/types -# yunion.io/x/pkg v0.0.0-20210721081124-55078288ca4c +# yunion.io/x/pkg v0.0.0-20210918114143-ce839f862c5f yunion.io/x/pkg/errors yunion.io/x/pkg/gotypes yunion.io/x/pkg/prettytable @@ -1211,7 +1211,7 @@ yunion.io/x/pkg/util/workqueue yunion.io/x/pkg/utils # yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e yunion.io/x/s3cli -# yunion.io/x/sqlchemy v0.0.0-20210619142628-653684d2c4f8 +# yunion.io/x/sqlchemy v0.0.0-20210918113031-c1c107f37ada yunion.io/x/sqlchemy # yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce yunion.io/x/structarg diff --git a/vendor/yunion.io/x/pkg/util/compare/compare.go b/vendor/yunion.io/x/pkg/util/compare/compare.go index fb64a78af8..1351de1614 100644 --- a/vendor/yunion.io/x/pkg/util/compare/compare.go +++ b/vendor/yunion.io/x/pkg/util/compare/compare.go @@ -69,12 +69,19 @@ func valueSet2Array(dbSet interface{}, field string) ([]valueElement, error) { return ret, nil } -func CompareSets(dbSet interface{}, extSet interface{}, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}) error { - dbSetArray, err := valueSet2Array(dbSet, "GetExternalId") +type SCompareSet struct { + DBFunc string + DBSet interface{} + ExtFunc string + ExtSet interface{} +} + +func CompareSetsFunc(cs SCompareSet, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}) error { + dbSetArray, err := valueSet2Array(cs.DBSet, cs.DBFunc) if err != nil { return err } - extSetArray, err := valueSet2Array(extSet, "GetGlobalId") + extSetArray, err := valueSet2Array(cs.ExtSet, cs.ExtFunc) if err != nil { return err } @@ -135,4 +142,14 @@ func CompareSets(dbSet interface{}, extSet interface{}, removed interface{}, com } } return nil + +} + +func CompareSets(dbSet interface{}, extSet interface{}, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}) error { + return CompareSetsFunc(SCompareSet{ + DBFunc: "GetExternalId", + DBSet: dbSet, + ExtFunc: "GetGlobalId", + ExtSet: extSet, + }, removed, commonDB, commonExt, added) } diff --git a/vendor/yunion.io/x/pkg/utils/tags.go b/vendor/yunion.io/x/pkg/utils/tags.go index 92c0ba88cb..9926630cc5 100644 --- a/vendor/yunion.io/x/pkg/utils/tags.go +++ b/vendor/yunion.io/x/pkg/utils/tags.go @@ -148,3 +148,21 @@ func TagPop(m map[string]string, key string) (map[string]string, string, bool) { } return m, val, ok } + +func SplitCSV(csv string) []string { + offset := 0 + words := make([]string, 0) + str := []byte(csv) + for offset < len(str) { + var word string + word, offset = _findWord(str, offset, ",\r\n") + words = append(words, word) + if offset < len(str) { + offset++ + if offset >= len(str) { + words = append(words, "") + } + } + } + return words +} diff --git a/vendor/yunion.io/x/sqlchemy/README.md b/vendor/yunion.io/x/sqlchemy/README.md index 8441d66e6e..2561351a95 100644 --- a/vendor/yunion.io/x/sqlchemy/README.md +++ b/vendor/yunion.io/x/sqlchemy/README.md @@ -1,6 +1,7 @@ # sqlchemy [![CircleCI](https://circleci.com/gh/yunionio/sqlchemy.svg?style=svg)](https://circleci.com/gh/yunionio/sqlchemy) +[![codecov](https://codecov.io/gh/yunionio/sqlchemy/branch/master/graph/badge.svg?token=K8cSYZzLbc)](https://codecov.io/gh/yunionio/sqlchemy) [![Go Report Card](https://goreportcard.com/badge/github.com/yunionio/sqlchemy)](https://goreportcard.com/report/github.com/yunionio/sqlchemy) A lightweight golang ORM library inspired by python sqlalchemy. @@ -20,134 +21,151 @@ Quick Examples Table schema is defined by struct field tags - type TestTable struct { - Id string `primary:"true" width:"128" charset:"ascii" nullable:"false"` - Name string `width:"64" charset:"utf8" index:"true"` - Gender string `width:"10" charset:"ascii"` - Age uint8 `default:"18"` - Info jsonutils.JSONObject `nullable:"false"` - Compond *SCompondStruct `width:1024` - CreatedAt time.Time `nullable:"false" created_at:"true"` - UpdatedAt time.Time `nullable:"false" updated_at:"true"` - Version int `default:"0" nullable:"false" auto_version:"true"` - DeletedAt time.Time `` - Deleted bool `nullable:"false" default:"false"` - Notes string `default:"default notes"` - } +```go +type TestTable struct { + Id string `primary:"true" width:"128" charset:"ascii" nullable:"false"` + Name string `width:"64" charset:"utf8" index:"true"` + Gender string `width:"10" charset:"ascii"` + Age uint8 `default:"18"` + Info jsonutils.JSONObject `nullable:"false"` + Compond *SCompondStruct `width:1024` + CreatedAt time.Time `nullable:"false" created_at:"true"` + UpdatedAt time.Time `nullable:"false" updated_at:"true"` + Version int `default:"0" nullable:"false" auto_version:"true"` + DeletedAt time.Time `` + Deleted bool `nullable:"false" default:"false"` + Notes string `default:"default notes"` +} +```` ## Table initialization Create a table from a struct schema - tablespec := sqlchemy.NewTableSpecFromStruct(TestTable{}, "testtable") +```go +tablespec := sqlchemy.NewTableSpecFromStruct(TestTable{}, "testtable") +``` Check whether table schema definition is consistent with schema in database. - if !tablespec.CheckSync() { - log.Fatalf("table not in sync") - } +```go +if !tablespec.CheckSync() { + log.Fatalf("table not in sync") +} +``` Synchronize database table schema and make it consistent with the struct defintion. - err := tablespec.Sync() - if err != nil { - log.Fataf("synchronize table schema failed: %s", er) - } +```go +err := tablespec.Sync() +if err != nil { + log.Fataf("synchronize table schema failed: %s", er) +} +``` ## Query ### Construct query - ti := tablespec.Instance() +```go +ti := tablespec.Instance() - // select * from testtable - q := ti.Query() +// select * from testtable +q := ti.Query() - // select * from testtable where id = '981b10ed-b6f9-4120-8a77-a3b03e343143' - // query by field name, in which the name is unique in the query - q := ti.Query().Equals("id", "981b10ed-b6f9-4120-8a77-a3b03e343143") +// select * from testtable where id = '981b10ed-b6f9-4120-8a77-a3b03e343143' +// query by field name, in which the name is unique in the query +q := ti.Query().Equals("id", "981b10ed-b6f9-4120-8a77-a3b03e343143") - // query by field instance, in which the field name might be ambiguous - q := ti.Query().Filter(sqlchemy.Equals(ti.Field("id"), "981b10ed-b6f9-4120-8a77-a3b03e343143")) +// query by field instance, in which the field name might be ambiguous +q := ti.Query().Filter(sqlchemy.Equals(ti.Field("id"), "981b10ed-b6f9-4120-8a77-a3b03e343143")) - // joint query +// joint query - // select * from t1 join t2 on t1.id=t2.testtable_id where t2.created_at > '2019-11-02' - q := ti.Query("name").Join(t2, sqlchemy.Equals(ti.Field("id"), t2.Field("testtable_id"))).Filter(sqlchermy.GT(t2.Field("created_at"), '2019-11-02') +// select * from t1 join t2 on t1.id=t2.testtable_id where t2.created_at > '2019-11-02' +q := ti.Query("name").Join(t2, sqlchemy.Equals(ti.Field("id"), t2.Field("testtable_id"))).Filter(sqlchermy.GT(t2.Field("created_at"), '2019-11-02') - // union query - // select id, name from testtable where id = '981b10ed-b6f9-4120-8a77-a3b03e343143' union select id, name from testtable where id='6fcc87ca-c1da-40ab-849a-305ff2663901' - q1 := t1.Query("id", "name").Equals("id", "981b10ed-b6f9-4120-8a77-a3b03e343143") - q2 := t1.Query("id", "name").Equals("id", "6fcc87ca-c1da-40ab-849a-305ff2663901") - qu := sqlchemy.Union(q1, q2) +// union query +// select id, name from testtable where id = '981b10ed-b6f9-4120-8a77-a3b03e343143' union select id, name from testtable where id='6fcc87ca-c1da-40ab-849a-305ff2663901' +q1 := t1.Query("id", "name").Equals("id", "981b10ed-b6f9-4120-8a77-a3b03e343143") +q2 := t1.Query("id", "name").Equals("id", "6fcc87ca-c1da-40ab-849a-305ff2663901") +qu := sqlchemy.Union(q1, q2) +``` ### Fetch data - q := ti.Query().Equals("id", "e2bc9b659cec407590dc2f3fcb009acb") +```go +q := ti.Query().Equals("id", "e2bc9b659cec407590dc2f3fcb009acb") - // fetch single row into object - row := TestTable{} - err := q.First(&row) - if err != nil { - log.Fatalf("fetch object error %s", err) - } +// fetch single row into object +row := TestTable{} +err := q.First(&row) +if err != nil { + log.Fatalf("fetch object error %s", err) +} - // fetch single row into a string map, where strMap is map[string]string - strMap, err := q.FirstStringMap() - if err != nil { - log.Fatalf("fetch object error %s", err) - } +// fetch single row into a string map, where strMap is map[string]string +strMap, err := q.FirstStringMap() +if err != nil { + log.Fatalf("fetch object error %s", err) +} - q := ti.Query().Startswith("id", "abc") - // fetch rows - rows := make([]TestTable, 0) - err := q.All(&rows) - if err != nil { - log.Fatalf("query failure: %s", err) - } +q := ti.Query().Startswith("id", "abc") +// fetch rows +rows := make([]TestTable, 0) +err := q.All(&rows) +if err != nil { + log.Fatalf("query failure: %s", err) +} - // fetch rows into string maps, where maps is []map[string]string - maps, err := q.AllStringMap() - if err != nil { - log.Fatalf("query failure: %s", err) - } +// fetch rows into string maps, where maps is []map[string]string +maps, err := q.AllStringMap() +if err != nil { + log.Fatalf("query failure: %s", err) +} +``` ### SubQuery Query can be used as a subquery in other queries. - // derive a subquery from an ordinary query - subq := t1.Query("id").Equals("version", "v2.0").SubQuery() - // use subquery - q := t1.Query().In("id", subq) +```go +// derive a subquery from an ordinary query +subq := t1.Query("id").Equals("version", "v2.0").SubQuery() +// use subquery +q := t1.Query().In("id", subq) +``` ## Insert - // hook to initialize data field before insert - func (t *TestTable) BeforeInsert() { - t.Id = uuid4() - } - // initialize data struct - dt1 := TestTable{ - Name: "Test", - } - // insert the data, primary key fields must be populated - // the primary key has been populated by the BeforeInsert hook - err = tablespec.Insert(&dt1) - - // insert or update - // insert the object if no primary key conflict, otherwise, update the record - err = tablespec.InsertOrUpdate(&dt1) +```go +// hook to initialize data field before insert +func (t *TestTable) BeforeInsert() { + t.Id = uuid4() +} +// initialize data struct +dt1 := TestTable{ + Name: "Test", +} +// insert the data, primary key fields must be populated +// the primary key has been populated by the BeforeInsert hook +err = tablespec.Insert(&dt1) +// insert or update +// insert the object if no primary key conflict, otherwise, update the record +err = tablespec.InsertOrUpdate(&dt1) +``` ## Update - // update the field - _, err = tablespec.Update(&dt3, func() error { - dt3.Name = "New name 4" - dt3.Compond = &SCompondStruct{Id: "998822333", Age: 80} - return nil - }) +```go +// update the field +_, err = tablespec.Update(&dt3, func() error { + dt3.Name = "New name 4" + dt3.Compond = &SCompondStruct{Id: "998822333", Age: 80} + return nil +}) +``` Please refer to sqltest/main.go for more examples. diff --git a/vendor/yunion.io/x/sqlchemy/case.go b/vendor/yunion.io/x/sqlchemy/case.go index acfca9e2bb..a40cd05d9e 100644 --- a/vendor/yunion.io/x/sqlchemy/case.go +++ b/vendor/yunion.io/x/sqlchemy/case.go @@ -23,11 +23,14 @@ type sCaseFieldBranch struct { thenField IQueryField } +// SCaseFunction represents function of case ... when ... branch type SCaseFunction struct { branches []sCaseFieldBranch elseField IQueryField } +// NewFunction creates a field with SQL function +// for example: SUM(count) as total func NewFunction(ifunc IFunction, name string) IQueryField { return &SFunctionFieldBase{ IFunction: ifunc, @@ -35,11 +38,13 @@ func NewFunction(ifunc IFunction, name string) IQueryField { } } +// Else adds else clause for case when function func (cf *SCaseFunction) Else(field IQueryField) *SCaseFunction { cf.elseField = field return cf } +// When adds when clause for case when function func (cf *SCaseFunction) When(when ICondition, then IQueryField) *SCaseFunction { cf.branches = append(cf.branches, sCaseFieldBranch{ whenCondition: when, @@ -48,6 +53,7 @@ func (cf *SCaseFunction) When(when ICondition, then IQueryField) *SCaseFunction return cf } +// NewCase creates a case... when...else... representation instance func NewCase() *SCaseFunction { return &SCaseFunction{} } diff --git a/vendor/yunion.io/x/sqlchemy/column.go b/vendor/yunion.io/x/sqlchemy/column.go index f99d300247..feb0dd6fa2 100644 --- a/vendor/yunion.io/x/sqlchemy/column.go +++ b/vendor/yunion.io/x/sqlchemy/column.go @@ -28,36 +28,82 @@ import ( "yunion.io/x/pkg/utils" ) +// IColumnSpec is an interface that represents a column of a table type IColumnSpec interface { + // Name returns the name of the column Name() string + + // ColType returns type of the column, e.g. INTEGER, VARCHAR ColType() string + + // Default returns default value of the column, represents in string Default() string + + // IsSupportDefault returns whether this column supports being given a default value IsSupportDefault() bool + + // IsNullable returns whether this column is nullable IsNullable() bool + + // SetNullable sets this column as nullable SetNullable(on bool) + + // IsPrimary returns whether this column is part of the primary keys IsPrimary() bool + + // IsUnique returns whether the value of this column unique for each row IsUnique() bool + + // IsIndex returns whether this column is indexable, if it is true, a index of this column will be automatically created IsIndex() bool + + // ExtraDefs returns some extra column attribute definitions, not covered by the standard fields ExtraDefs() string + + // DefinitionString return the SQL presentation of this column DefinitionString() string + + // IsText returns whether this column is actually a text, such a Datetime column is actually a text IsText() bool + + // IsSearchable returns whether this column is searchable, e.g. a integer column is not searchable, but a text field is searchable IsSearchable() bool + + // IsAscii returns whether this column is an ASCII type text, if true, the column should be compared with a UTF8 string IsAscii() bool + + // IsNumeric returns whether this column is a numeric type column, e.g. integer or float IsNumeric() bool + + // ConvertFromString returns the SQL representation of a value in string format for this column ConvertFromString(str string) string + // ConvertToString(str string) string + + // ConvertFromValue returns the SQL representation of a value for this column ConvertFromValue(val interface{}) interface{} + // ConvertToValue(str interface{}) interface{} + + // IsZero is used to determine a value is the zero value for this column IsZero(val interface{}) bool + + // AllowZero returns whether this column allow a zero value AllowZero() bool + // IsEqual(v1, v2 interface{}) bool + + // Tags returns the field tags for this column, which is in the struct definition Tags() map[string]string + // IsPointer returns whether this column is a pointer type definition, e.g. *int, *bool IsPointer() bool + // SetDefault sets the default value in the format of string for this column SetDefault(defStr string) } +// SBaseColumn is the base structure represents a column type SBaseColumn struct { name string dbName string @@ -72,98 +118,118 @@ type SBaseColumn struct { tags map[string]string } +// IsPointer implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsPointer() bool { return c.isPointer } +// Name implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) Name() string { if len(c.dbName) > 0 { return c.dbName - } else { - return c.name } + return c.name } +// ColType implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) ColType() string { return c.sqlType } +// Default implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) Default() string { return c.defaultString } +// SetDefault implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) SetDefault(defStr string) { c.defaultString = defStr } +// IsSupportDefault implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsSupportDefault() bool { return true } +// IsNullable implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsNullable() bool { return c.isNullable } +// SetNullable implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) SetNullable(on bool) { c.isNullable = on } +// IsPrimary implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsPrimary() bool { return c.isPrimary } +// IsUnique implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsUnique() bool { return c.isUnique } +// IsIndex implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsIndex() bool { return c.isIndex } +// ExtraDefs implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) ExtraDefs() string { return "" } +// IsText implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsText() bool { return false } +// IsAscii implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsAscii() bool { return false } +// IsSearchable implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsSearchable() bool { return false } +// IsNumeric implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) IsNumeric() bool { return false } +// AllowZero implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) AllowZero() bool { return c.isAllowZero } +// ConvertFromString implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) ConvertFromString(str string) string { return str } -func (c *SBaseColumn) ConvertToString(str string) string { +/*func (c *SBaseColumn) ConvertToString(str string) string { return str -} +}*/ +// ConvertFromValue implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) ConvertFromValue(val interface{}) interface{} { return val } -func (c *SBaseColumn) ConvertToValue(val interface{}) interface{} { +/*func (c *SBaseColumn) ConvertToValue(val interface{}) interface{} { return val -} +}*/ +// Tags implementation of SBaseColumn for IColumnSpec func (c *SBaseColumn) Tags() map[string]string { return c.tags } +// generate SQL representation of a column func definitionBuffer(c IColumnSpec) bytes.Buffer { var buf bytes.Buffer buf.WriteByte('`') @@ -204,6 +270,7 @@ func definitionBuffer(c IColumnSpec) bytes.Buffer { return buf } +// NewBaseColumn returns an instance of SBaseColumn func NewBaseColumn(name string, sqltype string, tagmap map[string]string, isPointer bool) SBaseColumn { var val string var ok bool @@ -260,19 +327,21 @@ func NewBaseColumn(name string, sqltype string, tagmap map[string]string, isPoin } } +// SBaseWidthColumn represents a type of column that with width attribute, such as VARCHAR(20), INT(10) type SBaseWidthColumn struct { SBaseColumn width int } +// ColType implementation of SBaseWidthColumn for IColumnSpec func (c *SBaseWidthColumn) ColType() string { if c.width > 0 { return fmt.Sprintf("%s(%d)", c.sqlType, c.width) - } else { - return c.sqlType } + return c.sqlType } +// NewBaseWidthColumn return an instance of SBaseWidthColumn func NewBaseWidthColumn(name string, sqltype string, tagmap map[string]string, isPointer bool) SBaseWidthColumn { width := 0 tagmap, v, ok := utils.TagPop(tagmap, TAG_WIDTH) @@ -286,15 +355,18 @@ func NewBaseWidthColumn(name string, sqltype string, tagmap map[string]string, i return wc } +// SBooleanColumn represents a boolean type column, which is a int(1) for mysql, with value of true or false type SBooleanColumn struct { SBaseWidthColumn } +// DefinitionString implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// ConvertFromString implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) ConvertFromString(str string) string { switch strings.ToLower(str) { case "true", "yes", "on", "ok", "1": @@ -304,7 +376,7 @@ func (c *SBooleanColumn) ConvertFromString(str string) string { } } -func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { +/*func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { switch bVal := val.(type) { case bool: if bVal { @@ -323,18 +395,19 @@ func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} { default: return 0 } -} +}*/ +// IsZero implementation of SBooleanColumn for IColumnSpec func (c *SBooleanColumn) IsZero(val interface{}) bool { if c.isPointer { bVal := val.(*bool) return bVal == nil - } else { - bVal := val.(bool) - return bVal == false } + bVal := val.(bool) + return bVal == false } +// NewBooleanColumn return an instance of SBooleanColumn func NewBooleanColumn(name string, tagmap map[string]string, isPointer bool) SBooleanColumn { bc := SBooleanColumn{SBaseWidthColumn: NewBaseWidthColumn(name, "TINYINT", tagmap, isPointer)} if !bc.IsPointer() && len(bc.Default()) > 0 && bc.ConvertFromString(bc.Default()) == "1" { @@ -344,15 +417,18 @@ func NewBooleanColumn(name string, tagmap map[string]string, isPointer bool) SBo return bc } +// STristateColumn represents a tristate type column, with value of true, false or none type STristateColumn struct { SBaseWidthColumn } +// DefinitionString implementation of STristateColumn for IColumnSpec func (c *STristateColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// ConvertFromString implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromString(str string) string { switch strings.ToLower(str) { case "true", "yes", "on", "ok", "1": @@ -364,43 +440,54 @@ func (c *STristateColumn) ConvertFromString(str string) string { } } +// ConvertFromValue implementation of STristateColumn for IColumnSpec func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} { bVal := val.(tristate.TriState) if bVal == tristate.True { return 1 - } else { - return 0 } + return 0 } +// IsZero implementation of STristateColumn for IColumnSpec func (c *STristateColumn) IsZero(val interface{}) bool { if c.isPointer { bVal := val.(*tristate.TriState) return bVal == nil - } else { - bVal := val.(tristate.TriState) - return bVal == tristate.None } + bVal := val.(tristate.TriState) + return bVal == tristate.None } +// NewTristateColumn return an instance of STristateColumn func NewTristateColumn(name string, tagmap map[string]string, isPointer bool) STristateColumn { bc := STristateColumn{SBaseWidthColumn: NewBaseWidthColumn(name, "TINYINT", tagmap, isPointer)} return bc } +// SIntegerColumn represents an integer type of column, with value of integer type SIntegerColumn struct { SBaseWidthColumn - IsAutoIncrement bool - IsAutoVersion bool - IsUnsigned bool + // Is this column an autoincrement colmn + IsAutoIncrement bool + + // Is this column is a version column for this records + IsAutoVersion bool + + // Is this column a unsigned integer? + IsUnsigned bool + + // If this column is an autoincrement column, AutoIncrementOffset records the initial offset AutoIncrementOffset int64 } +// IsNumeric implementation of SIntegerColumn for IColumnSpec func (c *SIntegerColumn) IsNumeric() bool { return true } +// ExtraDefs implementation of SIntegerColumn for IColumnSpec func (c *SIntegerColumn) ExtraDefs() string { if c.IsAutoIncrement { return "AUTO_INCREMENT" @@ -408,11 +495,13 @@ func (c *SIntegerColumn) ExtraDefs() string { return "" } +// DefinitionString implementation of SIntegerColumn for IColumnSpec func (c *SIntegerColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// IsZero implementation of SIntegerColumn for IColumnSpec func (c *SIntegerColumn) IsZero(val interface{}) bool { if val == nil || (c.isPointer && reflect.ValueOf(val).IsNil()) { return true @@ -424,6 +513,7 @@ func (c *SIntegerColumn) IsZero(val interface{}) bool { return true } +// ColType implementation of SIntegerColumn for IColumnSpec func (c *SIntegerColumn) ColType() string { str := (&c.SBaseWidthColumn).ColType() if c.IsUnsigned { @@ -432,6 +522,7 @@ func (c *SIntegerColumn) ColType() string { return str } +// NewIntegerColumn return an instance of SIntegerColumn func NewIntegerColumn(name string, sqltype string, unsigned bool, tagmap map[string]string, isPointer bool) SIntegerColumn { autoinc := false autoincBase := int64(0) @@ -471,19 +562,23 @@ func NewIntegerColumn(name string, sqltype string, unsigned bool, tagmap map[str return c } +// SFloatColumn represents a float type column, e.g. float32 or float64 type SFloatColumn struct { SBaseColumn } +// IsNumeric implementation of SFloatColumn for IColumnSpec func (c *SFloatColumn) IsNumeric() bool { return true } +// DefinitionString implementation of SFloatColumn for IColumnSpec func (c *SFloatColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// IsZero implementation of SFloatColumn for IColumnSpec func (c *SFloatColumn) IsZero(val interface{}) bool { if c.isPointer { switch val.(type) { @@ -503,28 +598,34 @@ func (c *SFloatColumn) IsZero(val interface{}) bool { return true } +// NewFloatColumn returns an instance of SFloatColumn func NewFloatColumn(name string, sqlType string, tagmap map[string]string, isPointer bool) SFloatColumn { return SFloatColumn{SBaseColumn: NewBaseColumn(name, sqlType, tagmap, isPointer)} } +// SDecimalColumn represents a DECIMAL type of column, i.e. a float with fixed width of digits type SDecimalColumn struct { SBaseWidthColumn Precision int } +// ColType implementation of SDecimalColumn for IColumnSpec func (c *SDecimalColumn) ColType() string { return fmt.Sprintf("%s(%d, %d)", c.sqlType, c.width, c.Precision) } +// IsNumeric implementation of SDecimalColumn for IColumnSpec func (c *SDecimalColumn) IsNumeric() bool { return true } +// DefinitionString implementation of SDecimalColumn for IColumnSpec func (c *SDecimalColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// IsZero implementation of SDecimalColumn for IColumnSpec func (c *SDecimalColumn) IsZero(val interface{}) bool { if c.isPointer { switch val.(type) { @@ -544,6 +645,7 @@ func (c *SDecimalColumn) IsZero(val interface{}) bool { return true } +// NewDecimalColumn returns an instance of SDecimalColumn func NewDecimalColumn(name string, tagmap map[string]string, isPointer bool) SDecimalColumn { tagmap, v, ok := utils.TagPop(tagmap, TAG_PRECISION) if !ok { @@ -559,21 +661,23 @@ func NewDecimalColumn(name string, tagmap map[string]string, isPointer bool) SDe } } +// STextColumn represents a text type of column type STextColumn struct { SBaseWidthColumn Charset string } +// IsSupportDefault implementation of STextColumn for IColumnSpec func (c *STextColumn) IsSupportDefault() bool { // https://stackoverflow.com/questions/3466872/why-cant-a-text-column-have-a-default-value-in-mysql // MySQL does not support default for TEXT/BLOB if c.sqlType == "VARCHAR" { return true - } else { - return false } + return false } +// ColType implementation of STextColumn for IColumnSpec func (c *STextColumn) ColType() string { var charset string var collate string @@ -588,35 +692,39 @@ func (c *STextColumn) ColType() string { return fmt.Sprintf("%s CHARACTER SET '%s' COLLATE '%s'", c.SBaseWidthColumn.ColType(), charset, collate) } +// IsText implementation of STextColumn for IColumnSpec func (c *STextColumn) IsText() bool { return true } +// IsSearchable implementation of STextColumn for IColumnSpec func (c *STextColumn) IsSearchable() bool { return true } +// IsAscii implementation of STextColumn for IColumnSpec func (c *STextColumn) IsAscii() bool { if c.Charset == "ascii" { return true - } else { - return false } + return false } +// DefinitionString implementation of STextColumn for IColumnSpec func (c *STextColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// IsZero implementation of STextColumn for IColumnSpec func (c *STextColumn) IsZero(val interface{}) bool { if c.isPointer { return gotypes.IsNil(val) - } else { - return reflect.ValueOf(val).Len() == 0 } + return reflect.ValueOf(val).Len() == 0 } +// NewTextColumn return an instance of STextColumn func NewTextColumn(name string, tagmap map[string]string, isPointer bool) STextColumn { var width int var sqltype string @@ -666,29 +774,33 @@ func NewStringColumn(name string, sqltype string, tagmap map[string]string) SStr return sc }*/ +// STimeTypeColumn represents a Detetime type of column, e.g. DateTime type STimeTypeColumn struct { SBaseColumn } +// IsText implementation of STimeTypeColumn for IColumnSpec func (c *STimeTypeColumn) IsText() bool { return true } +// DefinitionString implementation of STimeTypeColumn for IColumnSpec func (c *STimeTypeColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// IsZero implementation of STimeTypeColumn for IColumnSpec func (c *STimeTypeColumn) IsZero(val interface{}) bool { if c.isPointer { bVal := val.(*time.Time) return bVal == nil - } else { - bVal := val.(time.Time) - return bVal.IsZero() } + bVal := val.(time.Time) + return bVal.IsZero() } +// NewTimeTypeColumn return an instance of STimeTypeColumn func NewTimeTypeColumn(name string, typeStr string, tagmap map[string]string, isPointer bool) STimeTypeColumn { dc := STimeTypeColumn{ NewBaseColumn(name, typeStr, tagmap, isPointer), @@ -696,12 +808,18 @@ func NewTimeTypeColumn(name string, typeStr string, tagmap map[string]string, is return dc } +// SDateTimeColumn represents a DateTime type of column type SDateTimeColumn struct { STimeTypeColumn + + // Is this column a 'created_at' field, whichi records the time of create this record IsCreatedAt bool + + // Is this column a 'updated_at' field, whichi records the time when this record was updated IsUpdatedAt bool } +// NewDateTimeColumn returns an instance of DateTime column func NewDateTimeColumn(name string, tagmap map[string]string, isPointer bool) SDateTimeColumn { createdAt := false updatedAt := false @@ -720,15 +838,18 @@ func NewDateTimeColumn(name string, tagmap map[string]string, isPointer bool) SD return dtc } +// CompoundColumn represents a column of compound tye, e.g. a JSON, an Array, or a struct type CompoundColumn struct { STextColumn } +// DefinitionString implementation of CompoundColumn for IColumnSpec func (c *CompoundColumn) DefinitionString() string { buf := definitionBuffer(c) return buf.String() } +// IsZero implementation of CompoundColumn for IColumnSpec func (c *CompoundColumn) IsZero(val interface{}) bool { if val == nil { return true @@ -739,15 +860,16 @@ func (c *CompoundColumn) IsZero(val interface{}) bool { return false } +// ConvertFromValue implementation of CompoundColumn for IColumnSpec func (c *CompoundColumn) ConvertFromValue(val interface{}) interface{} { bVal, ok := val.(gotypes.ISerializable) if ok && bVal != nil { return bVal.String() - } else { - return "" } + return "" } +// NewCompoundColumn returns an instance of CompoundColumn func NewCompoundColumn(name string, tagmap map[string]string, isPointer bool) CompoundColumn { dtc := CompoundColumn{NewTextColumn(name, tagmap, isPointer)} return dtc diff --git a/vendor/yunion.io/x/sqlchemy/conditions.go b/vendor/yunion.io/x/sqlchemy/conditions.go index cd317641c3..4628af41ef 100644 --- a/vendor/yunion.io/x/sqlchemy/conditions.go +++ b/vendor/yunion.io/x/sqlchemy/conditions.go @@ -21,11 +21,16 @@ import ( "yunion.io/x/pkg/util/reflectutils" ) +// ICondition is the interface representing a condition for SQL query +// e.g. WHERE a1 = b1 is a condition of equal +// the condition support nested condition, with AND, OR and NOT boolean operators type ICondition interface { WhereClause() string Variables() []interface{} } +// SCompoundConditions is a Compound condition represents AND or OR boolean operation +// Compound condition also follows the ICondition interface type SCompoundConditions struct { conditions []ICondition } @@ -45,10 +50,12 @@ func compoundWhereClause(c *SCompoundConditions, op string) string { return buf.String() } +// WhereClause implementation of SCompoundConditions for ICondition func (c *SCompoundConditions) WhereClause() string { return "" } +// Variables implementation of SCompoundConditions for ICondition func (c *SCompoundConditions) Variables() []interface{} { vars := make([]interface{}, 0) for _, cond := range c.conditions { @@ -60,22 +67,27 @@ func (c *SCompoundConditions) Variables() []interface{} { return vars } +// SAndConditions represents the AND condition, which is a SCompoundConditions type SAndConditions struct { SCompoundConditions } +// WhereClause implementation of SAndConditions for IConditionq func (c *SAndConditions) WhereClause() string { return compoundWhereClause(&c.SCompoundConditions, SQL_OP_AND) } +// SOrConditions represents the OR condition, which is a SCompoundConditions type SOrConditions struct { SCompoundConditions } +// WhereClause implementation of SOrConditions for ICondition func (c *SOrConditions) WhereClause() string { return compoundWhereClause(&c.SCompoundConditions, SQL_OP_OR) } +// AND method that combines many conditions with AND operator func AND(cond ...ICondition) ICondition { conds := make([]ICondition, 0) for _, c := range cond { @@ -90,6 +102,7 @@ func AND(cond ...ICondition) ICondition { return &cc } +// OR method that combines many conditions with OR operator func OR(cond ...ICondition) ICondition { conds := make([]ICondition, 0) for _, c := range cond { @@ -104,152 +117,187 @@ func OR(cond ...ICondition) ICondition { return &cc } +// SNotCondition represents the NOT condition, which is a boolean operator type SNotCondition struct { condition ICondition } +// WhereClause implementationq of SNotCondition for ICondition func (c *SNotCondition) WhereClause() string { return fmt.Sprintf("%s (%s)", SQL_OP_NOT, c.condition.WhereClause()) } +// Variables implementation of SNotCondition for ICondition func (c *SNotCondition) Variables() []interface{} { return c.condition.Variables() } +// NOT method that makes negative operator on a condition func NOT(cond ICondition) ICondition { cc := SNotCondition{condition: cond} return &cc } +// SSingleCondition represents a kind of condition that composed of one query field type SSingleCondition struct { field IQueryField } +// Variables implementation of SSingleCondition for ICondition func (c *SSingleCondition) Variables() []interface{} { return []interface{}{} } +// NewSingleCondition returns an instance of SSingleCondition func NewSingleCondition(field IQueryField) SSingleCondition { return SSingleCondition{field: field} } +// SIsNullCondition is a condition representing a comparison with null, e.g. a is null type SIsNullCondition struct { SSingleCondition } +// WhereClause implementation for SIsNullCondition for ICondition func (c *SIsNullCondition) WhereClause() string { return fmt.Sprintf("%s IS NULL", c.field.Reference()) } +// IsNull methods that justifies a field is null func IsNull(f IQueryField) ICondition { c := SIsNullCondition{NewSingleCondition(f)} return &c } +// SIsNotNullCondition is a condition represents a comparison with not null, e.g. a is not null type SIsNotNullCondition struct { SSingleCondition } +// WhereClause implementation of SIsNotNullCondition for ICondition func (c *SIsNotNullCondition) WhereClause() string { return fmt.Sprintf("%s IS NOT NULL", c.field.Reference()) } +// IsNotNull methods that justifies a field is not null func IsNotNull(f IQueryField) ICondition { c := SIsNotNullCondition{NewSingleCondition(f)} return &c } +// SIsEmptyCondition is a condition representing the empty status of a field type SIsEmptyCondition struct { SSingleCondition } +// WhereClause implementation of SIsEmptyCondition for ICondition func (c *SIsEmptyCondition) WhereClause() string { return fmt.Sprintf("LENGTH(%s) = 0", c.field.Reference()) } +// IsEmpty method that justifies where a text field is empty, e.g. length is zero func IsEmpty(f IQueryField) ICondition { c := SIsEmptyCondition{NewSingleCondition(f)} return &c } +// SIsNullOrEmptyCondition is a condition that justifies a field is null or empty type SIsNullOrEmptyCondition struct { SSingleCondition } +// WhereClause implementation of SIsNullOrEmptyCondition for ICondition func (c *SIsNullOrEmptyCondition) WhereClause() string { return fmt.Sprintf("%s IS NULL OR LENGTH(%s) = 0", c.field.Reference(), c.field.Reference()) } +// IsNullOrEmpty is the ethod justifies a field is null or empty, e.g. a is null or length(a) == 0 func IsNullOrEmpty(f IQueryField) ICondition { c := SIsNullOrEmptyCondition{NewSingleCondition(f)} return &c } +// SIsNotEmptyCondition represents a condition that represents a field is not empty type SIsNotEmptyCondition struct { SSingleCondition } +// WhereClause implementation of SIsNotEmptyCondition for ICondition func (c *SIsNotEmptyCondition) WhereClause() string { return fmt.Sprintf("%s IS NOT NULL AND LENGTH(%s) > 0", c.field.Reference(), c.field.Reference()) } +// IsNotEmpty method justifies a field is not empty func IsNotEmpty(f IQueryField) ICondition { c := SIsNotEmptyCondition{NewSingleCondition(f)} return &c } +// SIsTrueCondition represents a boolean field (TINYINT) is true, e.g. a == 1 type SIsTrueCondition struct { SSingleCondition } +// WhereClause implementation of SIsTrueCondition for ICondition func (c *SIsTrueCondition) WhereClause() string { return fmt.Sprintf("%s = 1", c.field.Reference()) } +// IsTrue method that justifies a field is true, e.g. field == 1 func IsTrue(f IQueryField) ICondition { c := SIsTrueCondition{NewSingleCondition(f)} return &c } +// SIsFalseCondition represents a boolean is false type SIsFalseCondition struct { SSingleCondition } +// WhereClause implementation of SIsFalseCondition for ICondition func (c *SIsFalseCondition) WhereClause() string { return fmt.Sprintf("%s = 0", c.field.Reference()) } +// IsFalse method justifies a boolean is false func IsFalse(f IQueryField) ICondition { c := SIsFalseCondition{NewSingleCondition(f)} return &c } +// SNoLaterThanCondition coompares a DATETIME field with current time and ensure the field is no later than now, e.g. a <= NOW() type SNoLaterThanCondition struct { SSingleCondition } +// WhereClause implementation of SNoLaterThanCondition for ICondition func (c *SNoLaterThanCondition) WhereClause() string { - return fmt.Sprintf("%s >= NOW()", c.field.Reference()) + return fmt.Sprintf("%s <= NOW()", c.field.Reference()) } +// NoLaterThan method justifies a DATETIME field is before current time func NoLaterThan(f IQueryField) ICondition { c := SNoLaterThanCondition{NewSingleCondition(f)} return &c } +// SNoEarlierThanCondition compares a field with current time and ensure the field is no earlier than NOW, e.g. a >= NOW() type SNoEarlierThanCondition struct { SSingleCondition } +// WhereClause implementation of SNoEarlierThanCondition for ICondition func (c *SNoEarlierThanCondition) WhereClause() string { - return fmt.Sprintf("%s <= NOW()", c.field.Reference()) + return fmt.Sprintf("%s >= NOW()", c.field.Reference()) } +// NoEarlierThan justifies a field is no earlier than current time func NoEarlierThan(f IQueryField) ICondition { c := SNoEarlierThanCondition{NewSingleCondition(f)} return &c } +// STupleCondition is a base condition that composed of two fields type STupleCondition struct { left IQueryField right interface{} @@ -273,7 +321,7 @@ func questionMark(count int) string { } else { var buf bytes.Buffer buf.WriteString("( ") - for i := 0; i < count; i += 1 { + for i := 0; i < count; i++ { if i > 0 { buf.WriteString(", ") } @@ -313,14 +361,17 @@ func varConditionVariables(v interface{}) []interface{} { } } +// NewTupleCondition returns an instance of tuple condition func NewTupleCondition(l IQueryField, r interface{}) STupleCondition { return STupleCondition{left: l, right: r} } +// Variables implementation of STupleCondition for ICondition func (t *STupleCondition) Variables() []interface{} { return varConditionVariables(t.right) } +// SInCondition represents a IN operation in SQL query type SInCondition struct { STupleCondition op string @@ -334,10 +385,12 @@ func inConditionWhereClause(t *STupleCondition, op string) string { return "0" } +// WhereClause implementation of SInCondition for ICondition func (t *SInCondition) WhereClause() string { return inConditionWhereClause(&t.STupleCondition, t.op) } +// In SQL operator func In(f IQueryField, v interface{}) ICondition { c := SInCondition{ NewTupleCondition(f, v), @@ -346,6 +399,7 @@ func In(f IQueryField, v interface{}) ICondition { return &c } +// NotIn SQL operator func NotIn(f IQueryField, v interface{}) ICondition { c := SInCondition{ NewTupleCondition(f, v), @@ -354,6 +408,7 @@ func NotIn(f IQueryField, v interface{}) ICondition { return &c } +// SLikeCondition represents LIKE operation in a SQL query type SLikeCondition struct { STupleCondition } @@ -369,15 +424,18 @@ func likeEscape(s string) string { return res.String() } +// WhereClause implementation for SLikeCondition for ICondition func (t *SLikeCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_LIKE) } +// Like SQL operator func Like(f IQueryField, v string) ICondition { c := SLikeCondition{NewTupleCondition(f, v)} return &c } +// ContainsAny is a OR combination of serveral Contains conditions func ContainsAny(f IQueryField, v []string) ICondition { conds := make([]ICondition, len(v)) for i := range v { @@ -386,107 +444,130 @@ func ContainsAny(f IQueryField, v []string) ICondition { return OR(conds...) } +// Contains method is a shortcut of LIKE method, Contains represents the condtion that a field contains a substring func Contains(f IQueryField, v string) ICondition { v = likeEscape(v) nv := fmt.Sprintf("%%%s%%", v) return Like(f, nv) } +// Startswith method is a shortcut of LIKE method, Startswith represents the condition that field starts with a substring func Startswith(f IQueryField, v string) ICondition { v = likeEscape(v) nv := fmt.Sprintf("%s%%", v) return Like(f, nv) } +// Endswith method is a shortcut of LIKE condition, Endswith represents that condition that field endswith a substring func Endswith(f IQueryField, v string) ICondition { v = likeEscape(v) nv := fmt.Sprintf("%%%s", v) return Like(f, nv) } +// SEqualsCondition represents equal operation between two fields type SEqualsCondition struct { STupleCondition } +// WhereClause implementation of SEqualsCondition for ICondition func (t *SEqualsCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_EQUAL) } +// Equals method represents equal of two fields func Equals(f IQueryField, v interface{}) ICondition { c := SEqualsCondition{NewTupleCondition(f, v)} return &c } +// SNotEqualsCondition is the opposite of equal condition type SNotEqualsCondition struct { STupleCondition } +// WhereClause implementation of SNotEqualsCondition for ICondition func (t *SNotEqualsCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_NOTEQUAL) } +// NotEquals method represents not equal of two fields func NotEquals(f IQueryField, v interface{}) ICondition { c := SNotEqualsCondition{NewTupleCondition(f, v)} return &c } +// SGreatEqualCondition represents >= operation on two fields type SGreatEqualCondition struct { STupleCondition } +// WhereClause implementation of SGreatEqualCondition for ICondition func (t *SGreatEqualCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_GE) } +// GE method represetns operation of Greate Than Or Equal to, e.g. a >= b func GE(f IQueryField, v interface{}) ICondition { c := SGreatEqualCondition{NewTupleCondition(f, v)} return &c } +// SGreatThanCondition represetns > operation on two fields type SGreatThanCondition struct { STupleCondition } +// WhereClause implementation of SGreatThanCondition for ICondition func (t *SGreatThanCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_GT) } +// GT method represents operation of Great Than, e.g. a > b func GT(f IQueryField, v interface{}) ICondition { c := SGreatThanCondition{NewTupleCondition(f, v)} return &c } +// SLessEqualCondition represents <= operation on two fields type SLessEqualCondition struct { STupleCondition } +// WhereClause implementation of SLessEqualCondition for ICondition func (t *SLessEqualCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_LE) } +// LE method represents operation of Less Than Or Equal to, e.q. a <= b func LE(f IQueryField, v interface{}) ICondition { c := SLessEqualCondition{NewTupleCondition(f, v)} return &c } +// SLessThanCondition represents < operation on two fields type SLessThanCondition struct { STupleCondition } +// WhereClause implementation of SLessThanCondition for ICondition func (t *SLessThanCondition) WhereClause() string { return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_LT) } +// LT method represents operation of Less Than, e.g. a < b func LT(f IQueryField, v interface{}) ICondition { c := SLessThanCondition{NewTupleCondition(f, v)} return &c } +// STripleCondition represents a base condition that composed of THREE fields type STripleCondition struct { STupleCondition right2 interface{} } +// Variables implementation of STripleCondition for ICondition func (t *STripleCondition) Variables() []interface{} { ret := make([]interface{}, 0) vars := varConditionVariables(t.right) @@ -496,41 +577,51 @@ func (t *STripleCondition) Variables() []interface{} { return ret } +// NewTripleCondition return an instance of STripleCondition func NewTripleCondition(l IQueryField, r interface{}, r2 interface{}) STripleCondition { return STripleCondition{STupleCondition: NewTupleCondition(l, r), right2: r2} } +// SBetweenCondition represents BETWEEN operator, e.g. c between a and b type SBetweenCondition struct { STripleCondition } +// WhereClause implementation of SBetweenCondition for ICondition func (t *SBetweenCondition) WhereClause() string { ret := tupleConditionWhereClause(&t.STupleCondition, SQL_OP_BETWEEN) return fmt.Sprintf("%s AND %s", ret, varConditionWhereClause(t.right2)) } +// Between SQL operator func Between(f IQueryField, r1, r2 interface{}) ICondition { c := SBetweenCondition{NewTripleCondition(f, r1, r2)} return &c } +// STrueCondition represents a dummy condition that is always true type STrueCondition struct{} +// WhereClause implementation of STrueCondition for ICondition func (t *STrueCondition) WhereClause() string { return "1" } +// Variables implementation of STrueCondition for ICondition func (t *STrueCondition) Variables() []interface{} { return nil } +// SFalseCondition is a dummy condition that is always false type SFalseCondition struct{} +// WhereClause implementation of SFalseCondition for ICondition func (t *SFalseCondition) WhereClause() string { return "0" } +// Variables implementation of SFalseCondition for ICondition func (t *SFalseCondition) Variables() []interface{} { return nil } diff --git a/vendor/yunion.io/x/sqlchemy/const.go b/vendor/yunion.io/x/sqlchemy/const.go index 1f192b0030..d4478788ab 100644 --- a/vendor/yunion.io/x/sqlchemy/const.go +++ b/vendor/yunion.io/x/sqlchemy/const.go @@ -20,47 +20,78 @@ import ( ) const ( - SQL_OP_AND = "AND" - SQL_OP_OR = "OR" - SQL_OP_NOT = "NOT" - SQL_OP_LIKE = "LIKE" - SQL_OP_IN = "IN" - SQL_OP_NOTIN = "NOT IN" - SQL_OP_EQUAL = "=" - SQL_OP_LT = "<" - SQL_OP_LE = "<=" - SQL_OP_GT = ">" - SQL_OP_GE = ">=" - SQL_OP_BETWEEN = "BETWEEN" + // SQL_OP_AND represents AND operator + SQL_OP_AND = "AND" + // SQL_OP_OR represents OR operator + SQL_OP_OR = "OR" + // SQL_OP_NOT represents NOT operator + SQL_OP_NOT = "NOT" + // SQL_OP_LIKE represents LIKE operator + SQL_OP_LIKE = "LIKE" + // SQL_OP_IN represents IN operator + SQL_OP_IN = "IN" + // SQL_OP_NOTIN represents NOT IN operator + SQL_OP_NOTIN = "NOT IN" + // SQL_OP_EQUAL represents EQUAL operator + SQL_OP_EQUAL = "=" + // SQL_OP_LT represents < operator + SQL_OP_LT = "<" + // SQL_OP_LE represents <= operator + SQL_OP_LE = "<=" + // SQL_OP_GT represents > operator + SQL_OP_GT = ">" + // SQL_OP_GE represents >= operator + SQL_OP_GE = ">=" + // SQL_OP_BETWEEN represents BETWEEN operator + SQL_OP_BETWEEN = "BETWEEN" + // SQL_OP_NOTEQUAL represents NOT EQUAL operator SQL_OP_NOTEQUAL = "<>" ) const ( - TAG_IGNORE = "ignore" - TAG_NAME = "name" - TAG_WIDTH = "width" - TAG_TEXT_LENGTH = "length" - TAG_CHARSET = "charset" - TAG_PRECISION = "precision" - TAG_DEFAULT = "default" - TAG_UNIQUE = "unique" - TAG_INDEX = "index" - TAG_PRIMARY = "primary" - TAG_NULLABLE = "nullable" - TAG_AUTOINCREMENT = "auto_increment" - TAG_AUTOVERSION = "auto_version" + // TAG_IGNORE is a field tag that indicates the field is ignored, not represents a table column + TAG_IGNORE = "ignore" + // TAG_NAME is a field tag that indicates the column name of this field + TAG_NAME = "name" + // TAG_WIDTH is a field tag that indicates the width of the column, like VARCHAR(15) + TAG_WIDTH = "width" + // TAG_TEXT_LENGTH is a field tag that indicates the length of a text column + TAG_TEXT_LENGTH = "length" + // TAG_CHARSET is a field tag that indicates the charset of a text column + TAG_CHARSET = "charset" + // TAG_PRECISION is a field tag that indicates the precision of a float column + TAG_PRECISION = "precision" + // TAG_DEFAULT is a field tag that indicates the default value of a column + TAG_DEFAULT = "default" + // TAG_UNIQUE is a field tag that indicates the column value is unique + TAG_UNIQUE = "unique" + // TAG_INDEX is a field tag that indicates the column is a indexable column + TAG_INDEX = "index" + // TAG_PRIMARY is a field tag that indicates the column is part of primary key + TAG_PRIMARY = "primary" + // TAG_NULLABLE is a field tag that indicates the column is nullable + TAG_NULLABLE = "nullable" + // TAG_AUTOINCREMENT is a field tag that indicates the integer column is auto_increment, the column should must be primary + TAG_AUTOINCREMENT = "auto_increment" + // TAG_AUTOVERSION is a field tag that indicates the integer column is used to records the update version of a record + TAG_AUTOVERSION = "auto_version" + // TAG_UPDATE_TIMESTAMP is a field tag that indicates the datetime column is the updated_at timestamp TAG_UPDATE_TIMESTAMP = "updated_at" + // TAG_CREATE_TIMESTAMP is a field tag that indicates the datetime column is the created_at timestamp TAG_CREATE_TIMESTAMP = "created_at" - TAG_ALLOW_ZERO = "allow_zero" + // TAG_ALLOW_ZERO is a field tag that indicates whether the column allow zero value + TAG_ALLOW_ZERO = "allow_zero" ) var ( + // INT_WIDTH_DEFAULT records the default width of integer type INT_WIDTH_DEFAULT = map[string]int{ "TINYINT": 4, "SMALLINT": 6, "INT": 11, "BIGINT": 20, } + // UNSIGNED_INT_WIDTH_DEFAULT records the default width of unsigned integer type UNSIGNED_INT_WIDTH_DEFAULT = map[string]int{ "TINYINT": 3, "SMALLINT": 5, diff --git a/vendor/yunion.io/x/sqlchemy/constraint.go b/vendor/yunion.io/x/sqlchemy/constraint.go index 0041cf2efc..d0379a0deb 100644 --- a/vendor/yunion.io/x/sqlchemy/constraint.go +++ b/vendor/yunion.io/x/sqlchemy/constraint.go @@ -19,7 +19,7 @@ import ( "strings" ) -type STableConstraint struct { +type sTableConstraint struct { name string columns []string foreignTable string @@ -53,11 +53,11 @@ func fetchColumns(match string) []string { return ret } -func parseConstraints(defStr string) []STableConstraint { +func parseConstraints(defStr string) []sTableConstraint { matches := constraintRegexp.FindAllStringSubmatch(defStr, -1) - tcs := make([]STableConstraint, len(matches)) + tcs := make([]sTableConstraint, len(matches)) for i := range matches { - tcs[i] = STableConstraint{ + tcs[i] = sTableConstraint{ name: matches[i][1], foreignTable: matches[i][4], columns: fetchColumns(matches[i][2]), @@ -67,11 +67,11 @@ func parseConstraints(defStr string) []STableConstraint { return tcs } -func parseIndexes(defStr string) []STableIndex { +func parseIndexes(defStr string) []sTableIndex { matches := indexRegexp.FindAllStringSubmatch(defStr, -1) - tcs := make([]STableIndex, len(matches)) + tcs := make([]sTableIndex, len(matches)) for i := range matches { - tcs[i] = STableIndex{ + tcs[i] = sTableIndex{ name: matches[i][2], isUnique: len(matches[i][1]) > 0, columns: fetchColumns(matches[i][3]), diff --git a/vendor/yunion.io/x/sqlchemy/debug.go b/vendor/yunion.io/x/sqlchemy/debug.go index dee4307a0f..0ca6168d8e 100644 --- a/vendor/yunion.io/x/sqlchemy/debug.go +++ b/vendor/yunion.io/x/sqlchemy/debug.go @@ -15,33 +15,57 @@ package sqlchemy import ( + "fmt" + "strings" + "time" + "yunion.io/x/log" ) var ( + // DEBUG_SQLCHEMY is a global constant that indicates turn on SQL debug DEBUG_SQLCHEMY = false ) +func sqlDebug(sqlstr string, variables []interface{}) { + for _, v := range variables { + switch v.(type) { + case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: + sqlstr = strings.Replace(sqlstr, "?", fmt.Sprintf(`%v`, v), 1) + case string, time.Time: + sqlstr = strings.Replace(sqlstr, "?", fmt.Sprintf(`"%s"`, v), 1) + default: + sqlstr = strings.Replace(sqlstr, "?", fmt.Sprintf(`"%v"`, v), 1) + } + } + log.Debugln("SQuery ", sqlstr) +} + +// DebugQuery show the full query string for debug func (tq *SQuery) DebugQuery() { sqlstr := tq.String() vars := tq.Variables() - log.Debugf("SQuery %s with vars: %s", sqlstr, vars) + sqlDebug(sqlstr, vars) } +// DebugQuery show the full query string for a subquery for debug func (sqf *SSubQuery) DebugQuery() { sqlstr := sqf.Expression() vars := sqf.query.Variables() - log.Debugf("SQuery %s with vars: %s", sqlstr, vars) + sqlDebug(sqlstr, vars) } +// DebugInsert does insert with debug mode on func (t *STableSpec) DebugInsert(dt interface{}) error { return t.insert(dt, false, true) } +// DebugInsertOrUpdate does insertOrUpdate with debug mode on func (t *STableSpec) DebugInsertOrUpdate(dt interface{}) error { return t.insert(dt, true, true) } -func (ts *STableSpec) DebugUpdateFields(dt interface{}, fields map[string]interface{}) error { - return ts.updateFields(dt, fields, true) +// DebugUpdateFields does update with debug mode on +func (t *STableSpec) DebugUpdateFields(dt interface{}, fields map[string]interface{}) error { + return t.updateFields(dt, fields, true) } diff --git a/vendor/yunion.io/x/sqlchemy/errors.go b/vendor/yunion.io/x/sqlchemy/errors.go index f323edc22e..9aebf37494 100644 --- a/vendor/yunion.io/x/sqlchemy/errors.go +++ b/vendor/yunion.io/x/sqlchemy/errors.go @@ -21,19 +21,38 @@ import ( ) const ( - ErrNoDataToUpdate = errors.Error("No data to update") - ErrDuplicateEntry = errors.Error("duplicate entry") - ErrEmptyQuery = errors.Error("empty query") - ErrEmptyPrimaryKey = errors.Error("empty primary keys") + // ErrNoDataToUpdate is an Error constant: no data to update + ErrNoDataToUpdate = errors.Error("No data to update") + + // ErrDuplicateEntry is an Error constant: duplicate entry + ErrDuplicateEntry = errors.Error("duplicate entry") + + // ErrEmptyQuery is an Error constant: empty query + ErrEmptyQuery = errors.Error("empty query") + + // ErrEmptyPrimaryKey is an Error constant: no primary key + ErrEmptyPrimaryKey = errors.Error("empty primary keys") + + // ErrUnexpectRowCount is an Error constant: the number of rows impacted by modification unexpected ErrUnexpectRowCount = errors.Error("unexpected row count") + // ErrNeedsPointer is an Error constant: input should be a pointer ErrNeedsPointer = errors.Error("input needs pointer input") - ErrNeedsArray = errors.Error("input needs slice or array") - ErrReadOnly = errors.Error("read only input") + // ErrNeedsArray is an Error constant: input should be an Array or Slice + ErrNeedsArray = errors.Error("input needs slice or array") + + // ErrReadOnly is an Error constant: database is read-only + ErrReadOnly = errors.Error("read only input") + + // ErrNotSupported is an Error constant: method not supported yet ErrNotSupported = errors.ErrNotSupported + // ErrTableNotExists is an Error constant: table not exists ErrTableNotExists = errors.Error("TableNotExists") + + // ErrUnionFieldsNotMatch is an Error constant: fields of union queries not match + ErrUnionFieldsNotMatch = errors.Error("cannot union, name of fields not match") ) const ( diff --git a/vendor/yunion.io/x/sqlchemy/fetch.go b/vendor/yunion.io/x/sqlchemy/fetch.go index 06fec547a5..4088b5bb89 100644 --- a/vendor/yunion.io/x/sqlchemy/fetch.go +++ b/vendor/yunion.io/x/sqlchemy/fetch.go @@ -24,7 +24,7 @@ import ( "yunion.io/x/pkg/util/reflectutils" ) -// fetch the values of a struct whose primary key values have been set +// Fetch method fetches the values of a struct whose primary key values have been set // input is a pointer to the model to be populated func (ts *STableSpec) Fetch(dt interface{}) error { q := ts.Query() @@ -39,7 +39,7 @@ func (ts *STableSpec) Fetch(dt interface{}) error { return q.First(dt) } -// fetch the values of an array of structs whose primary key values have been set +// FetchAll method fetches the values of an array of structs whose primary key values have been set // input is a pointer to the array of models to be populated func (ts *STableSpec) FetchAll(dest interface{}) error { arrayType := reflect.TypeOf(dest).Elem() @@ -56,7 +56,7 @@ func (ts *STableSpec) FetchAll(dest interface{}) error { primaryCol := primaryCols[0] keyValues := make([]interface{}, arrayValue.Len()) - for i := 0; i < arrayValue.Len(); i += 1 { + for i := 0; i < arrayValue.Len(); i++ { eleValue := arrayValue.Index(i) fields := reflectutils.FetchStructFieldValueSet(eleValue) keyValues[i], _ = fields.GetInterface(primaryCol.Name()) @@ -69,11 +69,11 @@ func (ts *STableSpec) FetchAll(dest interface{}) error { } tmpDestMapMap := make(map[string]map[string]string) - for i := 0; i < len(tmpDestMaps); i += 1 { + for i := 0; i < len(tmpDestMaps); i++ { tmpDestMapMap[tmpDestMaps[i][primaryCol.Name()]] = tmpDestMaps[i] } - for i := 0; i < arrayValue.Len(); i += 1 { + for i := 0; i < arrayValue.Len(); i++ { keyValueStr := getStringValue(keyValues[i]) if tmpMap, ok := tmpDestMapMap[keyValueStr]; ok { err = mapString2Struct(tmpMap, arrayValue.Index(i)) diff --git a/vendor/yunion.io/x/sqlchemy/field_update.go b/vendor/yunion.io/x/sqlchemy/field_update.go index 342f84a53d..2633405b2b 100644 --- a/vendor/yunion.io/x/sqlchemy/field_update.go +++ b/vendor/yunion.io/x/sqlchemy/field_update.go @@ -51,6 +51,8 @@ func (ts *STableSpec) GetUpdateColumnValue(dataType reflect.Type, dataValue refl } */ +// UpdateFields update a record with the values provided by fields stringmap +// params dt: model struct, fileds: {struct-field-name-string: update-value} func (ts *STableSpec) UpdateFields(dt interface{}, fields map[string]interface{}) error { return ts.updateFields(dt, fields, false) } diff --git a/vendor/yunion.io/x/sqlchemy/filter.go b/vendor/yunion.io/x/sqlchemy/filter.go index b8c329d4e4..facd454812 100644 --- a/vendor/yunion.io/x/sqlchemy/filter.go +++ b/vendor/yunion.io/x/sqlchemy/filter.go @@ -14,6 +14,8 @@ package sqlchemy +// Filter method filters a SQL query with given ICondition +// equivalent to add a clause in where conditions func (tq *SQuery) Filter(cond ICondition) *SQuery { if tq.groupBy != nil && len(tq.groupBy) > 0 { if tq.having == nil { @@ -31,120 +33,144 @@ func (tq *SQuery) Filter(cond ICondition) *SQuery { return tq } +// FilterByTrue filters query with a true condition func (tq *SQuery) FilterByTrue() *SQuery { return tq.Filter(&STrueCondition{}) } +// FilterByFalse filters query with a false condition func (tq *SQuery) FilterByFalse() *SQuery { return tq.Filter(&SFalseCondition{}) } -func (q *SQuery) Like(f string, v string) *SQuery { - cond := Like(q.Field(f), v) - return q.Filter(cond) +// Like filters query with a like condition +func (tq *SQuery) Like(f string, v string) *SQuery { + cond := Like(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) Contains(f string, v string) *SQuery { - cond := Contains(q.Field(f), v) - return q.Filter(cond) +// Contains filters query with a contains condition +func (tq *SQuery) Contains(f string, v string) *SQuery { + cond := Contains(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) Startswith(f string, v string) *SQuery { - cond := Startswith(q.Field(f), v) - return q.Filter(cond) +// Startswith filters query with a startswith condition +func (tq *SQuery) Startswith(f string, v string) *SQuery { + cond := Startswith(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) Endswith(f string, v string) *SQuery { - cond := Endswith(q.Field(f), v) - return q.Filter(cond) +// Endswith filters query with a endswith condition +func (tq *SQuery) Endswith(f string, v string) *SQuery { + cond := Endswith(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) NotLike(f string, v string) *SQuery { - cond := Like(q.Field(f), v) - return q.Filter(NOT(cond)) +// NotLike filters query with a not like condition +func (tq *SQuery) NotLike(f string, v string) *SQuery { + cond := Like(tq.Field(f), v) + return tq.Filter(NOT(cond)) } -func (q *SQuery) In(f string, v interface{}) *SQuery { - cond := In(q.Field(f), v) - return q.Filter(cond) +// In filters query with a in condition +func (tq *SQuery) In(f string, v interface{}) *SQuery { + cond := In(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) NotIn(f string, v interface{}) *SQuery { - cond := In(q.Field(f), v) - return q.Filter(NOT(cond)) +// NotIn filters query with a not in condition +func (tq *SQuery) NotIn(f string, v interface{}) *SQuery { + cond := In(tq.Field(f), v) + return tq.Filter(NOT(cond)) } -func (q *SQuery) Between(f string, v1, v2 interface{}) *SQuery { - cond := Between(q.Field(f), v1, v2) - return q.Filter(cond) +// Between filters query with a between condition +func (tq *SQuery) Between(f string, v1, v2 interface{}) *SQuery { + cond := Between(tq.Field(f), v1, v2) + return tq.Filter(cond) } -func (q *SQuery) NotBetween(f string, v1, v2 interface{}) *SQuery { - cond := Between(q.Field(f), v1, v2) - return q.Filter(NOT(cond)) +// NotBetween filters query with a not between condition +func (tq *SQuery) NotBetween(f string, v1, v2 interface{}) *SQuery { + cond := Between(tq.Field(f), v1, v2) + return tq.Filter(NOT(cond)) } -func (q *SQuery) Equals(f string, v interface{}) *SQuery { - cond := Equals(q.Field(f), v) - return q.Filter(cond) +// Equals filters query with a equals condition +func (tq *SQuery) Equals(f string, v interface{}) *SQuery { + cond := Equals(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) NotEquals(f string, v interface{}) *SQuery { - cond := NotEquals(q.Field(f), v) - return q.Filter(cond) +// NotEquals filters the query with a not equals condition +func (tq *SQuery) NotEquals(f string, v interface{}) *SQuery { + cond := NotEquals(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) GE(f string, v interface{}) *SQuery { - cond := GE(q.Field(f), v) - return q.Filter(cond) +// GE filters the query with a >= condition +func (tq *SQuery) GE(f string, v interface{}) *SQuery { + cond := GE(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) LE(f string, v interface{}) *SQuery { - cond := LE(q.Field(f), v) - return q.Filter(cond) +// LE filters the query with a <= condition +func (tq *SQuery) LE(f string, v interface{}) *SQuery { + cond := LE(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) GT(f string, v interface{}) *SQuery { - cond := GT(q.Field(f), v) - return q.Filter(cond) +// GT filters the query with a > condition +func (tq *SQuery) GT(f string, v interface{}) *SQuery { + cond := GT(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) LT(f string, v interface{}) *SQuery { - cond := LT(q.Field(f), v) - return q.Filter(cond) +// LT filters the query with a < condition +func (tq *SQuery) LT(f string, v interface{}) *SQuery { + cond := LT(tq.Field(f), v) + return tq.Filter(cond) } -func (q *SQuery) IsNull(f string) *SQuery { - cond := IsNull(q.Field(f)) - return q.Filter(cond) +// IsNull filters the query with a is null condition +func (tq *SQuery) IsNull(f string) *SQuery { + cond := IsNull(tq.Field(f)) + return tq.Filter(cond) } -func (q *SQuery) IsNotNull(f string) *SQuery { - cond := IsNotNull(q.Field(f)) - return q.Filter(cond) +// IsNotNull filters the query with a is not null condition +func (tq *SQuery) IsNotNull(f string) *SQuery { + cond := IsNotNull(tq.Field(f)) + return tq.Filter(cond) } -func (q *SQuery) IsEmpty(f string) *SQuery { - cond := IsEmpty(q.Field(f)) - return q.Filter(cond) +// IsEmpty filters the query with a is_empty condition +func (tq *SQuery) IsEmpty(f string) *SQuery { + cond := IsEmpty(tq.Field(f)) + return tq.Filter(cond) } -func (q *SQuery) IsNullOrEmpty(f string) *SQuery { - cond := IsNullOrEmpty(q.Field(f)) - return q.Filter(cond) +// IsNullOrEmpty filters the query with a is null or empty condition +func (tq *SQuery) IsNullOrEmpty(f string) *SQuery { + cond := IsNullOrEmpty(tq.Field(f)) + return tq.Filter(cond) } -func (q *SQuery) IsNotEmpty(f string) *SQuery { - cond := IsNotEmpty(q.Field(f)) - return q.Filter(cond) +// IsNotEmpty filters the query with a is not empty condition +func (tq *SQuery) IsNotEmpty(f string) *SQuery { + cond := IsNotEmpty(tq.Field(f)) + return tq.Filter(cond) } -func (q *SQuery) IsTrue(f string) *SQuery { - cond := IsTrue(q.Field(f)) - return q.Filter(cond) +// IsTrue filters the query with a is true condition +func (tq *SQuery) IsTrue(f string) *SQuery { + cond := IsTrue(tq.Field(f)) + return tq.Filter(cond) } -func (q *SQuery) IsFalse(f string) *SQuery { - cond := IsFalse(q.Field(f)) - return q.Filter(cond) +// IsFalse filters the query with a is false condition +func (tq *SQuery) IsFalse(f string) *SQuery { + cond := IsFalse(tq.Field(f)) + return tq.Filter(cond) } diff --git a/vendor/yunion.io/x/sqlchemy/functions.go b/vendor/yunion.io/x/sqlchemy/functions.go index e33dc90a10..d2b8e4ebef 100644 --- a/vendor/yunion.io/x/sqlchemy/functions.go +++ b/vendor/yunion.io/x/sqlchemy/functions.go @@ -22,43 +22,46 @@ import ( "yunion.io/x/log" ) +// IFunction is the interface for a SQL embedded function, such as MIN, MAX, NOW, etc. type IFunction interface { expression() string variables() []interface{} } +// SFunctionFieldBase is a query field that is the result of a SQL embedded function, e.g. COUNT(*) as count type SFunctionFieldBase struct { IFunction alias string } +// Reference implementation of SFunctionFieldBase for IQueryField func (ff *SFunctionFieldBase) Reference() string { if len(ff.alias) == 0 { log.Warningf("reference a function field without alias! %s", ff.expression()) return ff.expression() - } else { - return fmt.Sprintf("`%s`", ff.alias) } + return fmt.Sprintf("`%s`", ff.alias) } +// Expression implementation of SFunctionFieldBase for IQueryField func (ff *SFunctionFieldBase) Expression() string { if len(ff.alias) > 0 { // add alias return fmt.Sprintf("%s AS `%s`", ff.expression(), ff.alias) - } else { - // no alias - return ff.expression() } + // no alias + return ff.expression() } +// Name implementation of SFunctionFieldBase for IQueryField func (ff *SFunctionFieldBase) Name() string { if len(ff.alias) > 0 { return ff.alias - } else { - return ff.expression() } + return ff.expression() } +// Label implementation of SFunctionFieldBase for IQueryField func (ff *SFunctionFieldBase) Label(label string) IQueryField { if len(label) > 0 && label != ff.alias { ff.alias = label @@ -66,16 +69,17 @@ func (ff *SFunctionFieldBase) Label(label string) IQueryField { return ff } +// Variables implementation of SFunctionFieldBase for IQueryField func (ff *SFunctionFieldBase) Variables() []interface{} { return ff.variables() } -type SExprFunction struct { +type sExprFunction struct { fields []IQueryField function string } -func (ff *SExprFunction) expression() string { +func (ff *sExprFunction) expression() string { fieldRefs := make([]interface{}, 0) for _, f := range ff.fields { fieldRefs = append(fieldRefs, f.Reference()) @@ -83,7 +87,7 @@ func (ff *SExprFunction) expression() string { return fmt.Sprintf(ff.function, fieldRefs...) } -func (ff *SExprFunction) variables() []interface{} { +func (ff *sExprFunction) variables() []interface{} { vars := make([]interface{}, 0) for _, f := range ff.fields { fromVars := f.Variables() @@ -92,8 +96,9 @@ func (ff *SExprFunction) variables() []interface{} { return vars } +// NewFunctionField returns an instance of query field by calling a SQL embedded function func NewFunctionField(name string, funcexp string, fields ...IQueryField) IQueryField { - funcBase := &SExprFunction{ + funcBase := &sExprFunction{ fields: fields, function: funcexp, } @@ -103,6 +108,7 @@ func NewFunctionField(name string, funcexp string, fields ...IQueryField) IQuery } } +// COUNT represents the SQL function COUNT func COUNT(name string, field ...IQueryField) IQueryField { var expr string if len(field) == 0 { @@ -113,47 +119,58 @@ func COUNT(name string, field ...IQueryField) IQueryField { return NewFunctionField(name, expr, field...) } +// MAX represents the SQL function MAX func MAX(name string, field IQueryField) IQueryField { return NewFunctionField(name, "MAX(%s)", field) } +// MIN represents the SQL function MIN func MIN(name string, field IQueryField) IQueryField { return NewFunctionField(name, "MIN(%s)", field) } +// SUM represents the SQL function SUM func SUM(name string, field IQueryField) IQueryField { return NewFunctionField(name, "SUM(%s)", field) } +// DISTINCT represents the SQL function DISTINCT func DISTINCT(name string, field IQueryField) IQueryField { return NewFunctionField(name, "DISTINCT(%s)", field) } +// GROUP_CONCAT represents the SQL function GROUP_CONCAT func GROUP_CONCAT(name string, field IQueryField) IQueryField { return NewFunctionField(name, "GROUP_CONCAT(%s)", field) } +// REPLACE represents the SQL function REPLACE func REPLACE(name string, field IQueryField, old string, new string) IQueryField { return NewFunctionField(name, fmt.Sprintf(`REPLACE(%s, "%s", "%s")`, "%s", old, new), field) } +// SConstField is a query field of a constant type SConstField struct { constVar interface{} alias string } +// Expression implementation of SConstField for IQueryField func (s *SConstField) Expression() string { return fmt.Sprintf("%s AS `%s`", s.Reference(), s.Name()) } +// Name implementation of SConstField for IQueryField func (s *SConstField) Name() string { return s.alias } +// Reference implementation of SConstField for IQueryField func (s *SConstField) Reference() string { return getQuoteStringValue(s.constVar) } +// Label implementation of SConstField for IQueryField func (s *SConstField) Label(label string) IQueryField { if len(label) > 0 { s.alias = label @@ -161,31 +178,38 @@ func (s *SConstField) Label(label string) IQueryField { return s } +// Variables implementation of SConstField for IQueryField func (s *SConstField) Variables() []interface{} { return nil } +// NewConstField returns an instance of SConstField func NewConstField(variable interface{}) *SConstField { return &SConstField{constVar: variable} } +// SStringField is a query field of a string constant type SStringField struct { strConst string alias string } +// Expression implementation of SStringField for IQueryField func (s *SStringField) Expression() string { return fmt.Sprintf("%s AS `%s`", s.Reference(), s.Name()) } +// Name implementation of SStringField for IQueryField func (s *SStringField) Name() string { return s.alias } +// Reference implementation of SStringField for IQueryField func (s *SStringField) Reference() string { return strconv.Quote(s.strConst) } +// Label implementation of SStringField for IQueryField func (s *SStringField) Label(label string) IQueryField { if len(label) > 0 { s.alias = label @@ -193,14 +217,17 @@ func (s *SStringField) Label(label string) IQueryField { return s } +// Variables implementation of SStringField for IQueryField func (s *SStringField) Variables() []interface{} { return nil } +// NewStringField returns an instance of SStringField func NewStringField(name string) *SStringField { return &SStringField{strConst: name} } +// CONCAT represents a SQL function CONCAT func CONCAT(name string, fields ...IQueryField) IQueryField { params := []string{} for i := 0; i < len(fields); i++ { @@ -209,6 +236,7 @@ func CONCAT(name string, fields ...IQueryField) IQueryField { return NewFunctionField(name, `CONCAT(`+strings.Join(params, ",")+`)`, fields...) } +// SubStr represents a SQL function SUBSTR func SubStr(name string, field IQueryField, pos, length int) IQueryField { var rightStr string if length <= 0 { @@ -219,20 +247,24 @@ func SubStr(name string, field IQueryField, pos, length int) IQueryField { return NewFunctionField(name, `SUBSTR(%s, `+rightStr, field) } +// OR_Val represents a SQL function that does binary | operation on a field func OR_Val(name string, field IQueryField, v interface{}) IQueryField { rightStr := fmt.Sprintf("|%v", v) return NewFunctionField(name, "%s"+rightStr, field) } +// AND_Val represents a SQL function that does binary & operation on a field func AND_Val(name string, field IQueryField, v interface{}) IQueryField { rightStr := fmt.Sprintf("&%v", v) return NewFunctionField(name, "%s"+rightStr, field) } +// INET_ATON represents a SQL function INET_ATON func INET_ATON(field IQueryField) IQueryField { return NewFunctionField("", `INET_ATON(%s)`, field) } +// TimestampAdd represents a SQL function TimestampAdd func TimestampAdd(name string, field IQueryField, offsetSeconds int) IQueryField { return NewFunctionField(name, `TIMESTAMPADD(SECOND, `+fmt.Sprintf("%d", offsetSeconds)+`, %s)`, field) } diff --git a/vendor/yunion.io/x/sqlchemy/inc.go b/vendor/yunion.io/x/sqlchemy/inc.go index 63a51a834a..399db674d8 100644 --- a/vendor/yunion.io/x/sqlchemy/inc.go +++ b/vendor/yunion.io/x/sqlchemy/inc.go @@ -26,10 +26,16 @@ import ( "yunion.io/x/pkg/util/reflectutils" ) +// Increment perform an incremental update on a record, the primary key of the record is specified in diff, +// the numeric fields of this record will be atomically added by the value of the corresponding field in diff +// if target is given as a pointer to a variable, the result will be stored in the target +// if target is not given, the updated result will be stored in diff func (t *STableSpec) Increment(diff interface{}, target interface{}) error { return t.incrementInternal(diff, "+", target) } +// Decrement is similar to Increment methods, the difference is that this method will atomically decrease the numeric fields +// with the value of diff func (t *STableSpec) Decrement(diff interface{}, target interface{}) error { return t.incrementInternal(diff, "-", target) } @@ -144,9 +150,8 @@ func (t *STableSpec) incrementInternal(diff interface{}, opcode string, target i if aCnt != 1 { if aCnt == 0 { return sql.ErrNoRows - } else { - return errors.Wrapf(ErrUnexpectRowCount, "affected rows %d != 1", aCnt) } + return errors.Wrapf(ErrUnexpectRowCount, "affected rows %d != 1", aCnt) } q := t.Query() for k, v := range primaries { diff --git a/vendor/yunion.io/x/sqlchemy/index.go b/vendor/yunion.io/x/sqlchemy/index.go index 5d29543be0..ee30008fd9 100644 --- a/vendor/yunion.io/x/sqlchemy/index.go +++ b/vendor/yunion.io/x/sqlchemy/index.go @@ -19,13 +19,13 @@ import ( "strings" ) -type STableIndex struct { +type sTableIndex struct { name string columns []string isUnique bool } -type TColumnNames []string +/*type TColumnNames []string func (cols TColumnNames) Len() int { return len(cols) @@ -41,13 +41,13 @@ func (cols TColumnNames) Less(i, j int) bool { } else { return false } -} +}*/ -func (index *STableIndex) IsIdentical(cols ...string) bool { +func (index *sTableIndex) IsIdentical(cols ...string) bool { if len(index.columns) != len(cols) { return false } - for i := 0; i < len(index.columns); i += 1 { + for i := 0; i < len(index.columns); i++ { if index.columns[i] != cols[i] { return false } @@ -55,22 +55,24 @@ func (index *STableIndex) IsIdentical(cols ...string) bool { return true } -func (index *STableIndex) QuotedColumns() []string { +func (index *sTableIndex) QuotedColumns() []string { ret := make([]string, len(index.columns)) - for i := 0; i < len(ret); i += 1 { + for i := 0; i < len(ret); i++ { ret[i] = fmt.Sprintf("`%s`", index.columns[i]) } return ret } +// AddIndex adds a SQL index over multiple columns for a Table +// param unique: indicates a unique index cols: name of columns func (ts *STableSpec) AddIndex(unique bool, cols ...string) bool { - for i := 0; i < len(ts.indexes); i += 1 { + for i := 0; i < len(ts.indexes); i++ { if ts.indexes[i].IsIdentical(cols...) { return false } } name := fmt.Sprintf("ix_%s_%s", ts.name, strings.Join(cols, "_")) - idx := STableIndex{name: name, columns: cols, isUnique: unique} + idx := sTableIndex{name: name, columns: cols, isUnique: unique} ts.indexes = append(ts.indexes, idx) return true } diff --git a/vendor/yunion.io/x/sqlchemy/insert.go b/vendor/yunion.io/x/sqlchemy/insert.go index f75bf95f01..95eb1207ca 100644 --- a/vendor/yunion.io/x/sqlchemy/insert.go +++ b/vendor/yunion.io/x/sqlchemy/insert.go @@ -25,11 +25,12 @@ import ( "yunion.io/x/pkg/util/reflectutils" ) +// Insert perform a insert operation, the value of the record is store in dt func (t *STableSpec) Insert(dt interface{}) error { return t.insert(dt, false, false) } -// +// InsertOrUpdate perform a insert or update operation, the value of the record is string in dt // MySQL: INSERT INTO ... ON DUPLICATE KEY UPDATE ... // works only for the cases that all values of primary keys are determeted before insert func (t *STableSpec) InsertOrUpdate(dt interface{}) error { @@ -222,9 +223,8 @@ func (t *STableSpec) insert(data interface{}, update bool, debug bool) error { lastId, err := results.LastInsertId() if err != nil { return errors.Wrap(err, "fetching lastInsertId failed") - } else { - q = q.Equals(c.Name(), lastId) } + q = q.Equals(c.Name(), lastId) } else { priVal, _ := dataFields.GetInterface(c.Name()) if !gotypes.IsNil(priVal) { diff --git a/vendor/yunion.io/x/sqlchemy/parser.go b/vendor/yunion.io/x/sqlchemy/parser.go index 3e069a9cac..f02284ebe2 100644 --- a/vendor/yunion.io/x/sqlchemy/parser.go +++ b/vendor/yunion.io/x/sqlchemy/parser.go @@ -94,14 +94,13 @@ func getFiledTypeCol(fieldType reflect.Type, fieldname string, tagmap map[string if _, ok := tagmap[TAG_WIDTH]; ok { col := NewDecimalColumn(fieldname, tagmap, isPointer) return &col - } else { - colType := "FLOAT" - if fieldType == gotypes.Float64Type { - colType = "DOUBLE" - } - col := NewFloatColumn(fieldname, colType, tagmap, isPointer) - return &col } + colType := "FLOAT" + if fieldType == gotypes.Float64Type { + colType = "DOUBLE" + } + col := NewFloatColumn(fieldname, colType, tagmap, isPointer) + return &col } if fieldType.Implements(gotypes.ISerializableType) { col := NewCompoundColumn(fieldname, tagmap, isPointer) @@ -113,11 +112,11 @@ func getFiledTypeCol(fieldType reflect.Type, fieldname string, tagmap map[string func struct2TableSpec(sv reflect.Value, table *STableSpec) { fields := reflectutils.FetchStructFieldValueSet(sv) autoIncCnt := 0 - for i := 0; i < len(fields); i += 1 { + for i := 0; i < len(fields); i++ { column := structField2ColumnSpec(&fields[i]) if column != nil { if intC, ok := column.(*SIntegerColumn); ok && intC.IsAutoIncrement { - autoIncCnt += 1 + autoIncCnt++ if autoIncCnt > 1 { panic(fmt.Sprintf("Table %s contains multiple autoincremental columns!!", table.name)) } diff --git a/vendor/yunion.io/x/sqlchemy/query.go b/vendor/yunion.io/x/sqlchemy/query.go index d725f8f7fa..460fa1f03f 100644 --- a/vendor/yunion.io/x/sqlchemy/query.go +++ b/vendor/yunion.io/x/sqlchemy/query.go @@ -27,81 +27,111 @@ import ( "yunion.io/x/pkg/util/reflectutils" ) +// IQuery is an interface that reprsents a SQL query, e.g. +// SELECT ... FROM ... WHERE ... type IQuery interface { // queryString String(fields ...IQueryField) string - // fields after select + + // fields in the select clause QueryFields() []IQueryField + // variables in statement Variables() []interface{} - // convert to a subquery + + // convert this SQL to a subquery SubQuery() *SSubQuery + // reference to a field by name Field(name string) IQueryField } +// IQuerySource is an interface that represents a data source of a SQL query. the source can be a table or a subquery +// e.g. SELECT ... FROM (SELECT * FROM tbl) AS A type IQuerySource interface { // string in select ... from (expresson here) Expression() string + // alias in select ... from (express) as alias Alias() string + // variables in statement Variables() []interface{} + // reference to a field by name, optionally giving an alias name Field(id string, alias ...string) IQueryField + // return all the fields that this source provides Fields() []IQueryField } +// IQueryField is an interface that represents a select field in a SQL query type IQueryField interface { // the string after select Expression() string + // the name of thie field Name() string - // the string in where clause + + // the reference string in where clause Reference() string + // give this field an alias name Label(label string) IQueryField + // return variables Variables() []interface{} } +// Expression implementation of STable for IQuerySource func (tbl *STable) Expression() string { return tbl.spec.Expression() } +// Alias implementation of STable for IQuerySource func (tbl *STable) Alias() string { return tbl.alias } +// Variables implementation of STable for IQuerySource func (tbl *STable) Variables() []interface{} { return []interface{}{} } +// QueryJoinType is the Join type of SQL query, namely, innerjoin, leftjoin and rightjoin type QueryJoinType string const ( + // INNERJOIN represents innerjoin INNERJOIN QueryJoinType = "JOIN" - LEFTJOIN QueryJoinType = "LEFT JOIN" + + // LEFTJOIN represents left join + LEFTJOIN QueryJoinType = "LEFT JOIN" + + // RIGHTJOIN represents right-join RIGHTJOIN QueryJoinType = "RIGHT JOIN" + // FULLJOIN QueryJoinType = "FULLJOIN" ) -type SQueryJoin struct { +// sQueryJoin represents the state of a Join Query +type sQueryJoin struct { jointype QueryJoinType from IQuerySource condition ICondition } +// SQuery is a data structure represents a SQL query in the form of +// SELECT ... FROM ... JOIN ... ON ... WHERE ... GROUP BY ... ORDER BY ... HAVING ... type SQuery struct { rawSql string fields []IQueryField distinct bool from IQuerySource - joins []SQueryJoin + joins []sQueryJoin where ICondition groupBy []IQueryField - orderBy []SQueryOrder + orderBy []sQueryOrder having ICondition limit int offset int @@ -111,6 +141,9 @@ type SQuery struct { snapshot string } +// SSubQuery represents a subquery. A subquery is a query used as a query source +// SSubQuery should implementation IQuerySource +// At the same time, a subquery can be used in condition. e.g. IN condition type SSubQuery struct { query IQuery alias string @@ -118,32 +151,35 @@ type SSubQuery struct { referedFields map[string]IQueryField } +// SSubQueryField represents a field of subquery, which implements IQueryField type SSubQueryField struct { field IQueryField query *SSubQuery alias string } +// Expression implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) Expression() string { if len(sqf.alias) > 0 { return fmt.Sprintf("`%s`.`%s` AS `%s`", sqf.query.alias, sqf.field.Name(), sqf.alias) - } else { - return fmt.Sprintf("`%s`.`%s`", sqf.query.alias, sqf.field.Name()) } + return fmt.Sprintf("`%s`.`%s`", sqf.query.alias, sqf.field.Name()) } +// Name implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) Name() string { if len(sqf.alias) > 0 { return sqf.alias - } else { - return sqf.field.Name() } + return sqf.field.Name() } +// Reference implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) Reference() string { return fmt.Sprintf("`%s`.`%s`", sqf.query.alias, sqf.Name()) } +// Label implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) Label(label string) IQueryField { if len(label) > 0 && label != sqf.field.Name() { sqf.alias = label @@ -151,10 +187,12 @@ func (sqf *SSubQueryField) Label(label string) IQueryField { return sqf } +// Variables implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) Variables() []interface{} { return nil } +// Expression implementation of SSubQuery for IQuerySource func (sq *SSubQuery) Expression() string { fields := make([]IQueryField, 0) for k := range sq.referedFields { @@ -167,10 +205,12 @@ func (sq *SSubQuery) Expression() string { return fmt.Sprintf("(%s)", sq.query.String(fields...)) } +// Alias implementation of SSubQuery for IQuerySource func (sq *SSubQuery) Alias() string { return sq.alias } +// Variables implementation of SSubQuery for IQuerySource func (sq *SSubQuery) Variables() []interface{} { return sq.query.Variables() } @@ -192,6 +232,7 @@ func (sq *SSubQuery) findField(id string) IQueryField { return nil } +// Field implementation of SSubQuery for IQuerySource func (sq *SSubQuery) Field(id string, alias ...string) IQueryField { f := sq.findField(id) if f == nil { @@ -204,6 +245,7 @@ func (sq *SSubQuery) Field(id string, alias ...string) IQueryField { return &sqf } +// Fields implementation of SSubQuery for IQuerySource func (sq *SSubQuery) Fields() []IQueryField { ret := make([]IQueryField, 0) for _, f := range sq.query.QueryFields() { @@ -213,6 +255,7 @@ func (sq *SSubQuery) Fields() []IQueryField { return ret } +// DoQuery returns a SQuery instance that query specified fields from a query source func DoQuery(from IQuerySource, f ...IQueryField) *SQuery { // if len(f) == 0 { // f = from.Fields() @@ -221,57 +264,68 @@ func DoQuery(from IQuerySource, f ...IQueryField) *SQuery { return &tq } -func (q *SQuery) AppendField(f ...IQueryField) *SQuery { - q.fields = append(q.fields, f...) - return q +// AppendField appends query field to a query +func (tq *SQuery) AppendField(f ...IQueryField) *SQuery { + tq.fields = append(tq.fields, f...) + return tq } -func (table *SSubQuery) Query(f ...IQueryField) *SQuery { - return DoQuery(table, f...) +// Query of SSubQuery generates a new query from a subquery +func (sq *SSubQuery) Query(f ...IQueryField) *SQuery { + return DoQuery(sq, f...) } +// Query of STable generates a new query from a table func (tbl *STable) Query(f ...IQueryField) *SQuery { return DoQuery(tbl, f...) } +// Query of STableSpec generates a new query from a STableSpec instance func (ts *STableSpec) Query(f ...IQueryField) *SQuery { return ts.Instance().Query(f...) } +// QueryOrderType indicates the query order type, either ASC or DESC type QueryOrderType string const ( - SQL_ORDER_ASC QueryOrderType = "ASC" + // SQL_ORDER_ASC represents Ascending order + SQL_ORDER_ASC QueryOrderType = "ASC" + + // SQL_ORDER_DESC represents Descending order SQL_ORDER_DESC QueryOrderType = "DESC" ) +// Equals of QueryOrderType determines whether two order type identical func (qot QueryOrderType) Equals(orderType string) bool { if strings.ToUpper(orderType) == string(qot) { return true - } else { - return false } + return false } -type SQueryOrder struct { +// internal structure to store state of query order +type sQueryOrder struct { field IQueryField order QueryOrderType } func (tq *SQuery) _orderBy(order QueryOrderType, fields []IQueryField) *SQuery { if tq.orderBy == nil { - tq.orderBy = make([]SQueryOrder, 0) + tq.orderBy = make([]sQueryOrder, 0) } for i := range fields { - tq.orderBy = append(tq.orderBy, SQueryOrder{field: fields[i], order: order}) + tq.orderBy = append(tq.orderBy, sQueryOrder{field: fields[i], order: order}) } return tq } +// Asc of SQuery does query in ascending order of specified fields func (tq *SQuery) Asc(fields ...interface{}) *SQuery { return tq._orderBy(SQL_ORDER_ASC, convertQueryField(tq, fields)) } +// Desc of SQuery does query in descending order of specified fields func (tq *SQuery) Desc(fields ...interface{}) *SQuery { return tq._orderBy(SQL_ORDER_DESC, convertQueryField(tq, fields)) } @@ -291,6 +345,7 @@ func convertQueryField(tq IQuery, fields []interface{}) []IQueryField { return nFields } +// GroupBy of SQuery does query group by specified fields func (tq *SQuery) GroupBy(f ...interface{}) *SQuery { if tq.groupBy == nil { tq.groupBy = make([]IQueryField, 0) @@ -300,24 +355,27 @@ func (tq *SQuery) GroupBy(f ...interface{}) *SQuery { return tq } +// Limit of SQuery adds limit to a query func (tq *SQuery) Limit(limit int) *SQuery { tq.limit = limit return tq } +// Offset of SQuery adds offset to a query func (tq *SQuery) Offset(offset int) *SQuery { tq.offset = offset return tq } +// QueryFields of SQuery returns fields in SELECT clause of a query func (tq *SQuery) QueryFields() []IQueryField { if len(tq.fields) > 0 { return tq.fields - } else { - return tq.from.Fields() } + return tq.from.Fields() } +// String of SQuery implemetation of SQuery for IQuery func (tq *SQuery) String(fields ...IQueryField) string { sql := queryString(tq, fields...) // log.Debugf("Query: %s", sql) @@ -395,14 +453,17 @@ func queryString(tq *SQuery, tmpFields ...IQueryField) string { return buf.String() } +// Join of SQuery joins query with another IQuerySource on specified condition func (tq *SQuery) Join(from IQuerySource, on ICondition) *SQuery { return tq._join(from, on, INNERJOIN) } +// LeftJoin of SQuery left-joins query with another IQuerySource on specified condition func (tq *SQuery) LeftJoin(from IQuerySource, on ICondition) *SQuery { return tq._join(from, on, LEFTJOIN) } +// RightJoin of SQuery right-joins query with another IQuerySource on specified condition func (tq *SQuery) RightJoin(from IQuerySource, on ICondition) *SQuery { return tq._join(from, on, RIGHTJOIN) } @@ -413,13 +474,14 @@ func (tq *SQuery) RightJoin(from IQuerySource, on ICondition) *SQuery { func (tq *SQuery) _join(from IQuerySource, on ICondition, joinType QueryJoinType) *SQuery { if tq.joins == nil { - tq.joins = make([]SQueryJoin, 0) + tq.joins = make([]sQueryJoin, 0) } - qj := SQueryJoin{jointype: joinType, from: from, condition: on} + qj := sQueryJoin{jointype: joinType, from: from, condition: on} tq.joins = append(tq.joins, qj) return tq } +// Variables implementation of SQuery for IQuery func (tq *SQuery) Variables() []interface{} { vars := make([]interface{}, 0) var fromvars []interface{} @@ -449,34 +511,40 @@ func (tq *SQuery) Variables() []interface{} { return vars } +// Distinct of SQuery indicates a distinct query results func (tq *SQuery) Distinct() *SQuery { tq.distinct = true return tq } +// SubQuery of SQuery generates a SSubQuery from a Query func (tq *SQuery) SubQuery() *SSubQuery { sq := SSubQuery{query: tq, alias: getTableAliasName()} return &sq } +// Row of SQuery returns an instance of sql.Row for native data fetching func (tq *SQuery) Row() *sql.Row { sqlstr := tq.String() vars := tq.Variables() if DEBUG_SQLCHEMY { - log.Debugf("SQuery %s with vars: %s", sqlstr, vars) + sqlDebug(sqlstr, vars) } return _db.QueryRow(sqlstr, vars...) } +// Rows of SQuery returns an instance of sql.Rows for native data fetching func (tq *SQuery) Rows() (*sql.Rows, error) { sqlstr := tq.String() vars := tq.Variables() if DEBUG_SQLCHEMY { - log.Debugf("SQuery %s with vars: %s", sqlstr, vars) + sqlDebug(sqlstr, vars) } return _db.Query(sqlstr, vars...) } +// Count of SQuery returns the count of a query +// use CountWithError instead // deprecated func (tq *SQuery) Count() int { cnt, _ := tq.CountWithError() @@ -496,6 +564,7 @@ func (tq *SQuery) countQuery() *SQuery { return cq } +// CountWithError of SQuery returns the row count of a query func (tq *SQuery) CountWithError() (int, error) { cq := tq.countQuery() count := 0 @@ -507,6 +576,7 @@ func (tq *SQuery) CountWithError() (int, error) { return -1, err } +// Field implementation of SQuery for IQuery func (tq *SQuery) Field(name string) IQueryField { f := tq.findField(name) if DEBUG_SQLCHEMY && f == nil { @@ -562,6 +632,7 @@ func (tq *SQuery) internalFindField(name string) IQueryField { return nil } +// IRowScanner is an interface for sql data fetching type IRowScanner interface { Scan(desc ...interface{}) error } @@ -590,8 +661,8 @@ func rowScan2StringMap(fields []string, row IRowScanner) (map[string]string, err return results, nil } -func (q *SQuery) rowScan2StringMap(row IRowScanner) (map[string]string, error) { - queryFields := q.QueryFields() +func (tq *SQuery) rowScan2StringMap(row IRowScanner) (map[string]string, error) { + queryFields := tq.QueryFields() fields := make([]string, len(queryFields)) for i, f := range queryFields { fields[i] = f.Name() @@ -599,19 +670,21 @@ func (q *SQuery) rowScan2StringMap(row IRowScanner) (map[string]string, error) { return rowScan2StringMap(fields, row) } -func (q *SQuery) FirstStringMap() (map[string]string, error) { - return q.rowScan2StringMap(q.Row()) +// FirstStringMap returns query result of the first row in a stringmap(map[string]string) +func (tq *SQuery) FirstStringMap() (map[string]string, error) { + return tq.rowScan2StringMap(tq.Row()) } -func (q *SQuery) AllStringMap() ([]map[string]string, error) { - rows, err := q.Rows() +// AllStringMap returns query result of all rows in an array of stringmap(map[string]string) +func (tq *SQuery) AllStringMap() ([]map[string]string, error) { + rows, err := tq.Rows() if err != nil { return nil, err } defer rows.Close() results := make([]map[string]string, 0) for rows.Next() { - result, err := q.rowScan2StringMap(rows) + result, err := tq.rowScan2StringMap(rows) if err != nil { return nil, err } @@ -644,8 +717,9 @@ func callAfterQuery(val reflect.Value) { } } -func (q *SQuery) First(dest interface{}) error { - mapResult, err := q.FirstStringMap() +// First return query result of first row and store the result in a data struct +func (tq *SQuery) First(dest interface{}) error { + mapResult, err := tq.FirstStringMap() if err != nil { return err } @@ -662,7 +736,8 @@ func (q *SQuery) First(dest interface{}) error { return nil } -func (q *SQuery) All(dest interface{}) error { +// All return query results of all rows and store the result in an array of data struct +func (tq *SQuery) All(dest interface{}) error { arrayType := reflect.TypeOf(dest).Elem() if arrayType.Kind() != reflect.Array && arrayType.Kind() != reflect.Slice { @@ -670,7 +745,7 @@ func (q *SQuery) All(dest interface{}) error { } elemType := arrayType.Elem() - mapResults, err := q.AllStringMap() + mapResults, err := tq.AllStringMap() if err != nil { return err } @@ -690,11 +765,13 @@ func (q *SQuery) All(dest interface{}) error { return err } -func (q *SQuery) Row2Map(row IRowScanner) (map[string]string, error) { - return q.rowScan2StringMap(row) +// Row2Map is a utility function that fetch stringmap(map[string]string) from a native sql.Row or sql.Rows +func (tq *SQuery) Row2Map(row IRowScanner) (map[string]string, error) { + return tq.rowScan2StringMap(row) } -func (q *SQuery) RowMap2Struct(result map[string]string, dest interface{}) error { +// RowMap2Struct is a utility function that fetch struct from a native sql.Row or sql.Rows +func (tq *SQuery) RowMap2Struct(result map[string]string, dest interface{}) error { destPtrValue := reflect.ValueOf(dest) if destPtrValue.Kind() != reflect.Ptr { return errors.Wrap(ErrNeedsPointer, "input must be a pointer") @@ -709,22 +786,25 @@ func (q *SQuery) RowMap2Struct(result map[string]string, dest interface{}) error return nil } -func (q *SQuery) Row2Struct(row IRowScanner, dest interface{}) error { - result, err := q.rowScan2StringMap(row) +// Row2Struct is a utility function that fill a struct with the value of a sql.Row or sql.Rows +func (tq *SQuery) Row2Struct(row IRowScanner, dest interface{}) error { + result, err := tq.rowScan2StringMap(row) if err != nil { return err } - return q.RowMap2Struct(result, dest) + return tq.RowMap2Struct(result, dest) } -func (q *SQuery) Snapshot() *SQuery { - q.snapshot = q.String() - return q +// Snapshot of SQuery take a snapshot of the query, so we can tell wether the query is modified later by comparing the SQL with snapshot +func (tq *SQuery) Snapshot() *SQuery { + tq.snapshot = tq.String() + return tq } -func (q *SQuery) IsAltered() bool { - if len(q.snapshot) == 0 { - panic(fmt.Sprintf("Query %s has never been snapshot when IsAltered called", q.String())) +// IsAltered of SQuery indicates whether a query was altered. By comparing with the saved query snapshot, we can tell whether a query is altered +func (tq *SQuery) IsAltered() bool { + if len(tq.snapshot) == 0 { + panic(fmt.Sprintf("Query %s has never been snapshot when IsAltered called", tq.String())) } - return q.String() != q.snapshot + return tq.String() != tq.snapshot } diff --git a/vendor/yunion.io/x/sqlchemy/rawquery.go b/vendor/yunion.io/x/sqlchemy/rawquery.go index 50e9840a5d..a466ec17c8 100644 --- a/vendor/yunion.io/x/sqlchemy/rawquery.go +++ b/vendor/yunion.io/x/sqlchemy/rawquery.go @@ -14,30 +14,40 @@ package sqlchemy +// SRawQueryField is a struct represents a field of a raw SQL query +// a raw query is a query that not follow standard SELECT ... FROM ... pattern +// e.g. show tables +// the struct implements IQueryField interface type SRawQueryField struct { name string } +// Expression implementation of SRawQueryField for IQueryField func (rqf *SRawQueryField) Expression() string { return rqf.name } +// Name implementation of SRawQueryField for IQueryField func (rqf *SRawQueryField) Name() string { return rqf.name } +// Reference implementation of SRawQueryField for IQueryField func (rqf *SRawQueryField) Reference() string { return rqf.name } +// Label implementation of SRawQueryField for IQueryField func (rqf *SRawQueryField) Label(label string) IQueryField { return rqf } +// Variables implementation of SRawQueryField for IQueryField func (rqf *SRawQueryField) Variables() []interface{} { return nil } +// NewRawQuery returns an instance of SQuery with raw SQL query. e.g. show tables func NewRawQuery(sqlStr string, fields ...string) *SQuery { qfs := make([]IQueryField, len(fields)) for i, f := range fields { diff --git a/vendor/yunion.io/x/sqlchemy/reflect.go b/vendor/yunion.io/x/sqlchemy/reflect.go index 6568e45b4d..0e2ddaa829 100644 --- a/vendor/yunion.io/x/sqlchemy/reflect.go +++ b/vendor/yunion.io/x/sqlchemy/reflect.go @@ -73,9 +73,8 @@ func getStringValue(dat interface{}) string { case reflect.Bool: if value.Bool() { return "true" - } else { - return "false" } + return "false" case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return fmt.Sprintf("%d", value.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: diff --git a/vendor/yunion.io/x/sqlchemy/sql.go b/vendor/yunion.io/x/sqlchemy/sql.go index 695ec15f18..bf6ea29697 100644 --- a/vendor/yunion.io/x/sqlchemy/sql.go +++ b/vendor/yunion.io/x/sqlchemy/sql.go @@ -20,16 +20,20 @@ import ( "yunion.io/x/log" ) +// the global DB connection var _db *sql.DB +// SetDB sets global DB instance func SetDB(db *sql.DB) { _db = db } +// GetDB get DB instance func GetDB() *sql.DB { return _db } +// CloseDB close DB connection func CloseDB() { _db.Close() _db = nil @@ -39,6 +43,7 @@ type tableName struct { Name string } +// GetTables get all tables' name in database func GetTables() []string { tables := make([]tableName, 0) q := NewRawQuery("SHOW TABLES", "name") @@ -54,6 +59,7 @@ func GetTables() []string { return ret } +// Exec execute a raw SQL query func Exec(sql string, args ...interface{}) (sql.Result, error) { return _db.Exec(sql, args...) } diff --git a/vendor/yunion.io/x/sqlchemy/sync.go b/vendor/yunion.io/x/sqlchemy/sync.go index cf397c98e0..3fdc0e5e6a 100644 --- a/vendor/yunion.io/x/sqlchemy/sync.go +++ b/vendor/yunion.io/x/sqlchemy/sync.go @@ -27,7 +27,7 @@ import ( "yunion.io/x/pkg/utils" ) -type SSqlColumnInfo struct { +type sSqlColumnInfo struct { Field string Type string Collation string @@ -44,13 +44,12 @@ func decodeSqlTypeString(typeStr string) []string { matches := typeReg.FindStringSubmatch(typeStr) if len(matches) >= 3 { return matches[1:] - } else { - parts := strings.Split(typeStr, " ") - return []string{parts[0]} } + parts := strings.Split(typeStr, " ") + return []string{parts[0]} } -func (info *SSqlColumnInfo) toColumnSpec() IColumnSpec { +func (info *sSqlColumnInfo) toColumnSpec() IColumnSpec { tagmap := make(map[string]string) matches := decodeSqlTypeString(info.Type) @@ -151,7 +150,7 @@ func (info *SSqlColumnInfo) toColumnSpec() IColumnSpec { func (ts *STableSpec) fetchColumnDefs() ([]IColumnSpec, error) { sql := fmt.Sprintf("SHOW FULL COLUMNS IN `%s`", ts.name) query := NewRawQuery(sql, "field", "type", "collation", "null", "key", "default", "extra", "privileges", "comment") - infos := make([]SSqlColumnInfo, 0) + infos := make([]sSqlColumnInfo, 0) err := query.All(&infos) if err != nil { return nil, err @@ -163,7 +162,7 @@ func (ts *STableSpec) fetchColumnDefs() ([]IColumnSpec, error) { return specs, nil } -func (ts *STableSpec) fetchIndexesAndConstraints() ([]STableIndex, []STableConstraint, error) { +func (ts *STableSpec) fetchIndexesAndConstraints() ([]sTableIndex, []sTableConstraint, error) { sql := fmt.Sprintf("SHOW CREATE TABLE `%s`", ts.name) query := NewRawQuery(sql, "table", "create table") row := query.Row() @@ -218,31 +217,31 @@ func diffCols(tableName string, cols1 []IColumnSpec, cols2 []IColumnSpec) ([]ICo newCol: cols2[j], }) } - i += 1 - j += 1 + i++ + j++ } else if comp > 0 { add = append(add, cols2[j]) - j += 1 + j++ } else { remove = append(remove, cols1[i]) - i += 1 + i++ } } else if i < len(cols1) { remove = append(remove, cols1[i]) - i += 1 + i++ } else if j < len(cols2) { add = append(add, cols2[j]) - j += 1 + j++ } } return remove, update, add } -func diffIndexes2(exists []STableIndex, defs []STableIndex) (diff []STableIndex) { - diff = make([]STableIndex, 0) - for i := 0; i < len(exists); i += 1 { +func diffIndexes2(exists []sTableIndex, defs []sTableIndex) (diff []sTableIndex) { + diff = make([]sTableIndex, 0) + for i := 0; i < len(exists); i++ { findDef := false - for j := 0; j < len(defs); j += 1 { + for j := 0; j < len(defs); j++ { if defs[j].IsIdentical(exists[i].columns...) { findDef = true break @@ -255,10 +254,11 @@ func diffIndexes2(exists []STableIndex, defs []STableIndex) (diff []STableIndex) return } -func diffIndexes(exists []STableIndex, defs []STableIndex) (added []STableIndex, removed []STableIndex) { +func diffIndexes(exists []sTableIndex, defs []sTableIndex) (added []sTableIndex, removed []sTableIndex) { return diffIndexes2(defs, exists), diffIndexes2(exists, defs) } +// DropForeignKeySQL returns the SQL statements to do droping foreignkey for a TableSpec func (ts *STableSpec) DropForeignKeySQL() []string { _, constraints, err := ts.fetchIndexesAndConstraints() if err != nil { @@ -278,12 +278,15 @@ func (ts *STableSpec) DropForeignKeySQL() []string { return ret } +// Exists checks wheter a table exists func (ts *STableSpec) Exists() bool { tables := GetTables() in, _ := utils.InStringArray(ts.name, tables) return in } +// SyncSQL returns SQL statements that make table in database consistent with TableSpec definitions +// by comparing table definition derived from TableSpec and that in database func (ts *STableSpec) SyncSQL() []string { if !ts.Exists() { log.Debugf("table %s not created yet", ts.name) @@ -408,6 +411,8 @@ func (ts *STableSpec) SyncSQL() []string { return ret } +// Sync executes the SQLs to synchronize the DB definion of s SQL database +// by applying the SQL statements generated by SyncSQL() func (ts *STableSpec) Sync() error { sqls := ts.SyncSQL() if sqls != nil { @@ -422,6 +427,7 @@ func (ts *STableSpec) Sync() error { return nil } +// CheckSync checks whether the table in database consistent with TableSpec func (ts *STableSpec) CheckSync() error { sqls := ts.SyncSQL() if len(sqls) > 0 { diff --git a/vendor/yunion.io/x/sqlchemy/table.go b/vendor/yunion.io/x/sqlchemy/table.go index 9eb6bca34c..0c0884dc05 100644 --- a/vendor/yunion.io/x/sqlchemy/table.go +++ b/vendor/yunion.io/x/sqlchemy/table.go @@ -23,48 +23,80 @@ import ( "yunion.io/x/pkg/utils" ) +// ITableSpec is the interface represents a table type ITableSpec interface { + // Insert performs an insert operation that insert one record at a time Insert(dt interface{}) error + + // InsertOrUpdate performs an atomic insert or update operation that insert a new record to update the record with current value InsertOrUpdate(dt interface{}) error + + // Update performs an update operation Update(dt interface{}, onUpdate func() error) (UpdateDiffs, error) + + // Increment performs a special update that do an atomic incremental update of the numeric fields Increment(diff, target interface{}) error + + // Decrement performs a special update that do an atomic decremental update of the numeric fields Decrement(diff, target interface{}) error + // DataType returns the data type corresponding to the table DataType() reflect.Type + + // ColumnSpec returns the column definition of a spcific column ColumnSpec(name string) IColumnSpec + + // Name returns the name of the table Name() string + + // Columns returns the array of columns definitions Columns() []IColumnSpec + + // PrimaryColumns returns the array of columns of primary keys PrimaryColumns() []IColumnSpec + + // Expression returns expression of the table Expression() string + // Instance returns an instance of STable for this spec Instance() *STable + // DropForeignKeySQL returns the SQL statements to drop foreignkeys for this table DropForeignKeySQL() []string + + // AddIndex adds index to table AddIndex(unique bool, cols ...string) bool + // SyncSQL forces synchronize the data definition and model definition of the table SyncSQL() []string + + // Fetch query a struct Fetch(dt interface{}) error } +// STableSpec defines the table specification, which implements ITableSpec type STableSpec struct { structType reflect.Type name string columns []IColumnSpec - indexes []STableIndex - contraints []STableConstraint + indexes []sTableIndex + contraints []sTableConstraint } +// STable is an instance of table for query, system will automatically give a alias to this table type STable struct { spec ITableSpec alias string } +// STableField represents a field in a table, implements IQueryField type STableField struct { table *STable spec IColumnSpec alias string } +// NewTableSpecFromStruct generates STableSpec based on the information of a struct model func NewTableSpecFromStruct(s interface{}, name string) *STableSpec { val := reflect.Indirect(reflect.ValueOf(s)) st := val.Type() @@ -80,14 +112,17 @@ func NewTableSpecFromStruct(s interface{}, name string) *STableSpec { return table } +// Name implementation of STableSpec for ITableSpec func (ts *STableSpec) Name() string { return ts.name } +// Expression implementation of STableSpec for ITableSpec func (ts *STableSpec) Expression() string { return fmt.Sprintf("`%s`", ts.name) } +// Clone makes a clone of a table, so we may create a new table of the same schema func (ts *STableSpec) Clone(name string, autoIncOffset int64) *STableSpec { newCols := make([]IColumnSpec, len(ts.columns)) for i := range newCols { @@ -109,10 +144,12 @@ func (ts *STableSpec) Clone(name string, autoIncOffset int64) *STableSpec { } } +// Columns implementation of STableSpec for ITableSpec func (ts *STableSpec) Columns() []IColumnSpec { return ts.columns } +// PrimaryColumns implementation of STableSpec for ITableSpec func (ts *STableSpec) PrimaryColumns() []IColumnSpec { ret := make([]IColumnSpec, 0) for i := range ts.columns { @@ -123,10 +160,12 @@ func (ts *STableSpec) PrimaryColumns() []IColumnSpec { return ret } +// DataType implementation of STableSpec for ITableSpec func (ts *STableSpec) DataType() reflect.Type { return ts.structType } +// CreateSQL returns the SQL for creating this table func (ts *STableSpec) CreateSQL() string { cols := make([]string, 0) primaries := make([]string, 0) @@ -153,15 +192,18 @@ func (ts *STableSpec) CreateSQL() string { return fmt.Sprintf("CREATE TABLE IF NOT EXISTS `%s` (\n%s\n) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci%s", ts.name, strings.Join(cols, ",\n"), autoInc) } +// NewTableInstance return an new table instance from an ITableSpec func NewTableInstance(ts ITableSpec) *STable { table := STable{spec: ts, alias: getTableAliasName()} return &table } +// Instance return an new table instance from an instance of STableSpec func (ts *STableSpec) Instance() *STable { return NewTableInstance(ts) } +// ColumnSpec implementation of STableSpec for ITableSpec func (ts *STableSpec) ColumnSpec(name string) IColumnSpec { for _, c := range ts.Columns() { if c.Name() == name { @@ -171,6 +213,7 @@ func (ts *STableSpec) ColumnSpec(name string) IColumnSpec { return nil } +// Field implementation of STableSpec for IQuerySource func (tbl *STable) Field(name string, alias ...string) IQueryField { // name = reflectutils.StructFieldName(name) name = utils.CamelSplit(name, "_") @@ -186,6 +229,7 @@ func (tbl *STable) Field(name string, alias ...string) IQueryField { return &col } +// Fields implementation of STable for IQuerySource func (tbl *STable) Fields() []IQueryField { ret := make([]IQueryField, 0) for _, c := range tbl.spec.Columns() { @@ -194,26 +238,28 @@ func (tbl *STable) Fields() []IQueryField { return ret } +// Expression implementation of STableField for IQueryField func (c *STableField) Expression() string { if len(c.alias) > 0 { return fmt.Sprintf("`%s`.`%s` as `%s`", c.table.Alias(), c.spec.Name(), c.alias) - } else { - return fmt.Sprintf("`%s`.`%s`", c.table.Alias(), c.spec.Name()) } + return fmt.Sprintf("`%s`.`%s`", c.table.Alias(), c.spec.Name()) } +// Name implementation of STableField for IQueryField func (c *STableField) Name() string { if len(c.alias) > 0 { return c.alias - } else { - return c.spec.Name() } + return c.spec.Name() } +// Reference implementation of STableField for IQueryField func (c *STableField) Reference() string { return fmt.Sprintf("`%s`.`%s`", c.table.Alias(), c.Name()) } +// Label implementation of STableField for IQueryField func (c *STableField) Label(label string) IQueryField { if len(label) > 0 && label != c.spec.Name() { c.alias = label @@ -221,6 +267,7 @@ func (c *STableField) Label(label string) IQueryField { return c } +// Variables implementation of STableField for IQueryField func (c *STableField) Variables() []interface{} { return nil } diff --git a/vendor/yunion.io/x/sqlchemy/tablealias.go b/vendor/yunion.io/x/sqlchemy/tablealias.go index 60763b2fde..96b14f118b 100644 --- a/vendor/yunion.io/x/sqlchemy/tablealias.go +++ b/vendor/yunion.io/x/sqlchemy/tablealias.go @@ -27,6 +27,6 @@ var ( func getTableAliasName() string { tableIDLock.Lock() defer tableIDLock.Unlock() - tableID += 1 + tableID++ return fmt.Sprintf("t%d", tableID) } diff --git a/vendor/yunion.io/x/sqlchemy/union.go b/vendor/yunion.io/x/sqlchemy/union.go index 7af8c7174a..fb23d0c2d5 100644 --- a/vendor/yunion.io/x/sqlchemy/union.go +++ b/vendor/yunion.io/x/sqlchemy/union.go @@ -23,32 +23,35 @@ import ( "yunion.io/x/pkg/errors" ) +// SUnionQueryField represents a field of a union query type SUnionQueryField struct { union *SUnion name string alias string } +// Expression implementation of SUnionQueryField for IQueryField func (sqf *SUnionQueryField) Expression() string { if len(sqf.alias) > 0 { return fmt.Sprintf("`%s`.`%s` as `%s`", sqf.union.Alias(), sqf.name, sqf.alias) - } else { - return fmt.Sprintf("`%s`.`%s`", sqf.union.Alias(), sqf.name) } + return fmt.Sprintf("`%s`.`%s`", sqf.union.Alias(), sqf.name) } +// Name implementation of SUnionQueryField for IQueryField func (sqf *SUnionQueryField) Name() string { if len(sqf.alias) > 0 { return sqf.alias - } else { - return sqf.name } + return sqf.name } +// Reference implementation of SUnionQueryField for IQueryField func (sqf *SUnionQueryField) Reference() string { return fmt.Sprintf("`%s`.`%s`", sqf.union.Alias(), sqf.Name()) } +// Label implementation of SUnionQueryField for IQueryField func (sqf *SUnionQueryField) Label(label string) IQueryField { if len(label) > 0 && label != sqf.name { sqf.alias = label @@ -56,23 +59,27 @@ func (sqf *SUnionQueryField) Label(label string) IQueryField { return sqf } +// Variables implementation of SUnionQueryField for IQueryField func (sqf *SUnionQueryField) Variables() []interface{} { return nil } +// SUnion is the struct to store state of a Union query, which implementation the interface of IQuerySource type SUnion struct { alias string queries []IQuery fields []IQueryField - orderBy []SQueryOrder + orderBy []sQueryOrder limit int offset int } +// Alias implementation of SUnion for IQuerySource func (uq *SUnion) Alias() string { return uq.alias } +// Expression implementation of SUnion for IQuerySource func (uq *SUnion) Expression() string { var buf strings.Builder buf.WriteByte('(') @@ -122,20 +129,24 @@ func (tq *SUnion) Desc(fields ...interface{}) *SUnion { } */ +// Limit adds limit to a union query func (uq *SUnion) Limit(limit int) *SUnion { uq.limit = limit return uq } +// Offset adds offset to a union query func (uq *SUnion) Offset(offset int) *SUnion { uq.offset = offset return uq } +// Fields implementation of SUnion for IQuerySource func (uq *SUnion) Fields() []IQueryField { return uq.fields } +// Field implementation of SUnion for IQuerySource func (uq *SUnion) Field(name string, alias ...string) IQueryField { for i := range uq.fields { if name == uq.fields[i].Name() { @@ -148,6 +159,7 @@ func (uq *SUnion) Field(name string, alias ...string) IQueryField { return nil } +// Variables implementation of SUnion for IQuerySource func (uq *SUnion) Variables() []interface{} { ret := make([]interface{}, 0) for i := range uq.queries { @@ -156,6 +168,8 @@ func (uq *SUnion) Variables() []interface{} { return ret } +// Union method returns union query of several queries. +// Require the fields of all queries should exactly match // deprecated func Union(query ...IQuery) *SUnion { u, err := UnionWithError(query...) @@ -165,6 +179,8 @@ func Union(query ...IQuery) *SUnion { return u } +// UnionWithError constructs union query of several Queries +// Require the fields of all queries should exactly match func UnionWithError(query ...IQuery) (*SUnion, error) { if len(query) == 0 { return nil, errors.Wrap(sql.ErrNoRows, "empty union query") @@ -175,14 +191,14 @@ func UnionWithError(query ...IQuery) (*SUnion, error) { fieldNames = append(fieldNames, f.Name()) } - for i := 1; i < len(query); i += 1 { + for i := 1; i < len(query); i++ { qfields := query[i].QueryFields() if len(fieldNames) != len(qfields) { - return nil, fmt.Errorf("cannot union, number of fields not match!") + return nil, errors.Wrap(ErrUnionFieldsNotMatch, "number not match") } for i := range qfields { if fieldNames[i] != qfields[i].Name() { - return nil, fmt.Errorf("cannot union, name of fields not match!") + return nil, errors.Wrapf(ErrUnionFieldsNotMatch, "name %s:%s not match", fieldNames[i], qfields[i].Name()) } } } @@ -202,6 +218,7 @@ func UnionWithError(query ...IQuery) (*SUnion, error) { return uq, nil } +// Query of SUnion returns a SQuery of a union query func (uq *SUnion) Query(f ...IQueryField) *SQuery { return DoQuery(uq, f...) } diff --git a/vendor/yunion.io/x/sqlchemy/update.go b/vendor/yunion.io/x/sqlchemy/update.go index cd3f9225d0..9f8b38feb3 100644 --- a/vendor/yunion.io/x/sqlchemy/update.go +++ b/vendor/yunion.io/x/sqlchemy/update.go @@ -28,6 +28,7 @@ import ( "yunion.io/x/pkg/utils" ) +// SUpdateSession is a struct to store the state of a update session type SUpdateSession struct { oValue reflect.Value tableSpec *STableSpec @@ -62,12 +63,14 @@ func (ts *STableSpec) prepareUpdate(dt interface{}) (*SUpdateSession, error) { return &us, nil } +// SUpdateDiff is a struct to store the differences for an update of a column type SUpdateDiff struct { old interface{} new interface{} col IColumnSpec } +// String of SUpdateDiff returns the string representation of a SUpdateDiff func (ud *SUpdateDiff) String() string { return fmt.Sprintf("%s->%s", utils.TruncateString(ud.old, 32), @@ -81,8 +84,10 @@ func (ud SUpdateDiff) jsonObj() jsonutils.JSONObject { return r } +// UpdateDiffs is a map of SUpdateDiff whose key is the column name type UpdateDiffs map[string]SUpdateDiff +// String of UpdateDiffs returns the string representation of UpdateDiffs func (uds UpdateDiffs) String() string { obj := jsonutils.NewDict() for k := range uds { @@ -206,6 +211,9 @@ func (us *SUpdateSession) saveUpdate(dt interface{}) (UpdateDiffs, error) { return setters, nil } +// Update method of STableSpec updates a record of a table, +// dt is the point to the struct storing the record +// doUpdate provides method to update the field of the record func (ts *STableSpec) Update(dt interface{}, doUpdate func() error) (UpdateDiffs, error) { session, err := ts.prepareUpdate(dt) if err != nil {