Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for UUID keys comparison #86

Merged
merged 14 commits into from
Dec 7, 2020
Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

* Fixed typo in error for case when failed to get `bucket_id`

### Added

* Support for UUID field types and UUID values

## [0.3.0] - 2020-10-26

### Fixed
Expand Down
109 changes: 109 additions & 0 deletions crud/common/types.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
local errors = require('errors')

local collations = require('crud.common.collations')
local TypeMismatchError = errors.new_class('TypeMismatchError')
local UnsupportedCollationError = errors.new_class('UnsupportedCollationError')

local types = {}

local function comparing_nil(cmp)
akudiyar marked this conversation as resolved.
Show resolved Hide resolved
return function(lhs, rhs)
if lhs == nil and rhs ~= nil then
return true
elseif rhs == nil then
return false
end

return cmp(lhs, rhs)
end
end

local function lt(lhs, rhs)
return lhs < rhs
end

local function lt_default(lhs, rhs)
return comparing_nil(lt)(lhs, rhs)
end

local function eq(lhs, rhs)
return lhs == rhs
end

local function lt_boolean(lhs, rhs)
local lhs_is_boolean = type(lhs) == 'boolean'
local rhs_is_boolean = type(rhs) == 'boolean'

if lhs_is_boolean and rhs_is_boolean then
return (not lhs) and rhs
elseif lhs_is_boolean or rhs_is_boolean then
TypeMismatchError:assert(false, 'Could not compare boolean and not boolean')
end
end

local function lt_unicode(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.cmp(lhs, rhs) == -1
end

return lt_default(lhs, rhs)
end

local function lt_unicode_ci(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.casecmp(lhs, rhs) == -1
end

return lt_default(lhs, rhs)
end

local function eq_unicode(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.cmp(lhs, rhs) == 0
end

return lhs == rhs
end

local function eq_unicode_ci(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.casecmp(lhs, rhs) == 0
end

return lhs == rhs
end

local function lt_uuid(lhs, rhs)
return lhs:str() < rhs:str()
end

local functions_by_key_type = {
boolean = function ()
return comparing_nil(lt_boolean), eq
end,
string = function (key_part)
local collation = collations.get(key_part)
if collations.is_default(collation) then
return lt_default, eq
elseif collation == collations.UNICODE then
return lt_unicode, eq_unicode
elseif collation == collations.UNICODE_CI then
return lt_unicode_ci, eq_unicode_ci
else
UnsupportedCollationError:assert(false, 'Unsupported Tarantool collation %q', collation)
end
end,
uuid = function ()
return comparing_nil(lt_uuid), eq
end
}

function types.comparators(key_part)
akudiyar marked this conversation as resolved.
Show resolved Hide resolved
if key_part and key_part.type and functions_by_key_type[key_part.type] then
return functions_by_key_type[key_part.type](key_part)
akudiyar marked this conversation as resolved.
Show resolved Hide resolved
else
return lt_default, eq
akudiyar marked this conversation as resolved.
Show resolved Hide resolved
end
end

return types
akudiyar marked this conversation as resolved.
Show resolved Hide resolved
28 changes: 21 additions & 7 deletions crud/common/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,9 @@ function utils.merge_primary_key_parts(key_parts, pk_parts)
return merged_parts
end

local __tarantool_supports_fieldpaths
local function tarantool_supports_fieldpaths()
if __tarantool_supports_fieldpaths ~= nil then
return __tarantool_supports_fieldpaths
end
local features = {}

local function determine_enabled_features()
local major_minor_patch = _G._TARANTOOL:split('-', 1)[1]
local major_minor_patch_parts = major_minor_patch:split('.', 2)

Expand All @@ -120,9 +117,26 @@ local function tarantool_supports_fieldpaths()
local patch = tonumber(major_minor_patch_parts[3])

-- since Tarantool 2.3
__tarantool_supports_fieldpaths = major >= 2 and (minor > 3 or minor == 3 and patch >= 1)
features.tarantool_supports_fieldpaths = major >= 2 and (minor > 3 or minor == 3 and patch >= 1)

-- since Tarantool 2.4
features.tarantool_supports_uuids = major >= 2 and (minor > 4 or minor == 4 and patch >= 1)
end

local function tarantool_supports_fieldpaths()
if features.tarantool_supports_fieldpaths == nil then
determine_enabled_features()
end

return features.tarantool_supports_fieldpaths
end

function utils.tarantool_supports_uuids()
if features.tarantool_supports_uuids == nil then
determine_enabled_features()
end

return __tarantool_supports_fieldpaths
return features.tarantool_supports_uuids
akudiyar marked this conversation as resolved.
Show resolved Hide resolved
end

function utils.convert_operations(user_operations, space_format)
Expand Down
77 changes: 4 additions & 73 deletions crud/select/comparators.lua
Original file line number Diff line number Diff line change
@@ -1,74 +1,15 @@
local errors = require('errors')

local collations = require('crud.common.collations')
local select_conditions = require('crud.select.conditions')
local operators = select_conditions.operators

local utils = require('crud.common.utils')
local types = require('crud.common.types')

local LessThenError = errors.new_class('LessThenError')
local GenFuncError = errors.new_class('GenFuncError')
local ComparatorsError = errors.new_class('ComparatorsError')

local comparators = {}

local function eq(lhs, rhs)
return lhs == rhs
end

local function eq_unicode(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.cmp(lhs, rhs) == 0
end

return eq(lhs)
end

local function eq_unicode_ci(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.casecmp(lhs, rhs) == 0
end

return lhs == rhs
end

local function lt(lhs, rhs)
if lhs == nil and rhs ~= nil then
return true
elseif rhs == nil then
return false
end

-- boolean compare
local lhs_is_boolean = type(lhs) == 'boolean'
local rhs_is_boolean = type(rhs) == 'boolean'

if lhs_is_boolean and rhs_is_boolean then
return (not lhs) and rhs
elseif lhs_is_boolean or rhs_is_boolean then
LessThenError:assert(false, 'Could not compare boolean and not boolean')
end

-- general compare
return lhs < rhs
end

local function lt_unicode(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.cmp(lhs, rhs) == -1
end

return lt(lhs, rhs)
end

local function lt_unicode_ci(lhs, rhs)
if type(lhs) == 'string' and type(rhs) == 'string' then
return utf8.casecmp(lhs, rhs) == -1
end

return lt(lhs, rhs)
end

local function array_eq(lhs, rhs, len, _, eq_funcs)
for i = 1, len do
if not eq_funcs[i](lhs[i], rhs[i]) then
Expand Down Expand Up @@ -132,19 +73,9 @@ local function gen_array_cmp_func(target, key_parts)
local eq_funcs = {}

for _, part in ipairs(key_parts) do
local collation = collations.get(part)
if collations.is_default(collation) then
table.insert(lt_funcs, lt)
table.insert(eq_funcs, eq)
elseif collation == collations.UNICODE then
table.insert(lt_funcs, lt_unicode)
table.insert(eq_funcs, eq_unicode)
elseif collation == collations.UNICODE_CI then
table.insert(lt_funcs, lt_unicode_ci)
table.insert(eq_funcs, eq_unicode_ci)
else
return nil, GenFuncError:new('Unsupported Tarantool collation %q', collation)
end
local lt_func, eq_func = types.comparators(part)
table.insert(lt_funcs, lt_func)
table.insert(eq_funcs, eq_func)
end

return function(lhs, rhs)
Expand Down
Loading