mirror of
https://github.com/adjust/pg-base36.git
synced 2025-12-16 15:54:39 +00:00
initial commit
This commit is contained in:
4
Gemfile
Normal file
4
Gemfile
Normal file
@@ -0,0 +1,4 @@
|
||||
# Gemfile
|
||||
source 'https://rubygems.org'
|
||||
gem 'dumbo', :path => "/Users/rapimo/adjust/dumbo"
|
||||
gem 'pry'
|
||||
69
Gemfile.lock
Normal file
69
Gemfile.lock
Normal file
@@ -0,0 +1,69 @@
|
||||
PATH
|
||||
remote: /Users/rapimo/adjust/dumbo
|
||||
specs:
|
||||
dumbo (0.0.4)
|
||||
activerecord
|
||||
activesupport
|
||||
bundler (~> 1.5)
|
||||
erubis
|
||||
pg (> 0.17)
|
||||
pry
|
||||
rake
|
||||
rspec (~> 3.0.0)
|
||||
thor
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activemodel (4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.3)
|
||||
activemodel (= 4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.3)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
arel (6.0.0)
|
||||
builder (3.2.2)
|
||||
coderay (1.1.0)
|
||||
diff-lcs (1.2.5)
|
||||
erubis (2.7.0)
|
||||
i18n (0.7.0)
|
||||
json (1.8.3)
|
||||
method_source (0.8.2)
|
||||
minitest (5.7.0)
|
||||
pg (0.18.2)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
rake (10.4.2)
|
||||
rspec (3.0.0)
|
||||
rspec-core (~> 3.0.0)
|
||||
rspec-expectations (~> 3.0.0)
|
||||
rspec-mocks (~> 3.0.0)
|
||||
rspec-core (3.0.4)
|
||||
rspec-support (~> 3.0.0)
|
||||
rspec-expectations (3.0.4)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.0.0)
|
||||
rspec-mocks (3.0.4)
|
||||
rspec-support (~> 3.0.0)
|
||||
rspec-support (3.0.4)
|
||||
slop (3.6.0)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
dumbo!
|
||||
pry
|
||||
12
Makefile
Normal file
12
Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
#http://blog.pgxn.org/post/4783001135/extension-makefiles pg makefiles
|
||||
EXTENSION = base36
|
||||
PG_CONFIG ?= pg_config
|
||||
DATA = $(wildcard *--*.sql)
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
MODULE_big = base36
|
||||
OBJS = $(patsubst %.c,%.o,$(wildcard src/*.c))
|
||||
TESTS = $(wildcard test/sql/*.sql)
|
||||
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
||||
REGRESS_OPTS = --inputdir=test --load-language=plpgsql
|
||||
include $(PGXS)
|
||||
|
||||
15
Rakefile
Normal file
15
Rakefile
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'rake'
|
||||
require 'rspec/core/rake_task'
|
||||
require 'dumbo/rake_task'
|
||||
require 'dumbo/db_task'
|
||||
require 'dumbo'
|
||||
|
||||
load File.expand_path('../config/boot.rb', __FILE__)
|
||||
|
||||
Dumbo::RakeTask.new(:dumbo)
|
||||
Dumbo::DbTask.new(:db)
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
|
||||
Dir.glob('lib/tasks/**/*.rake').each { |taskfile| load taskfile }
|
||||
|
||||
task default: ['dumbo:all', 'db:test:prepare', :spec]
|
||||
152
base36--0.0.1.sql
Normal file
152
base36--0.0.1.sql
Normal file
@@ -0,0 +1,152 @@
|
||||
--source file sql/base36.sql
|
||||
CREATE FUNCTION base36_in(cstring)
|
||||
RETURNS base36
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION base36_out(base36)
|
||||
RETURNS cstring
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION base36_recv(internal)
|
||||
RETURNS base36
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION base36_send(base36)
|
||||
RETURNS bytea
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE TYPE base36 (
|
||||
INPUT = base36_in,
|
||||
OUTPUT = base36_out,
|
||||
RECEIVE = base36_recv,
|
||||
SEND = base36_send,
|
||||
LIKE = bigint,
|
||||
CATEGORY = 'N'
|
||||
);
|
||||
COMMENT ON TYPE base36 IS 'bigint written in base36: [0-9A-Z]+';
|
||||
|
||||
CREATE FUNCTION base36(text)
|
||||
RETURNS base36
|
||||
AS '$libdir/base36', 'base36_cast_from_text'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION text(base36)
|
||||
RETURNS text
|
||||
AS '$libdir/base36', 'base36_cast_to_text'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE CAST (text as base36) WITH FUNCTION base36(text) AS IMPLICIT;
|
||||
CREATE CAST (base36 as text) WITH FUNCTION text(base36);
|
||||
|
||||
CREATE CAST (bigint as base36) WITHOUT FUNCTION AS IMPLICIT;
|
||||
CREATE CAST (base36 as bigint) WITHOUT FUNCTION AS IMPLICIT;
|
||||
|
||||
CREATE FUNCTION base36_eq(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8eq';
|
||||
|
||||
CREATE FUNCTION base36_ne(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8ne';
|
||||
|
||||
CREATE FUNCTION base36_lt(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8lt';
|
||||
|
||||
CREATE FUNCTION base36_le(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8le';
|
||||
|
||||
CREATE FUNCTION base36_gt(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8gt';
|
||||
|
||||
CREATE FUNCTION base36_ge(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8ge';
|
||||
|
||||
CREATE FUNCTION base36_cmp(base36, base36)
|
||||
RETURNS integer LANGUAGE internal IMMUTABLE AS 'btint8cmp';
|
||||
|
||||
CREATE FUNCTION hash_base36(base36)
|
||||
RETURNS integer LANGUAGE internal IMMUTABLE AS 'hashint8';
|
||||
|
||||
CREATE OPERATOR = (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_eq,
|
||||
COMMUTATOR = '=',
|
||||
NEGATOR = '<>',
|
||||
RESTRICT = eqsel,
|
||||
JOIN = eqjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR =(base36, base36) IS 'equals?';
|
||||
|
||||
CREATE OPERATOR <> (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_ne,
|
||||
COMMUTATOR = '<>',
|
||||
NEGATOR = '=',
|
||||
RESTRICT = neqsel,
|
||||
JOIN = neqjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR <>(base36, base36) IS 'not equals?';
|
||||
|
||||
CREATE OPERATOR < (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_lt,
|
||||
COMMUTATOR = > ,
|
||||
NEGATOR = >= ,
|
||||
RESTRICT = scalarltsel,
|
||||
JOIN = scalarltjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR <(base36, base36) IS 'less-than';
|
||||
|
||||
CREATE OPERATOR <= (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_le,
|
||||
COMMUTATOR = >= ,
|
||||
NEGATOR = > ,
|
||||
RESTRICT = scalarltsel,
|
||||
JOIN = scalarltjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR <=(base36, base36) IS 'less-than-or-equal';
|
||||
|
||||
CREATE OPERATOR > (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_gt,
|
||||
COMMUTATOR = < ,
|
||||
NEGATOR = <= ,
|
||||
RESTRICT = scalargtsel,
|
||||
JOIN = scalargtjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR >(base36, base36) IS 'greater-than';
|
||||
|
||||
CREATE OPERATOR >= (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_ge,
|
||||
COMMUTATOR = <= ,
|
||||
NEGATOR = < ,
|
||||
RESTRICT = scalargtsel,
|
||||
JOIN = scalargtjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR >=(base36, base36) IS 'greater-than-or-equal';
|
||||
|
||||
CREATE OPERATOR CLASS btree_base36_ops
|
||||
DEFAULT FOR TYPE base36 USING btree
|
||||
AS
|
||||
OPERATOR 1 < ,
|
||||
OPERATOR 2 <= ,
|
||||
OPERATOR 3 = ,
|
||||
OPERATOR 4 >= ,
|
||||
OPERATOR 5 > ,
|
||||
FUNCTION 1 base36_cmp(base36, base36);
|
||||
|
||||
CREATE OPERATOR CLASS hash_base36_ops
|
||||
DEFAULT FOR TYPE base36 USING hash AS
|
||||
OPERATOR 1 = ,
|
||||
FUNCTION 1 hash_base36(base36);
|
||||
|
||||
5
base36.control
Normal file
5
base36.control
Normal file
@@ -0,0 +1,5 @@
|
||||
# base36 extension
|
||||
comment = 'my awesome extension'
|
||||
default_version = '0.0.1'
|
||||
relocatable = true
|
||||
requires = ''
|
||||
14
config/boot.rb
Normal file
14
config/boot.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
$LOAD_PATH.unshift File.expand_path('../..', __FILE__)
|
||||
|
||||
# Set up gems listed in the Gemfile.
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
||||
ENV['DUMBO_ENV'] ||= 'development'
|
||||
|
||||
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
||||
Bundler.require(:default, ENV['DUMBO_ENV'].to_sym)
|
||||
|
||||
def db_config
|
||||
@config ||= YAML.load_file('config/database.yml')
|
||||
end
|
||||
|
||||
ActiveRecord::Base.establish_connection db_config[ENV['DUMBO_ENV']]
|
||||
31
config/database.yml
Normal file
31
config/database.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
# general postgres settings
|
||||
# Connect on a TCP socket. Omitted by default since the client uses a
|
||||
# domain socket that doesn't need configuration. Windows does not have
|
||||
# domain sockets, so uncomment these lines.
|
||||
# host: localhost
|
||||
# port: 5432
|
||||
|
||||
# Schema search path. The server defaults to $user,public
|
||||
# schema_search_path: myapp,sharedapp,public
|
||||
|
||||
# Minimum log levels, in increasing order:
|
||||
# debug5, debug4, debug3, debug2, debug1,
|
||||
# log, notice, warning, error, fatal, and panic
|
||||
# The server defaults to notice.
|
||||
# min_messages: warning
|
||||
|
||||
postgres: &postgres
|
||||
adapter: postgresql
|
||||
encoding: utf8
|
||||
pool: 5
|
||||
username: postgres
|
||||
password:
|
||||
host: localhost
|
||||
|
||||
development:
|
||||
<<: *postgres
|
||||
database: base36_development
|
||||
|
||||
test:
|
||||
<<: *postgres
|
||||
database: base36_test
|
||||
47
spec/base36_spec.rb
Normal file
47
spec/base36_spec.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'base36' do
|
||||
before do
|
||||
install_extension
|
||||
end
|
||||
|
||||
describe 'conversion' do
|
||||
it 'should encode integer' do
|
||||
query('SELECT 120::bigint::base36').should match '3c'
|
||||
end
|
||||
|
||||
it 'should decode text' do
|
||||
query("SELECT '3c'::base36::bigint").should match '120'
|
||||
end
|
||||
|
||||
it 'should error to big values' do
|
||||
expect{query("SELECT '3caaaaaaaaaaaaa'::base36::bigint")}.to throw_error 'value "3caaaaaaaaaaaaa" is out of range for type base36'
|
||||
end
|
||||
|
||||
it 'should cast big base36 values' do
|
||||
query("SELECT '1y2p0ij32e8e7'::base36::bigint").should match 9223372036854775807
|
||||
end
|
||||
|
||||
it 'should cast big int values' do
|
||||
query("SELECT 9223372036854775807::base36").should match '1y2p0ij32e8e7'
|
||||
end
|
||||
|
||||
it 'should error to big values' do
|
||||
expect{query("SELECT '1y2p0ij32e8e8'::base36::bigint")}.to throw_error 'value "1y2p0ij32e8e8" is out of range for type base36'
|
||||
end
|
||||
|
||||
it 'should error invalid values' do
|
||||
expect{query("SELECT '3&'::base36::bigint")}.to throw_error 'value "&" is not a valid digit for type base36'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'comparison' do
|
||||
it 'should compare using >' do
|
||||
query("SELECT 'a'::base36 > 'b'::base36").should match 'f'
|
||||
end
|
||||
|
||||
it 'should compare using <' do
|
||||
query("SELECT 'a'::base36 < 'b'::base36").should match 't'
|
||||
end
|
||||
end
|
||||
end
|
||||
34
spec/spec_helper.rb
Normal file
34
spec/spec_helper.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
require 'rubygems'
|
||||
require 'dumbo/test'
|
||||
|
||||
ENV['DUMBO_ENV'] ||= 'test'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
|
||||
require 'dumbo/test/silence_unknown_oid'
|
||||
|
||||
ActiveRecord::Base.logger.level = 0 if ActiveRecord::Base.logger
|
||||
|
||||
Dir.glob('spec/support/**/*.rb').each { |f| require f }
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.fail_fast = false
|
||||
config.order = 'random'
|
||||
config.filter_run focus: true
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.expect_with :rspec do |c|
|
||||
c.syntax = [:should, :expect]
|
||||
end
|
||||
|
||||
# wrap test in transactions
|
||||
config.around(:each) do |example|
|
||||
ActiveRecord::Base.transaction do
|
||||
example.run
|
||||
fail ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
|
||||
config.include(Dumbo::Test::Helper)
|
||||
config.include(Dumbo::Matchers)
|
||||
end
|
||||
|
||||
require 'dumbo/test/regression_helper' if ENV['DUMBO_REGRESSION']
|
||||
31
spec/support/extension_helper.rb
Normal file
31
spec/support/extension_helper.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
module ExtensionHelper
|
||||
def install_testing_extension
|
||||
system <<-CMD
|
||||
(
|
||||
mkdir -p #{spec_root}/dumbo_sample_runtime && \
|
||||
cp -R #{spec_root}/dumbo_sample/ #{spec_root}/dumbo_sample_runtime
|
||||
cd #{spec_root}/dumbo_sample_runtime && \
|
||||
make clean && \
|
||||
make && \
|
||||
make install
|
||||
) 1> /dev/null
|
||||
CMD
|
||||
end
|
||||
|
||||
def uninstall_testing_extension
|
||||
system <<-CMD
|
||||
(
|
||||
cd #{spec_root}/dumbo_sample_runtime && \
|
||||
make clean && \
|
||||
make uninstall && \
|
||||
rm -rf #{spec_root}/dumbo_sample_runtime
|
||||
) 1> /dev/null
|
||||
CMD
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def spec_root
|
||||
File.join(File.dirname(__FILE__), '..')
|
||||
end
|
||||
end
|
||||
150
sql/base36.sql
Normal file
150
sql/base36.sql
Normal file
@@ -0,0 +1,150 @@
|
||||
CREATE FUNCTION base36_in(cstring)
|
||||
RETURNS base36
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION base36_out(base36)
|
||||
RETURNS cstring
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION base36_recv(internal)
|
||||
RETURNS base36
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION base36_send(base36)
|
||||
RETURNS bytea
|
||||
AS '$libdir/base36'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE TYPE base36 (
|
||||
INPUT = base36_in,
|
||||
OUTPUT = base36_out,
|
||||
RECEIVE = base36_recv,
|
||||
SEND = base36_send,
|
||||
LIKE = bigint,
|
||||
CATEGORY = 'N'
|
||||
);
|
||||
COMMENT ON TYPE base36 IS 'bigint written in base36: [0-9A-Z]+';
|
||||
|
||||
CREATE FUNCTION base36(text)
|
||||
RETURNS base36
|
||||
AS '$libdir/base36', 'base36_cast_from_text'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE FUNCTION text(base36)
|
||||
RETURNS text
|
||||
AS '$libdir/base36', 'base36_cast_to_text'
|
||||
LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
CREATE CAST (text as base36) WITH FUNCTION base36(text) AS IMPLICIT;
|
||||
CREATE CAST (base36 as text) WITH FUNCTION text(base36);
|
||||
|
||||
CREATE CAST (bigint as base36) WITHOUT FUNCTION AS IMPLICIT;
|
||||
CREATE CAST (base36 as bigint) WITHOUT FUNCTION AS IMPLICIT;
|
||||
|
||||
CREATE FUNCTION base36_eq(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8eq';
|
||||
|
||||
CREATE FUNCTION base36_ne(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8ne';
|
||||
|
||||
CREATE FUNCTION base36_lt(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8lt';
|
||||
|
||||
CREATE FUNCTION base36_le(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8le';
|
||||
|
||||
CREATE FUNCTION base36_gt(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8gt';
|
||||
|
||||
CREATE FUNCTION base36_ge(base36, base36)
|
||||
RETURNS boolean LANGUAGE internal IMMUTABLE AS 'int8ge';
|
||||
|
||||
CREATE FUNCTION base36_cmp(base36, base36)
|
||||
RETURNS integer LANGUAGE internal IMMUTABLE AS 'btint8cmp';
|
||||
|
||||
CREATE FUNCTION hash_base36(base36)
|
||||
RETURNS integer LANGUAGE internal IMMUTABLE AS 'hashint8';
|
||||
|
||||
CREATE OPERATOR = (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_eq,
|
||||
COMMUTATOR = '=',
|
||||
NEGATOR = '<>',
|
||||
RESTRICT = eqsel,
|
||||
JOIN = eqjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR =(base36, base36) IS 'equals?';
|
||||
|
||||
CREATE OPERATOR <> (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_ne,
|
||||
COMMUTATOR = '<>',
|
||||
NEGATOR = '=',
|
||||
RESTRICT = neqsel,
|
||||
JOIN = neqjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR <>(base36, base36) IS 'not equals?';
|
||||
|
||||
CREATE OPERATOR < (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_lt,
|
||||
COMMUTATOR = > ,
|
||||
NEGATOR = >= ,
|
||||
RESTRICT = scalarltsel,
|
||||
JOIN = scalarltjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR <(base36, base36) IS 'less-than';
|
||||
|
||||
CREATE OPERATOR <= (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_le,
|
||||
COMMUTATOR = >= ,
|
||||
NEGATOR = > ,
|
||||
RESTRICT = scalarltsel,
|
||||
JOIN = scalarltjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR <=(base36, base36) IS 'less-than-or-equal';
|
||||
|
||||
CREATE OPERATOR > (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_gt,
|
||||
COMMUTATOR = < ,
|
||||
NEGATOR = <= ,
|
||||
RESTRICT = scalargtsel,
|
||||
JOIN = scalargtjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR >(base36, base36) IS 'greater-than';
|
||||
|
||||
CREATE OPERATOR >= (
|
||||
LEFTARG = base36,
|
||||
RIGHTARG = base36,
|
||||
PROCEDURE = base36_ge,
|
||||
COMMUTATOR = <= ,
|
||||
NEGATOR = < ,
|
||||
RESTRICT = scalargtsel,
|
||||
JOIN = scalargtjoinsel
|
||||
);
|
||||
COMMENT ON OPERATOR >=(base36, base36) IS 'greater-than-or-equal';
|
||||
|
||||
CREATE OPERATOR CLASS btree_base36_ops
|
||||
DEFAULT FOR TYPE base36 USING btree
|
||||
AS
|
||||
OPERATOR 1 < ,
|
||||
OPERATOR 2 <= ,
|
||||
OPERATOR 3 = ,
|
||||
OPERATOR 4 >= ,
|
||||
OPERATOR 5 > ,
|
||||
FUNCTION 1 base36_cmp(base36, base36);
|
||||
|
||||
CREATE OPERATOR CLASS hash_base36_ops
|
||||
DEFAULT FOR TYPE base36 USING hash AS
|
||||
OPERATOR 1 = ,
|
||||
FUNCTION 1 hash_base36(base36);
|
||||
151
src/base36.c
Normal file
151
src/base36.c
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "base36.h"
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
#define BASE36_LENGTH 13
|
||||
|
||||
static int base36_digits[36] =
|
||||
{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z'
|
||||
};
|
||||
|
||||
static base36 base36_powers[BASE36_LENGTH] =
|
||||
{
|
||||
1ULL,
|
||||
36ULL,
|
||||
1296ULL,
|
||||
46656ULL,
|
||||
1679616ULL,
|
||||
60466176ULL,
|
||||
2176782336ULL,
|
||||
78364164096ULL,
|
||||
2821109907456ULL,
|
||||
101559956668416ULL,
|
||||
3656158440062976ULL,
|
||||
131621703842267136ULL,
|
||||
4738381338321616896ULL
|
||||
};
|
||||
|
||||
static inline
|
||||
base36 base36_from_str(const char *str)
|
||||
{
|
||||
int i, d = 0, n = strlen(str);
|
||||
base36 c = 0;
|
||||
|
||||
if( n == 0 || n > BASE36_LENGTH )
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value \"%s\" is out of range for type base36",
|
||||
str)));
|
||||
}
|
||||
|
||||
for(i=0; i<n; i++) {
|
||||
if( str[i] >= '0' && str[i] <= '9' )
|
||||
d = str[i] - '0';
|
||||
else if ( str[i] >= 'A' && str[i] <= 'Z' )
|
||||
d = 10 + str[i] - 'A';
|
||||
else if ( str[i] >= 'a' && str[i] <= 'z' )
|
||||
d = 10 + str[i] - 'a';
|
||||
else
|
||||
ereport(ERROR, (
|
||||
errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("value \"%c\" is not a valid digit for type base36", str[i])
|
||||
)
|
||||
);
|
||||
|
||||
c += d * base36_powers[n-i-1];
|
||||
|
||||
if ( c < 0 )
|
||||
{
|
||||
ereport(ERROR, (
|
||||
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value \"%s\" is out of range for type base36", str)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline
|
||||
char *base36_to_str(base36 c)
|
||||
{
|
||||
int i, d, p = 0;
|
||||
base36 m = c;
|
||||
bool discard = true;
|
||||
char *str = palloc0((BASE36_LENGTH + 1) * sizeof(char));
|
||||
|
||||
for(i=BASE36_LENGTH-1; i>=0; i--)
|
||||
{
|
||||
d = m / base36_powers[i];
|
||||
m = m - base36_powers[i] * d;
|
||||
|
||||
discard = discard && (d == 0 && i >0);
|
||||
|
||||
if( !discard )
|
||||
str[p++] = base36_digits[d];
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(base36_in);
|
||||
Datum
|
||||
base36_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *str = PG_GETARG_CSTRING(0);
|
||||
PG_RETURN_INT64(base36_from_str(str));
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(base36_out);
|
||||
Datum
|
||||
base36_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
base36 c = PG_GETARG_INT64(0);
|
||||
PG_RETURN_CSTRING(base36_to_str(c));
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(base36_recv);
|
||||
Datum
|
||||
base36_recv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||||
const char *str = pq_getmsgstring(buf);
|
||||
pq_getmsgend(buf);
|
||||
PG_RETURN_INT64(base36_from_str(str));
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(base36_send);
|
||||
Datum
|
||||
base36_send(PG_FUNCTION_ARGS)
|
||||
{
|
||||
base36 c = PG_GETARG_INT64(0);
|
||||
StringInfoData buf;
|
||||
|
||||
pq_begintypsend(&buf);
|
||||
pq_sendstring(&buf, base36_to_str(c));
|
||||
|
||||
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(base36_cast_from_text);
|
||||
Datum
|
||||
base36_cast_from_text(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *txt = PG_GETARG_TEXT_P(0);
|
||||
char *str = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(txt)));
|
||||
PG_RETURN_INT64(base36_from_str(str));
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(base36_cast_to_text);
|
||||
Datum
|
||||
base36_cast_to_text(PG_FUNCTION_ARGS)
|
||||
{
|
||||
base36 c = PG_GETARG_INT64(0);
|
||||
text *out = (text *)DirectFunctionCall1(textin, PointerGetDatum(base36_to_str(c)));
|
||||
PG_RETURN_TEXT_P(out);
|
||||
}
|
||||
19
src/base36.h
Normal file
19
src/base36.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef BASE36_H
|
||||
#define BASE36_H
|
||||
|
||||
#include "postgres.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "libpq/pqformat.h"
|
||||
|
||||
typedef long long int base36;
|
||||
|
||||
Datum base36_in(PG_FUNCTION_ARGS);
|
||||
Datum base36_out(PG_FUNCTION_ARGS);
|
||||
Datum base36_recv(PG_FUNCTION_ARGS);
|
||||
Datum base36_send(PG_FUNCTION_ARGS);
|
||||
Datum base36_cast_to_text(PG_FUNCTION_ARGS);
|
||||
Datum base36_cast_from_text(PG_FUNCTION_ARGS);
|
||||
Datum base36_cast_to_bigint(PG_FUNCTION_ARGS);
|
||||
Datum base36_cast_from_bigint(PG_FUNCTION_ARGS);
|
||||
|
||||
#endif // BASE36_H
|
||||
Reference in New Issue
Block a user