diff --git a/.gitlab-ci.jsonnet b/.gitlab-ci.jsonnet deleted file mode 100644 index 215964ff16e17ab414915529ba8d26bf6a6e6983..0000000000000000000000000000000000000000 --- a/.gitlab-ci.jsonnet +++ /dev/null @@ -1,32 +0,0 @@ -local jobs = [ - {name: "pggat", merge: {}}, -]; -local param_job(image,tag_var, merge = {}) = std.mergePatch({ - stage: 'build', - image: { - name: 'gcr.io/kaniko-project/executor:debug', - entrypoint: [''], - }, - script: [ - 'mkdir -p /kaniko/.docker', - @'echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64)\"}}}" > /kaniko/.docker/config.json', - std.strReplace(||| - /kaniko/executor - --context ${CI_PROJECT_DIR} - --cache=true - --build-arg GOPROXY - --cache-repo="${CI_REGISTRY_IMAGE}/kaniko/cache" - --dockerfile "${CI_PROJECT_DIR}/Dockerfile" - --destination "${CI_REGISTRY_IMAGE}/%(img)s:%(tag_var)s" - --destination "${CI_REGISTRY_IMAGE}/%(img)s:latest" - ||| % {img: image, tag_var: tag_var}, "\n", " "), - ] - }, merge); -{ - [job.name+"-tag"]: param_job(job.name,"${CI_COMMIT_TAG}",std.mergePatch(job.merge, {only:["tags"]})) - for job in jobs -} + { - [job.name]: param_job(job.name,"${CI_COMMIT_SHORT_SHA}",job.merge) - for job in jobs -} - diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index fc750f96f0b5cc7858b710c2a1d57696c20fb908..0000000000000000000000000000000000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,38 +0,0 @@ -jsonnet: - stage: build - image: alpine:latest - script: - - apk add -U jsonnet - - jsonnet .gitlab-ci.jsonnet > generated-config.yml - artifacts: - paths: - - generated-config.yml - -trigger-builds: - stage: build - needs: - - jsonnet - trigger: - include: - - artifact: generated-config.yml - job: jsonnet - strategy: depend - -trigger-deploy: - stage: deploy - needs: - - trigger-builds - image: - name: bitnami/kubectl:latest - entrypoint: [''] - variables: - APP_NAME: "gfx-pggat" - IMAGE_NAME: "pggat" - RESOURCE: "deployment" - only: - variables: - - $CI_COMMIT_BRANCH == "master" - script: - - kubectl config get-contexts - - kubectl config use-context gfx/gitlab-agents:gfxlabs-dev - - kubectl patch $RESOURCE $APP_NAME -p '{"spec":{"template":{"metadata":{"labels":{"date":"'$(date +'%s')'","sha":"'${CI_COMMIT_SHA}'"}},"spec":{"containers":[{"name":"'${APP_NAME}'","image":"'${CI_REGISTRY_IMAGE}'/'${IMAGE_NAME}':latest"}]}}}}' diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 142a5afb4a0034eb98d1c5fd1a47af535a5fea4b..0000000000000000000000000000000000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "postgres"] - path = postgres - url = git@github.com:postgres/postgres.git diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b018ad684f3a35fee301741b2734c8f4..0000000000000000000000000000000000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 72c2bfa4247ab754495ba45fdfb7ca26a03e7662..0000000000000000000000000000000000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ProjectModuleManager"> - <modules> - <module fileurl="file://$PROJECT_DIR$/.idea/pggat2.iml" filepath="$PROJECT_DIR$/.idea/pggat2.iml" /> - </modules> - </component> -</project> \ No newline at end of file diff --git a/.idea/pggat2.iml b/.idea/pggat2.iml deleted file mode 100644 index 5e764c4f0b9a64bb78a5babfdd583713b2df47bf..0000000000000000000000000000000000000000 --- a/.idea/pggat2.iml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module type="WEB_MODULE" version="4"> - <component name="Go" enabled="true" /> - <component name="NewModuleRootManager"> - <content url="file://$MODULE_DIR$" /> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> -</module> \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 209cea14b7e2af0ec962357f610f02aa70a5f7a5..0000000000000000000000000000000000000000 --- a/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM golang - -WORKDIR /wd -COPY . /wd -RUN go mod tidy -RUN go build ./cmd/cgat -ENTRYPOINT ["./cgat"] diff --git a/codegen/errors/errors.txt b/codegen/errors/errors.txt deleted file mode 100644 index 8beff74a77e4ce165d0dcc2c8dda1f343215817a..0000000000000000000000000000000000000000 --- a/codegen/errors/errors.txt +++ /dev/null @@ -1,259 +0,0 @@ -00000 successful_completion -01000 warning -0100C dynamic_result_sets_returned -01008 implicit_zero_bit_padding -01003 null_value_eliminated_in_set_function -01007 privilege_not_granted -01006 privilege_not_revoked -01004 string_data_right_truncation -01P01 deprecated_feature -02000 no_data -02001 no_additional_dynamic_result_sets_returned -03000 sql_statement_not_yet_complete -08000 connection_exception -08003 connection_does_not_exist -08006 connection_failure -08001 sqlclient_unable_to_establish_sqlconnection -08004 sqlserver_rejected_establishment_of_sqlconnection -08007 transaction_resolution_unknown -08P01 protocol_violation -09000 triggered_action_exception -0A000 feature_not_supported -0B000 invalid_transaction_initiation -0F000 locator_exception -0F001 invalid_locator_specification -0L000 invalid_grantor -0LP01 invalid_grant_operation -0P000 invalid_role_specification -0Z000 diagnostics_exception -0Z002 stacked_diagnostics_accessed_without_active_handler -20000 case_not_found -21000 cardinality_violation -22000 data_exception -2202E array_subscript_error -22021 character_not_in_repertoire -22008 datetime_field_overflow -22012 division_by_zero -22005 error_in_assignment -2200B escape_character_conflict -22022 indicator_overflow -22015 interval_field_overflow -2201E invalid_argument_for_logarithm -22014 invalid_argument_for_ntile_function -22016 invalid_argument_for_nth_value_function -2201F invalid_argument_for_power_function -2201G invalid_argument_for_width_bucket_function -22018 invalid_character_value_for_cast -22007 invalid_datetime_format -22019 invalid_escape_character -2200D invalid_escape_octet -22025 invalid_escape_sequence -22P06 nonstandard_use_of_escape_character -22010 invalid_indicator_parameter_value -22023 invalid_parameter_value -22013 invalid_preceding_or_following_size -2201B invalid_regular_expression -2201W invalid_row_count_in_limit_clause -2201X invalid_row_count_in_result_offset_clause -2202H invalid_tablesample_argument -2202G invalid_tablesample_repeat -22009 invalid_time_zone_displacement_value -2200C invalid_use_of_escape_character -2200G most_specific_type_mismatch -22004 null_value_not_allowed -22002 null_value_no_indicator_parameter -22003 numeric_value_out_of_range -2200H sequence_generator_limit_exceeded -22026 string_data_length_mismatch -22001 string_data_right_truncation -22011 substring_error -22027 trim_error -22024 unterminated_c_string -2200F zero_length_character_string -22P01 floating_point_exception -22P02 invalid_text_representation -22P03 invalid_binary_representation -22P04 bad_copy_file_format -22P05 untranslatable_character -2200L not_an_xml_document -2200M invalid_xml_document -2200N invalid_xml_content -2200S invalid_xml_comment -2200T invalid_xml_processing_instruction -22030 duplicate_json_object_key_value -22031 invalid_argument_for_sql_json_datetime_function -22032 invalid_json_text -22033 invalid_sql_json_subscript -22034 more_than_one_sql_json_item -22035 no_sql_json_item -22036 non_numeric_sql_json_item -22037 non_unique_keys_in_a_json_object -22038 singleton_sql_json_item_required -22039 sql_json_array_not_found -2203A sql_json_member_not_found -2203B sql_json_number_not_found -2203C sql_json_object_not_found -2203D too_many_json_array_elements -2203E too_many_json_object_members -2203F sql_json_scalar_required -23000 integrity_constraint_violation -23001 restrict_violation -23502 not_null_violation -23503 foreign_key_violation -23505 unique_violation -23514 check_violation -23P01 exclusion_violation -24000 invalid_cursor_state -25000 invalid_transaction_state -25001 active_sql_transaction -25002 branch_transaction_already_active -25008 held_cursor_requires_same_isolation_level -25003 inappropriate_access_mode_for_branch_transaction -25004 inappropriate_isolation_level_for_branch_transaction -25005 no_active_sql_transaction_for_branch_transaction -25006 read_only_sql_transaction -25007 schema_and_data_statement_mixing_not_supported -25P01 no_active_sql_transaction -25P02 in_failed_sql_transaction -25P03 idle_in_transaction_session_timeout -26000 invalid_sql_statement_name -27000 triggered_data_change_violation -28000 invalid_authorization_specification -28P01 invalid_password -2B000 dependent_privilege_descriptors_still_exist -2BP01 dependent_objects_still_exist -2D000 invalid_transaction_termination -2F000 sql_routine_exception -2F005 function_executed_no_return_statement -2F002 modifying_sql_data_not_permitted -2F003 prohibited_sql_statement_attempted -2F004 reading_sql_data_not_permitted -34000 invalid_cursor_name -38000 external_routine_exception -38001 containing_sql_not_permitted -38002 modifying_sql_data_not_permitted -38003 prohibited_sql_statement_attempted -38004 reading_sql_data_not_permitted -39000 external_routine_invocation_exception -39001 invalid_sqlstate_returned -39004 null_value_not_allowed -39P01 trigger_protocol_violated -39P02 srf_protocol_violated -39P03 event_trigger_protocol_violated -3B000 savepoint_exception -3B001 invalid_savepoint_specification -3D000 invalid_catalog_name -3F000 invalid_schema_name -40000 transaction_rollback -40002 transaction_integrity_constraint_violation -40001 serialization_failure -40003 statement_completion_unknown -40P01 deadlock_detected -42000 syntax_error_or_access_rule_violation -42601 syntax_error -42501 insufficient_privilege -42846 cannot_coerce -42803 grouping_error -42P20 windowing_error -42P19 invalid_recursion -42830 invalid_foreign_key -42602 invalid_name -42622 name_too_long -42939 reserved_name -42804 datatype_mismatch -42P18 indeterminate_datatype -42P21 collation_mismatch -42P22 indeterminate_collation -42809 wrong_object_type -428C9 generated_always -42703 undefined_column -42883 undefined_function -42P01 undefined_table -42P02 undefined_parameter -42704 undefined_object -42701 duplicate_column -42P03 duplicate_cursor -42P04 duplicate_database -42723 duplicate_function -42P05 duplicate_prepared_statement -42P06 duplicate_schema -42P07 duplicate_table -42712 duplicate_alias -42710 duplicate_object -42702 ambiguous_column -42725 ambiguous_function -42P08 ambiguous_parameter -42P09 ambiguous_alias -42P10 invalid_column_reference -42611 invalid_column_definition -42P11 invalid_cursor_definition -42P12 invalid_database_definition -42P13 invalid_function_definition -42P14 invalid_prepared_statement_definition -42P15 invalid_schema_definition -42P16 invalid_table_definition -42P17 invalid_object_definition -44000 with_check_option_violation -53000 insufficient_resources -53100 disk_full -53200 out_of_memory -53300 too_many_connections -53400 configuration_limit_exceeded -54000 program_limit_exceeded -54001 statement_too_complex -54011 too_many_columns -54023 too_many_arguments -55000 object_not_in_prerequisite_state -55006 object_in_use -55P02 cant_change_runtime_param -55P03 lock_not_available -55P04 unsafe_new_enum_value_usage -57000 operator_intervention -57014 query_canceled -57P01 admin_shutdown -57P02 crash_shutdown -57P03 cannot_connect_now -57P04 database_dropped -57P05 idle_session_timeout -58000 system_error -58030 io_error -58P01 undefined_file -58P02 duplicate_file -72000 snapshot_too_old -F0000 config_file_error -F0001 lock_file_exists -HV000 fdw_error -HV005 fdw_column_name_not_found -HV002 fdw_dynamic_parameter_value_needed -HV010 fdw_function_sequence_error -HV021 fdw_inconsistent_descriptor_information -HV024 fdw_invalid_attribute_value -HV007 fdw_invalid_column_name -HV008 fdw_invalid_column_number -HV004 fdw_invalid_data_type -HV006 fdw_invalid_data_type_descriptors -HV091 fdw_invalid_descriptor_field_identifier -HV00B fdw_invalid_handle -HV00C fdw_invalid_option_index -HV00D fdw_invalid_option_name -HV090 fdw_invalid_string_length_or_buffer_length -HV00A fdw_invalid_string_format -HV009 fdw_invalid_use_of_null_pointer -HV014 fdw_too_many_handles -HV001 fdw_out_of_memory -HV00P fdw_no_schemas -HV00J fdw_option_name_not_found -HV00K fdw_reply_handle -HV00Q fdw_schema_not_found -HV00R fdw_table_not_found -HV00L fdw_unable_to_create_execution -HV00M fdw_unable_to_create_reply -HV00N fdw_unable_to_establish_connection -P0000 plpgsql_error -P0001 raise_exception -P0002 no_data_found -P0003 too_many_rows -P0004 assert_failure -XX000 internal_error -XX001 data_corrupted -XX002 index_corrupted \ No newline at end of file diff --git a/codegen/errors/main.go b/codegen/errors/main.go deleted file mode 100644 index e90e57f22f37934544075add1917908781efc91a..0000000000000000000000000000000000000000 --- a/codegen/errors/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "fmt" - "github.com/iancoleman/strcase" - "strings" - - _ "embed" -) - -// errors copied from https://www.postgresql.org/docs/current/errcodes-appendix.html -// -//go:embed errors.txt -var errors string - -func main() { - var out strings.Builder - lines := strings.Split(errors, "\n") - for _, line := range lines { - kv := strings.Split(line, "\t") - out.WriteString(fmt.Sprintf("%s = \"%s\"\n", strcase.ToCamel(kv[1]), kv[0])) - } - fmt.Println(out.String()) -} diff --git a/codegen/protocol/main.go b/codegen/protocol/main.go deleted file mode 100644 index 2c6746c25b61bac63339cf892ae57908836a6fdb..0000000000000000000000000000000000000000 --- a/codegen/protocol/main.go +++ /dev/null @@ -1,109 +0,0 @@ -package main - -import ( - "bytes" - "go/format" - "os" - "path/filepath" - "strings" - "text/template" - - "gopkg.in/yaml.v2" - - "github.com/iancoleman/strcase" -) - -const ( - CODEGEN = "./codegen/protocol" - INPUT = "./spec/protocol" - OUTPUT = "./lib/gat/protocol" -) - -var funcs = template.FuncMap{ - "list": func(x ...any) []any { - return x - }, - "camelCase": func(v string) string { - return strcase.ToCamel(v) - }, -} - -func main() { - f, err := os.ReadDir(INPUT) - if err != nil { - panic(err) - } - t := template.Must(template.New("packets.tmpl").Funcs(funcs).ParseFiles(filepath.Join(CODEGEN, "packets.tmpl"))) - err = os.MkdirAll(OUTPUT, 0777) - if err != nil { - panic(err) - } - backend := make(map[string]any) - frontend := make(map[string]any) - var out bytes.Buffer - for _, e := range f { - b, err := os.ReadFile(filepath.Join(INPUT, e.Name())) - if err != nil { - panic(err) - } - var packets map[string]any - err = yaml.Unmarshal(b, &packets) - if err != nil { - panic(err) - } - switch e.Name() { - case "backend.yaml": - for k, v := range packets { - backend[k] = v - } - case "frontend.yaml": - for k, v := range packets { - frontend[k] = v - } - default: - for k, v := range packets { - backend[k] = v - frontend[k] = v - } - } - - err = t.Execute(&out, packets) - if err != nil { - panic(err) - } - - var fmtd []byte - fmtd, err = format.Source(out.Bytes()) - if err != nil { - panic(err) - } - - // output to file - err = os.WriteFile(filepath.Join(OUTPUT, strings.TrimSuffix(e.Name(), filepath.Ext(e.Name()))+".go"), fmtd, 0777) - if err != nil { - panic(err) - } - - out.Reset() - } - - t = template.Must(template.New("mod.tmpl").Funcs(funcs).ParseFiles(filepath.Join(CODEGEN, "mod.tmpl"))) - err = t.Execute(&out, map[string]any{ - "BackEnd": backend, - "FrontEnd": frontend, - }) - if err != nil { - panic(err) - } - - var fmtd []byte - fmtd, err = format.Source(out.Bytes()) - if err != nil { - panic(err) - } - - err = os.WriteFile(filepath.Join(OUTPUT, "mod.go"), fmtd, 0777) - if err != nil { - panic(err) - } -} diff --git a/codegen/protocol/mod.tmpl b/codegen/protocol/mod.tmpl deleted file mode 100644 index 7b5a89d3fdc6288cc92a7fbe8585707709bea485..0000000000000000000000000000000000000000 --- a/codegen/protocol/mod.tmpl +++ /dev/null @@ -1,71 +0,0 @@ -package protocol - -import ( - "io" - "fmt" -) - -// codegen: modify for debug only - -type Packet interface { - Read(reader io.Reader) error - Write(writer io.Writer) (int, error) -} - -// ReadFrontend switches on frontend packet identifiers and returns the matching packet -// DO NOT call this function if the packet in queue does not have an identifier -func ReadFrontend(reader io.Reader) (packet Packet, err error) { - var identifier byte - identifier, err = ReadByte(reader) - if err != nil { - return - } - - switch identifier { - {{range $name, $packet := .FrontEnd -}} - {{if $packet.Identifier -}} - case byte('{{$packet.Identifier}}'): - packet = new({{$name}}) - {{end -}} - {{end -}} - default: - err = fmt.Errorf("no such packet with identifier 0x%x in Frontend", identifier) - return - } - - err = packet.Read(reader) - if err != nil { - return - } - - return -} - -// ReadBackend switches on backend packet identifier and returns the matching packet -// DO NOT call this function if the packet in queue does not have an identifier -func ReadBackend(reader io.Reader) (packet Packet, err error) { - var identifier byte - identifier, err = ReadByte(reader) - if err != nil { - return - } - - switch identifier { - {{range $name, $packet := .BackEnd -}} - {{if $packet.Identifier -}} - case byte('{{$packet.Identifier}}'): - packet = new({{$name}}) - {{end -}} - {{end -}} - default: - err = fmt.Errorf("no such packet with identifier 0x%x in Backend", identifier) - return - } - - err = packet.Read(reader) - if err != nil { - return - } - - return -} diff --git a/codegen/protocol/packets.tmpl b/codegen/protocol/packets.tmpl deleted file mode 100644 index 435705be302bccb00513b7c3e09a2d46ee55818b..0000000000000000000000000000000000000000 --- a/codegen/protocol/packets.tmpl +++ /dev/null @@ -1,243 +0,0 @@ -package protocol - -import ( - "bytes" - "io" - "gfx.cafe/util/go/bufpool" -) - -// codegen: modify for debug only - -var _ bytes.Buffer -var _ io.Reader - -{{define "fieldTypeNoArr" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - - {{if $field.Struct -}} - Fields{{$parent}}{{$field.Name}} - {{- else if $field.Type -}} - {{$field.Type}} - {{- end -}} -{{end -}} - -{{define "fieldType" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - - {{if or $field.LengthPrefixed $field.ArrayLength $field.While -}} - [] - {{- end -}} - {{template "fieldTypeNoArr" (list $parent $field) -}} -{{end -}} - -{{define "declareField" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - - {{$field.Name}} {{template "fieldType" (list $parent $field)}} -{{end -}} - -{{define "readFieldL1" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - {{$location := (index . 2) -}} - - {{if $field.Struct -}} - err = {{$location}}.Read(payloadLength, reader) - if err != nil { - return - } - {{- else if $field.Type -}} - {{$location}}, err = Read{{camelCase $field.Type}}(reader) - if err != nil { - return - } - {{- end -}} -{{end -}} - -{{define "readField" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - - {{if $field.If -}} - if {{$field.If}} { - {{end -}} - - {{if $field.LengthPrefixed -}} - var {{$field.Name}}Length {{$field.LengthPrefixed}} - {{$field.Name}}Length, err = Read{{camelCase $field.LengthPrefixed}}(reader) - if err != nil { - return - } - {{else if $field.ArrayLength -}} - {{$field.Name}}Length := {{$field.ArrayLength}} - {{end -}} - - {{if $field.LengthPrefixed -}} - if {{$field.Name}}Length == {{$field.LengthPrefixed}}(-1) { - T.{{$field.Name}} = nil - } else { - T.{{$field.Name}} = make({{template "fieldType" (list $parent $field)}}, int({{$field.Name}}Length)) - for i := 0; i < int({{$field.Name}}Length); i++ { - {{template "readFieldL1" (list $parent $field (printf "T.%s[i]" $field.Name))}} - } - } - {{else if $field.ArrayLength -}} - T.{{$field.Name}} = make({{template "fieldType" (list $parent $field)}}, int({{$field.Name}}Length)) - for i := 0; i < int({{$field.Name}}Length); i++ { - {{template "readFieldL1" (list $parent $field (printf "T.%s[i]" $field.Name))}} - } - {{else if $field.While -}} - var P {{template "fieldTypeNoArr" (list $parent $field)}} - for ok := true; ok; ok = {{$field.While}} { - var newP {{template "fieldTypeNoArr" (list $parent $field)}} - {{template "readFieldL1" (list $parent $field "newP")}} - T.{{$field.Name}} = append(T.{{$field.Name}}, newP) - P = newP - } - {{else -}} - {{template "readFieldL1" (list $parent $field (printf "T.%s" $field.Name))}} - {{end -}} - - {{if $field.If -}} - } - {{end -}} -{{end -}} - -{{define "writeFieldL1" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - {{$location := (index . 2) -}} - - {{if $field.Struct -}} - temp, err = {{$location}}.Write(writer) - if err != nil { - return - } - length += temp - {{- else if $field.Type -}} - temp, err = Write{{camelCase $field.Type}}(writer, {{$location}}) - if err != nil { - return - } - length += temp - {{- end -}} -{{end -}} - -{{define "writeField" -}} - {{$parent := (index . 0) -}} - {{$field := (index . 1) -}} - - {{if $field.If -}} - if {{$field.If}} { - {{end -}} - - {{if $field.LengthPrefixed -}} - if T.{{$field.Name}} == nil { - temp, err = Write{{camelCase $field.LengthPrefixed}}(writer, {{$field.LengthPrefixed}}(-1)) - } else { - temp, err = Write{{camelCase $field.LengthPrefixed}}(writer, {{$field.LengthPrefixed}}(len(T.{{$field.Name}}))) - } - if err != nil { - return - } - length += temp - {{end -}} - - {{if or $field.LengthPrefixed $field.ArrayLength $field.While -}} - for _, v := range T.{{$field.Name}} { - {{template "writeFieldL1" (list $parent $field "v")}} - } - {{else -}} - {{template "writeFieldL1" (list $parent $field (printf "T.%s" $field.Name))}} - {{end -}} - - {{if $field.If -}} - } - {{end -}} -{{end -}} - -{{define "declareFields" -}} - {{$parent := (index . 0) -}} - {{$struct := (index . 1) -}} - - {{range $idx, $field := $struct.Fields -}} - {{if $field.Struct -}} - {{template "declareFields" (list (printf "%s%s" $parent $field.Name) $field.Struct)}} - {{- end -}} - {{end -}} - - type Fields{{$parent}} struct { - {{range $idx, $field := $struct.Fields -}} - {{template "declareField" (list $parent $field) -}} - {{end -}} - } - - func (T *Fields{{$parent}}) Read(payloadLength int, reader io.Reader) (err error) { - {{range $idx, $field := $struct.Fields -}} - {{template "readField" (list $parent $field) -}} - {{end -}} - return - } - - func (T *Fields{{$parent}}) Write(writer io.Writer) (length int, err error) { - var temp int - {{range $idx, $field := $struct.Fields -}} - {{template "writeField" (list $parent $field) -}} - {{end -}} - _ = temp - return - } -{{end -}} - -{{range $name, $packet := . -}} - {{template "declareFields" (list $name $packet) -}} - - type {{$name}} struct { - Fields Fields{{$name}} - } - - // Read reads all but the packet identifier - {{if $packet.Identifier -}} - // WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! - {{end -}} - func (T *{{$name}}) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length - 4), reader) - } - - func (T *{{$name}}) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - {{if $packet.Identifier -}} - _, err = WriteByte(writer, byte('{{$packet.Identifier}}')) - if err != nil { - length = 1 - return - } - {{end -}} - _, err = WriteInt32(writer, int32(length) + 4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return - } - - var _ Packet = (*{{$name}})(nil) - -{{end -}} diff --git a/config_data.toml b/config_data.toml deleted file mode 100644 index cbe013b640b350951c880244df4298a12d5c711d..0000000000000000000000000000000000000000 --- a/config_data.toml +++ /dev/null @@ -1,147 +0,0 @@ -# -# PgCat config example. -# - -# -# General pooler settings -[general] -# What IP to run on, 0.0.0.0 means accessible from everywhere. -host = "0.0.0.0" - -# Port to run on, same as PgBouncer used in this example. -port = 6432 - -# Whether to enable prometheus exporter or not. -enable_prometheus_exporter = true - -# Port at which prometheus exporter listens on. -prometheus_exporter_port = 9930 - -# How long to wait before aborting a server connection (ms). -connect_timeout = 5000 - -# How much time to give `SELECT 1` health check query to return with a result (ms). -healthcheck_timeout = 1000 - -# How long to keep connection available for immediate re-use, without running a healthcheck query on it -healthcheck_delay = 30000 - -# How much time to give clients during shutdown before forcibly killing client connections (ms). -shutdown_timeout = 60000 - -# For how long to ban a server if it fails a health check (seconds). -ban_time = 60 # seconds - -# Reload config automatically if it changes. -autoreload = false - -# TLS -# tls_certificate = "server.cert" -# tls_private_key = "server.key" - -# Credentials to access the virtual administrative database (pgbouncer or pgcat) -# Connecting to that database allows running commands like `SHOW POOLS`, `SHOW DATABASES`, etc.. -admin_username = "postgres" -admin_password = "postgres" - -# pool -# configs are structured as pool.<pool_name> -# the pool_name is what clients use as database name when connecting -# For the example below a client can connect using "postgres://sharding_user:sharding_user@pgcat_host:pgcat_port/sharded" -[pools.sharded] -# Pool mode (see PgBouncer docs for more). -# session: one server connection per connected client -# transaction: one server connection per client transaction -pool_mode = "transaction" - -# If the client doesn't specify, route traffic to -# this role by default. -# -# any: round-robin between primary and replicas, -# replica: round-robin between replicas only without touching the primary, -# primary: all queries go to the primary unless otherwise specified. -default_role = "any" - -# Query parser. If enabled, we'll attempt to parse -# every incoming query to determine if it's a read or a write. -# If it's a read query, we'll direct it to a replica. Otherwise, if it's a write, -# we'll direct it to the primary. -query_parser_enabled = true - -# If the query parser is enabled and this setting is enabled, the primary will be part of the pool of databases used for -# load balancing of read queries. Otherwise, the primary will only be used for write -# queries. The primary can always be explicitely selected with our custom protocol. -primary_reads_enabled = true - -# So what if you wanted to implement a different hashing function, -# or you've already built one and you want this pooler to use it? -# -# Current options: -# -# pg_bigint_hash: PARTITION BY HASH (Postgres hashing function) -# sha1: A hashing function based on SHA1 -# -sharding_function = "pg_bigint_hash" - -# Credentials for users that may connect to this cluster -[pools.sharded.users.0] -username = "postgres" -password = "postgres" -# Maximum number of server connections that can be established for this user -# The maximum number of connection from a single Pgcat process to any database in the cluster -# is the sum of pool_size across all users. -pool_size = 9 - -# Maximum query duration. Dangerous, but protects against DBs that died in a non-obvious way. -statement_timeout = 0 - -[pools.sharded.users.1] -username = "postgres" -password = "postgres" -pool_size = 21 -statement_timeout = 15000 - -# Shard 0 -[pools.sharded.shards.0] -# [ host, port, role ] -servers = [ - [ "localhost", 5432, "primary" ], - [ "localhost", 5432, "replica" ] -] -# Database name (e.g. "postgres") -database = "postgres" - -[pools.sharded.shards.1] -servers = [ - [ "localhost", 5432, "primary" ], - [ "localhost", 5432, "replica" ], -] -database = "postgres" - -[pools.sharded.shards.2] -servers = [ - [ "localhost", 5432, "primary" ], - [ "localhost", 5432, "replica" ], -] -database = "postgres" - - -[pools.simple_db] -pool_mode = "session" -default_role = "primary" -query_parser_enabled = true -primary_reads_enabled = true -sharding_function = "pg_bigint_hash" - -[pools.simple_db.users.0] -username = "postgres" -password = "password" -pool_size = 5 -statement_timeout = 0 - -[pools.simple_db.shards.0] -servers = [ - [ "localhost", 5432, "primary" ], - [ "localhost", 5432, "replica" ] -] -database = "postgres" diff --git a/config_data.yml b/config_data.yml deleted file mode 100644 index 51f3c4701fc55ca83f01778176199684de92542b..0000000000000000000000000000000000000000 --- a/config_data.yml +++ /dev/null @@ -1,80 +0,0 @@ -general: - host: 0.0.0.0 - port: 6432 - enable_prometheus_exporter: true - prometheus_exporter_port: 9090 - connect_timeout: 5000 - healthcheck_timeout: 1000 - healthcheck_delay: 30000 - shutdown_timeout: 60000 - ban_time: 60 - autoreload: false - admin_username: ENV$PSQL_DB_ADMIN_USER - admin_password: ENV$PSQL_DB_ADMIN_PASS -pools: - postgres: - pool_mode: transaction - default_role: primary - query_parser_enabled: true - primary_reads_enabled: true - sharding_function: pg_bigint_hash - users: - - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW - role: writer - pool_size: 1 - statement_timeout: 0 - shards: - - pool_size: 5 - statement_timeout: 0 - database: postgres - servers: - - host: ENV$PSQL_PRI_DB_HOST - port: 5432 - role: primary - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW - prest: - pool_mode: transaction - default_role: primary - query_parser_enabled: true - primary_reads_enabled: true - sharding_function: pg_bigint_hash - users: - - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW - role: writer - pool_size: 1 - statement_timeout: 0 - shards: - - pool_size: 5 - statement_timeout: 0 - database: prest - servers: - - host: ENV$PSQL_PRI_DB_HOST - port: 5432 - role: primary - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW -# poolname OR databasename: -# pool_mode: session OR transaction -# default_role: primary OR replica -# query_parser_enabled: true or false -# primary_reads_enabled: true or false -# sharding_function: pg_bigint_hash -# users: -# - username: postgres -# password: password -# role: writer OR reader OR admin -# pool_size: 20 -# statement_timeout: 0 -# shards: -# - pool_size: 5 -# statement_timeout: 0 -# database: regression -# servers: -# - host: db -# port: 5432 -# role: primary OR replica -# username: postgres -# password: ENV$PGGAT_DB_PASS diff --git a/config_data.yml-template b/config_data.yml-template deleted file mode 100644 index c1d8134cba63e9408356814c06538a37e8a0d075..0000000000000000000000000000000000000000 --- a/config_data.yml-template +++ /dev/null @@ -1,90 +0,0 @@ -general: - host: 0.0.0.0 - port: 6432 - enable_prometheus_exporter: true - prometheus_exporter_port: 9090 - connect_timeout: 5000 - healthcheck_timeout: 1000 - healthcheck_delay: 30000 - shutdown_timeout: 60000 - ban_time: 60 - autoreload: false - admin_username: ENV$PSQL_DB_ADMIN_USER - admin_password: ENV$PSQL_DB_ADMIN_PASS -pools: - postgres: - pool_mode: transaction - default_role: primary - query_parser_enabled: true - primary_reads_enabled: true - sharding_function: pg_bigint_hash - users: - - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW - role: writer - pool_size: 20 - statement_timeout: 0 - shards: - - pool_size: 5 - statement_timeout: 0 - database: postgres - servers: - - host: ENV$PSQL_PRI_DB_HOST - port: 5432 - role: primary - username: ENV$PSQL_DB_USER_RW - password: postgres - - host: ENV$PSQL_REP_DB_HOST - port: 5432 - role: replica - username: ENV$PSQL_DB_USER_RO - password: ENV$PSQL_DB_PASS_RO - uniswap: - pool_mode: transaction - default_role: primary - query_parser_enabled: true - primary_reads_enabled: true - sharding_function: pg_bigint_hash - users: - - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW - role: writer - pool_size: 20 - statement_timeout: 0 - shards: - - pool_size: 5 - statement_timeout: 0 - database: uniswap - servers: - - host: ENV$PSQL_PRI_DB_HOST - port: 5432 - role: primary - username: ENV$PSQL_DB_USER_RW - password: ENV$PSQL_DB_PASS_RW - - host: ENV$PSQL_REP_DB_HOST - port: 5432 - role: replica - username: ENV$PSQL_DB_USER_RO - password: ENV$PSQL_DB_PASS_RO -# poolname OR databasename: -# pool_mode: session OR transaction -# default_role: primary OR replica -# query_parser_enabled: true or false -# primary_reads_enabled: true or false -# sharding_function: pg_bigint_hash -# users: -# - username: postgres -# password: password -# role: writer OR reader OR admin -# pool_size: 20 -# statement_timeout: 0 -# shards: -# - pool_size: 5 -# statement_timeout: 0 -# database: regression -# servers: -# - host: db -# port: 5432 -# role: primary OR replica -# username: postgres -# password: ENV$PGGAT_DB_PASS diff --git a/lib/auth/README.md b/lib/auth/README.md deleted file mode 100644 index c0deefff3b746ee7de5e68d7980d21e0be423764..0000000000000000000000000000000000000000 --- a/lib/auth/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - - - -taken from https://github.com/segmentio/kafka-go/ diff --git a/lib/auth/sasl/sasl.go b/lib/auth/sasl/sasl.go deleted file mode 100644 index 4056d1f3c2a1fb6788b082daf7ee5793eb9ad786..0000000000000000000000000000000000000000 --- a/lib/auth/sasl/sasl.go +++ /dev/null @@ -1,65 +0,0 @@ -package sasl - -import "context" - -type ctxKey struct{} - -// Mechanism implements the SASL state machine for a particular mode of -// authentication. It is used by the kafka.Dialer to perform the SASL -// handshake. -// -// A Mechanism must be re-usable and safe for concurrent access by multiple -// goroutines. -type Mechanism interface { - // Name returns the identifier for this SASL mechanism. This string will be - // passed to the SASL handshake request and much match one of the mechanisms - // supported by Kafka. - Name() string - - // Start begins SASL authentication. It returns an authentication state - // machine and "initial response" data (if required by the selected - // mechanism). A non-nil error causes the client to abort the authentication - // attempt. - // - // A nil ir value is different from a zero-length value. The nil value - // indicates that the selected mechanism does not use an initial response, - // while a zero-length value indicates an empty initial response, which must - // be sent to the server. - Start(ctx context.Context) (sess StateMachine, ir []byte, err error) -} - -// StateMachine implements the SASL challenge/response flow for a single SASL -// handshake. A StateMachine will be created by the Mechanism per connection, -// so it does not need to be safe for concurrent access by multiple goroutines. -// -// Once the StateMachine is created by the Mechanism, the caller loops by -// passing the server's response into Next and then sending Next's returned -// bytes to the server. Eventually either Next will indicate that the -// authentication has been successfully completed via the done return value, or -// it will indicate that the authentication failed by returning a non-nil error. -type StateMachine interface { - // Next continues challenge-response authentication. A non-nil error - // indicates that the client should abort the authentication attempt. If - // the client has been successfully authenticated, then the done return - // value will be true. - Next(ctx context.Context, challenge []byte) (done bool, response []byte, err error) -} - -// Metadata contains additional data for performing SASL authentication. -type Metadata struct { - // Host is the address of the broker the authentication will be - // performed on. - Host string - Port int -} - -// WithMetadata returns a copy of the context with associated Metadata. -func WithMetadata(ctx context.Context, m *Metadata) context.Context { - return context.WithValue(ctx, ctxKey{}, m) -} - -// MetadataFromContext retrieves the Metadata from the context. -func MetadataFromContext(ctx context.Context) *Metadata { - m, _ := ctx.Value(ctxKey{}).(*Metadata) - return m -} diff --git a/lib/auth/scram/scram.go b/lib/auth/scram/scram.go deleted file mode 100644 index de4131a1f626fbd65a0be6bdb3d54f5a2785612f..0000000000000000000000000000000000000000 --- a/lib/auth/scram/scram.go +++ /dev/null @@ -1,88 +0,0 @@ -package scram - -import ( - "context" - "crypto/sha256" - "crypto/sha512" - "hash" - - "gfx.cafe/gfx/pggat/lib/auth/sasl" - "github.com/xdg-go/scram" -) - -// Algorithm determines the hash function used by SCRAM to protect the user's -// credentials. -type Algorithm interface { - // Name returns the algorithm's name, e.g. "SCRAM-SHA-256" - Name() string - - // Hash returns a new hash.Hash. - Hash() hash.Hash -} - -type sha256Algo struct{} - -func (sha256Algo) Name() string { - return "SCRAM-SHA-256" -} - -func (sha256Algo) Hash() hash.Hash { - return sha256.New() -} - -type sha512Algo struct{} - -func (sha512Algo) Name() string { - return "SCRAM-SHA-512" -} - -func (sha512Algo) Hash() hash.Hash { - return sha512.New() -} - -var ( - SHA256 Algorithm = sha256Algo{} - SHA512 Algorithm = sha512Algo{} -) - -type mechanism struct { - algo Algorithm - client *scram.Client -} - -type session struct { - convo *scram.ClientConversation -} - -// Mechanism returns a new sasl.Mechanism that will use SCRAM with the provided -// Algorithm to securely transmit the provided credentials to postgres -func Mechanism(algo Algorithm, username, password string) (sasl.Mechanism, error) { - hashGen := scram.HashGeneratorFcn(algo.Hash) - client, err := hashGen.NewClient(username, password, "") - if err != nil { - return nil, err - } - - return &mechanism{ - algo: algo, - client: client, - }, nil -} - -func (m *mechanism) Name() string { - return m.algo.Name() -} - -func (m *mechanism) Start(ctx context.Context) (sasl.StateMachine, []byte, error) { - convo := m.client.NewConversation() - str, err := convo.Step("") - if err != nil { - return nil, nil, err - } - return &session{convo: convo}, []byte(str), nil -} - -func (s *session) Next(ctx context.Context, challenge []byte) (bool, []byte, error) { - str, err := s.convo.Step(string(challenge)) - return s.convo.Done(), []byte(str), err -} diff --git a/lib/config/config.go b/lib/config/config.go deleted file mode 100644 index 26522f2370bec703db20ebf14dc9368b852e1d0a..0000000000000000000000000000000000000000 --- a/lib/config/config.go +++ /dev/null @@ -1,159 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/BurntSushi/toml" - "gopkg.in/yaml.v3" -) - -type PoolMode string - -const ( - POOLMODE_SESSION PoolMode = "session" - POOLMODE_TXN PoolMode = "transaction" -) - -type ServerRole string - -const ( - SERVERROLE_PRIMARY ServerRole = "primary" - SERVERROLE_REPLICA ServerRole = "replica" - SERVERROLE_NONE ServerRole = "NONE" -) - -type UserRole string - -const ( - USERROLE_ADMIN UserRole = "admin" - USERROLE_WRITER UserRole = "writer" - USERROLE_READER UserRole = "reader" -) - -func (r UserRole) CanUse(which ServerRole) bool { - switch r { - case USERROLE_ADMIN: - return true - case USERROLE_WRITER: - return true - case USERROLE_READER: - return which != SERVERROLE_PRIMARY - default: - panic(fmt.Sprintf("unknown role `%s`", which)) - } -} - -func getIfEnv(field *string) { - if strings.HasPrefix(*field, "ENV$") { - *field = os.Getenv(strings.TrimPrefix(*field, "ENV$")) - } -} - -func Load(path string) (*Global, error) { - var g Global - ext := filepath.Ext(path) - file, err := os.ReadFile(path) - if err != nil { - return nil, err - } - switch ext { - case "toml": - err := toml.Unmarshal(file, &g) - if err != nil { - return nil, err - } - case "yml", "yaml", "json": - fallthrough - default: - err := yaml.Unmarshal(file, &g) - if err != nil { - return nil, err - } - } - - getIfEnv(&g.General.AdminUsername) - getIfEnv(&g.General.AdminPassword) - - for _, g := range g.Pools { - for _, shard := range g.Shards { - for _, srv := range shard.Servers { - getIfEnv(&srv.Host) - getIfEnv(&srv.Username) - getIfEnv(&srv.Password) - } - for _, srv := range g.Users { - getIfEnv(&srv.Name) - getIfEnv(&srv.Password) - } - } - } - return &g, nil -} - -type Global struct { - General General `toml:"general" yaml:"general" json:"general"` - Pools map[string]*Pool `toml:"pools" yaml:"pools" json:"pools"` -} - -type General struct { - Host string `toml:"host" yaml:"host" json:"host"` - Port uint16 `toml:"port" yaml:"port" json:"port"` - - AdminOnly bool `toml:"admin_only" yaml:"admin_only" json:"admin_only"` - AdminUsername string `toml:"admin_username" yaml:"admin_username" json:"admin_username"` - AdminPassword string `toml:"admin_password" yaml:"admin_password" json:"admin_password"` - - EnableMetrics bool `toml:"enable_prometheus_exporter" yaml:"enable_prometheus_exporter" json:"enable_prometheus_exporter"` - MetricsPort uint16 `toml:"prometheus_exporter_port" yaml:"prometheus_exporter_port" json:"prometheus_exporter_port"` - - PoolSize int `toml:"pool_size" yaml:"pool_size" json:"pool_size"` - PoolMode PoolMode `toml:"pool_mode" yaml:"pool_mode" json:"pool_mode"` - - ConnectTimeout int `toml:"connect_timeout" yaml:"connect_timeout" json:"connect_timeout"` - HealthCheckTimeout int `toml:"healthcheck_timeout" yaml:"healthcheck_timeout" json:"healthcheck_timeout"` - ShutdownTimeout int `toml:"shutdown_timeout" yaml:"shutdown_timeout" json:"shutdown_timeout"` - - BanTime int `toml:"ban_time" yaml:"ban_time" json:"ban_time"` - - TlsCertificate string `toml:"tls_certificate" yaml:"tls_certificate" json:"tls_certificate"` - TlsPrivateKey string `toml:"tls_private_key" yaml:"tls_private_key" json:"tls_private_key"` - - AutoReload bool `toml:"autoreload" yaml:"autoreload" json:"autoreload"` -} - -type Pool struct { - PoolMode PoolMode `toml:"pool_mode" yaml:"pool_mode" json:"pool_mode"` - DefaultRole string `toml:"default_role" yaml:"default_role" json:"default_role"` - QueryParserEnabled bool `toml:"query_parser_enabled" yaml:"query_parser_enabled" json:"query_parser_enabled"` - PrimaryReadsEnabled bool `toml:"primary_reads_enabled" yaml:"primary_reads_enabled" json:"primary_reads_enabled"` - ShardingFunction string `toml:"sharding_function" yaml:"sharding_function" json:"sharding_function"` - - Shards []*Shard `toml:"shards" yaml:"shards" json:"shards"` - Users []*User `toml:"users" yaml:"users" json:"users"` -} - -type User struct { - Name string `toml:"username" yaml:"username" json:"username"` - Password string `toml:"password" yaml:"password" json:"password"` - - Role UserRole `toml:"role" yaml:"role" json:"role"` - PoolSize int `toml:"pool_size" yaml:"pool_size" json:"pool_size"` - StatementTimeout int `toml:"statement_timeout" yaml:"statement_timeout" json:"statement_timeout"` -} - -type Shard struct { - Database string `toml:"database" yaml:"database" json:"database"` - Servers []*Server `toml:"servers" yaml:"servers" json:"servers"` -} - -type Server struct { - Host string `toml:"host" yaml:"host" json:"host"` - Port uint16 `toml:"port" yaml:"port" json:"port"` - Role ServerRole `toml:"role" yaml:"role" json:"role"` - - Username string `toml:"username" yaml:"username" json:"username"` - Password string `toml:"password" yaml:"password" json:"password"` -} diff --git a/lib/config/config_test.go b/lib/config/config_test.go deleted file mode 100644 index 7365c766b2029adf5dfd35b7a23c361cb8c2902f..0000000000000000000000000000000000000000 --- a/lib/config/config_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package config - -import ( - "os" - "testing" - - "github.com/BurntSushi/toml" -) - -func TestParseToml(t *testing.T) { - var c Global - bts, err := os.ReadFile("./config_data.toml") - if err != nil { - t.Error(err) - } - err = toml.Unmarshal(bts, &c) - if err != nil { - t.Error(err) - } - - // TODO: write the rest of this test - if len(c.Pools) != 2 { - t.Errorf("expect 2 pool, got %d", len(c.Pools)) - } - if c.General.Host != "0.0.0.0" { - t.Errorf("expect host %s, got %s", "0.0.0.0", c.General.Host) - } -} diff --git a/lib/gat/admin/admin.go b/lib/gat/admin/admin.go deleted file mode 100644 index 0dc63ca41385e12c9fed6f793bcd4bee34265b1a..0000000000000000000000000000000000000000 --- a/lib/gat/admin/admin.go +++ /dev/null @@ -1,314 +0,0 @@ -package admin - -import ( - "context" - "errors" - "gfx.cafe/gfx/pggat/lib/util/gatutil" - - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "gfx.cafe/gfx/pggat/lib/parse" - "gfx.cafe/gfx/pggat/lib/util/cmux" -) - -// The admin database, implemented through the gat.Database interface, allowing it to be added to any existing Gat - -const DataType_String = 25 -const DataType_Int64 = 20 -const DataType_Float64 = 701 - -func getServerInfo(g gat.Gat) []*protocol.ParameterStatus { - return []*protocol.ParameterStatus{ - { - Fields: protocol.FieldsParameterStatus{ - Parameter: "application_name", - Value: "", - }, - }, - { - Fields: protocol.FieldsParameterStatus{ - Parameter: "client_encoding", - Value: "UTF8", - }, - }, - { - Fields: protocol.FieldsParameterStatus{ - Parameter: "server_encoding", - Value: "UTF8", - }, - }, - { - Fields: protocol.FieldsParameterStatus{ - Parameter: "server_encoding", - Value: "UTF8", - }, - }, - { - Fields: protocol.FieldsParameterStatus{ - Parameter: "server_version", - Value: g.GetVersion(), - }, - }, - { - Fields: protocol.FieldsParameterStatus{ - Parameter: "DataStyle", - Value: "ISO, MDY", - }, - }, - } -} - -func getAdminUser(g gat.Gat) *config.User { - conf := g.GetConfig() - return &config.User{ - Name: conf.General.AdminUsername, - Password: conf.General.AdminPassword, - - Role: config.USERROLE_ADMIN, - PoolSize: 1, - StatementTimeout: 0, - } -} - -type Database struct { - gat gat.Gat - connPool *Pool - - r cmux.Mux[gat.Client, error] -} - -func New(g gat.Gat) *Database { - out := &Database{ - gat: g, - } - out.connPool = &Pool{ - database: out, - } - out.r = cmux.NewMapMux[gat.Client, error]() - - out.r.Register([]string{"show", "servers"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "clients"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "pools"}, func(c gat.Client, _ []string) error { - table := gatutil.Table{ - Header: gatutil.TableHeader{ - Columns: []gatutil.TableHeaderColumn{ - { - Name: "Database", - Type: gatutil.Text{}, - }, - { - Name: "User", - Type: gatutil.Text{}, - }, - { - Name: "Min latency", - Type: gatutil.Float64{}, - }, - { - Name: "Max latency", - Type: gatutil.Float64{}, - }, - { - Name: "Avg latency", - Type: gatutil.Float64{}, - }, - { - Name: "Request Count", - Type: gatutil.Int64{}, - }, - }, - }, - } - for dbname, db := range g.GetDatabases() { - for _, pool := range db.GetPools() { - uname := pool.GetUser().Name - table.Rows = append(table.Rows, gatutil.TableRow{ - Columns: []any{ - dbname, - uname, - float64(0), - float64(0), - float64(0), - int64(0), - }, - }) - } - } - return table.Send(c) - }) - out.r.Register([]string{"show", "lists"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "users"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "databases"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "fds"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "sockets"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "active_sockets"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "config"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "mem"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "dns_hosts"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "dns_zones"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"show", "version"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"pause"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"disable"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"enable"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"reconnect"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"kill"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"suspend"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"resume"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"shutdown"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"reload"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"wait_close"}, func(_ gat.Client, _ []string) error { - return nil - }) - out.r.Register([]string{"set"}, func(_ gat.Client, _ []string) error { - return nil - }) - return out -} - -func (p *Database) GetUser(name string) *config.User { - u := getAdminUser(p.gat) - if name != u.Name { - return nil - } - return u -} - -func (p *Database) GetRouter() gat.QueryRouter { - return nil -} - -func (p *Database) GetName() string { - return "pggat" -} - -func (p *Database) WithUser(name string) gat.Pool { - conf := p.gat.GetConfig() - if name != conf.General.AdminUsername { - return nil - } - return p.connPool -} - -func (p *Database) GetPools() []gat.Pool { - return []gat.Pool{ - p.connPool, - } -} - -func (p *Database) EnsureConfig(c *config.Pool) { - // TODO -} - -var _ gat.Database = (*Database)(nil) - -type Pool struct { - database *Database -} - -func (c *Pool) GetUser() *config.User { - return getAdminUser(c.database.gat) -} - -func (c *Pool) GetServerInfo(_ gat.Client) []*protocol.ParameterStatus { - return getServerInfo(c.database.gat) -} - -func (c *Pool) GetDatabase() gat.Database { - return c.database -} - -func (c *Pool) EnsureConfig(conf *config.Pool, u *config.User) { - // TODO -} - -func (c *Pool) OnDisconnect(_ gat.Client) {} - -func (c *Pool) Describe(ctx context.Context, client gat.Client, describe *protocol.Describe) error { - return errors.New("describe not implemented") -} - -func (c *Pool) Execute(ctx context.Context, client gat.Client, execute *protocol.Execute) error { - return errors.New("execute not implemented") -} - -func (c *Pool) SimpleQuery(ctx context.Context, client gat.Client, query string) error { - parsed, err := parse.Parse(query) - if err != nil { - return err - } - if len(parsed) == 0 { - return client.Send(new(protocol.EmptyQueryResponse)) - } - for _, cmd := range parsed { - var matched bool - err, matched = c.database.r.Call(client, append([]string{cmd.Command}, cmd.Arguments...)) - if !matched { - return errors.New("unknown command") - } - if err != nil { - return err - } - done := new(protocol.CommandComplete) - done.Fields.Data = cmd.Command - err = client.Send(done) - if err != nil { - return err - } - } - return nil -} - -func (c *Pool) Transaction(ctx context.Context, client gat.Client, query string) error { - return errors.New("transactions not implemented") -} - -func (c *Pool) CallFunction(ctx context.Context, client gat.Client, payload *protocol.FunctionCall) error { - return errors.New("functions not implemented") -} - -var _ gat.Pool = (*Pool)(nil) diff --git a/lib/gat/admin/admin_test.go b/lib/gat/admin/admin_test.go deleted file mode 100644 index 96cc0a094153fddb27423bcff78e213eb4b09bfe..0000000000000000000000000000000000000000 --- a/lib/gat/admin/admin_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package admin - -// TODO: no tests in original package. we shoul write our oen diff --git a/lib/gat/database/database.go b/lib/gat/database/database.go deleted file mode 100644 index 36e4f36dad21c5d7f9f38838ec1d0ad7d998ef75..0000000000000000000000000000000000000000 --- a/lib/gat/database/database.go +++ /dev/null @@ -1,103 +0,0 @@ -package database - -import ( - "sync" - - "gfx.cafe/gfx/pggat/lib/gat/database/query_router" - "gfx.cafe/gfx/pggat/lib/gat/pool/session" - "gfx.cafe/gfx/pggat/lib/gat/pool/transaction" - - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" -) - -type Database struct { - c *config.Pool - users map[string]config.User - connPools map[string]gat.Pool - name string - - router *query_router.QueryRouter - - dialer gat.Dialer - - mu sync.RWMutex -} - -func New(dialer gat.Dialer, name string, conf *config.Pool) *Database { - pool := &Database{ - connPools: make(map[string]gat.Pool), - router: query_router.DefaultRouter(conf), - name: name, - - dialer: dialer, - } - pool.EnsureConfig(conf) - return pool -} - -func (p *Database) EnsureConfig(conf *config.Pool) { - p.mu.Lock() - defer p.mu.Unlock() - p.c = conf - p.users = make(map[string]config.User) - for _, user := range conf.Users { - p.users[user.Name] = *user - } - // ensure conn pools - for name, user := range p.users { - u := user - if existing, ok := p.connPools[name]; ok { - existing.EnsureConfig(conf, &u) - } else { - switch p.c.PoolMode { - case config.POOLMODE_SESSION: - p.connPools[name] = session.New(p, p.dialer, conf, &u) - case config.POOLMODE_TXN: - p.connPools[name] = transaction.New(p, p.dialer, conf, &u) - } - } - } -} - -func (p *Database) GetUser(name string) *config.User { - p.mu.RLock() - defer p.mu.RUnlock() - user, ok := p.users[name] - if !ok { - return nil - } - return &user -} - -func (p *Database) GetRouter() gat.QueryRouter { - return p.router -} - -func (p *Database) GetName() string { - return p.name -} - -func (p *Database) WithUser(name string) gat.Pool { - p.mu.RLock() - defer p.mu.RUnlock() - pool, ok := p.connPools[name] - if !ok { - return nil - } - return pool -} - -func (p *Database) GetPools() []gat.Pool { - p.mu.RLock() - defer p.mu.RUnlock() - out := make([]gat.Pool, len(p.connPools)) - idx := 0 - for _, v := range p.connPools { - out[idx] = v - idx += 1 - } - return out -} - -var _ gat.Database = (*Database)(nil) diff --git a/lib/gat/database/database_test.go b/lib/gat/database/database_test.go deleted file mode 100644 index e2f1b03a90cb076a0edd2b830b737cc0baa1655e..0000000000000000000000000000000000000000 --- a/lib/gat/database/database_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package database - -// TODO: no tests, we need to write our own diff --git a/lib/gat/database/query_router/query_router.go b/lib/gat/database/query_router/query_router.go deleted file mode 100644 index 3df02ff2a8bc23b084151ad78415fa210aaff92b..0000000000000000000000000000000000000000 --- a/lib/gat/database/query_router/query_router.go +++ /dev/null @@ -1,149 +0,0 @@ -package query_router - -import ( - "errors" - "strconv" - "unicode" - "unicode/utf8" - - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/util/cmux" - "gfx.cafe/ghalliday1/pg3p" - "gfx.cafe/ghalliday1/pg3p/lex" - "gfx.cafe/util/go/generic" -) - -type QueryRouter struct { - router cmux.Mux[gat.Client, error] - parsers generic.HookPool[*pg3p.Parser] - c *config.Pool -} - -var defaultMux = func() *cmux.MapMux[gat.Client, error] { - r := cmux.NewMapMux[gat.Client, error]() - r.Register([]string{"set", "sharding", "key", "to"}, func(_ gat.Client, args []string) error { - return nil - }) - r.Register([]string{"set", "shard", "to"}, func(client gat.Client, args []string) error { - if len(args) == 0 { - return errors.New("expected at least one argument") - } - - v := args[0] - r, l := utf8.DecodeRuneInString(v) - if !unicode.IsNumber(r) { - if len(v)-l <= l { - return errors.New("malformed input") - } - v = v[l : len(v)-l] - } - - if v == "any" { - client.UnsetRequestedShard() - return nil - } - - num, err := strconv.Atoi(v) - if err != nil { - return err - } - - client.SetRequestedShard(num) - - return nil - }) - r.Register([]string{"show", "shard"}, func(_ gat.Client, args []string) error { - return nil - }) - r.Register([]string{"set", "server", "role", "to"}, func(_ gat.Client, args []string) error { - return nil - }) - r.Register([]string{"show", "server", "role"}, func(_ gat.Client, args []string) error { - return nil - }) - r.Register([]string{"set", "primary", "reads", "to"}, func(_ gat.Client, args []string) error { - return nil - }) - r.Register([]string{"show", "primary", "reads"}, func(_ gat.Client, args []string) error { - return nil - }) - return r -}() - -func DefaultRouter(c *config.Pool) *QueryRouter { - return &QueryRouter{ - router: defaultMux, - parsers: generic.HookPool[*pg3p.Parser]{ - New: pg3p.NewParser, - }, - c: c, - } -} - -// Try to infer the server role to try to connect to -// based on the contents of the query. -// note that the user needs to be checked to see if they are allowed to access. -func (r *QueryRouter) InferRole(query string) (config.ServerRole, error) { - // if we don't want to parse queries, route them to primary - if !r.c.QueryParserEnabled { - return config.SERVERROLE_PRIMARY, nil - } - // parse the query - parser := r.parsers.Get() - defer r.parsers.Put(parser) - tokens := parser.Lex(query) - depth := 0 - for _, token := range tokens { - switch token.Token { - case lex.KeywordUpdate, - lex.KeywordDelete, - lex.KeywordInsert, - lex.KeywordDrop, - lex.KeywordCreate, - lex.KeywordTruncate, - lex.KeywordVacuum, - lex.KeywordAnalyze, - lex.KeywordAlter, - lex.KeywordGrant, - lex.KeywordComment: - return config.SERVERROLE_PRIMARY, nil - case lex.KeywordBegin: - depth += 1 - case lex.KeywordCase: - if depth > 0 { - depth += 1 - } - case lex.KeywordEnd: - if depth > 0 { - depth -= 1 - } - } - } - if depth > 0 { - return config.SERVERROLE_PRIMARY, nil - } - return config.SERVERROLE_REPLICA, nil -} - -func (r *QueryRouter) TryHandle(client gat.Client, query string) (handled bool, err error) { - if !r.c.QueryParserEnabled { - return - } - /*var parsed parser.Statements TODO - parsed, err = parser.Parse(query) - if err != nil { - return - } - if len(parsed) == 0 { - // send empty query - err = client.Send(new(protocol.EmptyQueryResponse)) - return true, err - } - if len(parsed) != 1 { - return - } - err, handled = r.router.Call(client, append([]string{parsed[0].Cmd}, parsed[0].Arguments...)) - */ - return -} diff --git a/lib/gat/database/query_router/query_router_test.go b/lib/gat/database/query_router/query_router_test.go deleted file mode 100644 index f94af3a9323742a9f75d0701cf7fc4520dcf9c6b..0000000000000000000000000000000000000000 --- a/lib/gat/database/query_router/query_router_test.go +++ /dev/null @@ -1,307 +0,0 @@ -package query_router - -import ( - "testing" - - "gfx.cafe/gfx/pggat/lib/config" -) - -// TODO: adapt tests -func TestQueryRouterInterRoleReplica(t *testing.T) { - qr := DefaultRouter(nil) - role, err := qr.InferRole(`UPDATE items SET name = 'pumpkin' WHERE id = 5`) - if err != nil { - t.Fatal(err) - } - if role != config.SERVERROLE_PRIMARY { - t.Error("expect primary") - } - role, err = qr.InferRole(`select * from items WHERE id = 5`) - if err != nil { - t.Fatal(err) - } - if role != config.SERVERROLE_REPLICA { - t.Error("expect replica") - } - -} - -// assert!(qr.try_execute_command(simple_query("SET SERVER ROLE TO 'auto'")) != None); -// assert_eq!(qr.query_parser_enabled(), true); - -// assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None); - -// let queries = vec![ -// simple_query("SELECT * FROM items WHERE id = 5"), -// simple_query( -// "SELECT id, name, value FROM items INNER JOIN prices ON item.id = prices.item_id", -// ), -// simple_query("WITH t AS (SELECT * FROM items) SELECT * FROM t"), -// ]; - -// for query in queries { -// // It's a recognized query -// assert!(qr.infer_role(query)); -// assert_eq!(qr.role(), Some(Role::Replica)); -// } -// } -// -// #[test] -// fn test_infer_role_primary() { -// QueryRouter::setup(); -// let mut qr = QueryRouter::new(); -// -// let queries = vec![ -// simple_query("UPDATE items SET name = 'pumpkin' WHERE id = 5"), -// simple_query("INSERT INTO items (id, name) VALUES (5, 'pumpkin')"), -// simple_query("DELETE FROM items WHERE id = 5"), -// simple_query("BEGIN"), // Transaction start -// ]; -// -// for query in queries { -// // It's a recognized query -// assert!(qr.infer_role(query)); -// assert_eq!(qr.role(), Some(Role::GetPrimary)); -// } -// } -// -// #[test] -// fn test_infer_role_primary_reads_enabled() { -// QueryRouter::setup(); -// let mut qr = QueryRouter::new(); -// let query = simple_query("SELECT * FROM items WHERE id = 5"); -// assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO on")) != None); -// -// assert!(qr.infer_role(query)); -// assert_eq!(qr.role(), None); -// } -// -// #[test] -// fn test_infer_role_parse_prepared() { -// QueryRouter::setup(); -// let mut qr = QueryRouter::new(); -// qr.try_execute_command(simple_query("SET SERVER ROLE TO 'auto'")); -// assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None); -// -// let prepared_stmt = BytesMut::from( -// &b"WITH t AS (SELECT * FROM items WHERE name = $1) SELECT * FROM t WHERE id = $2\0"[..], -// ); -// let mut res = BytesMut::from(&b"P"[..]); -// res.put_i32(prepared_stmt.len() as i32 + 4 + 1 + 2); -// res.put_u8(0); -// res.put(prepared_stmt); -// res.put_i16(0); -// -// assert!(qr.infer_role(res)); -// assert_eq!(qr.role(), Some(Role::Replica)); -// } -// -// #[test] -// fn test_regex_set() { -// QueryRouter::setup(); -// -// let tests = [ -// // Upper case -// "SET SHARDING KEY TO '1'", -// "SET SHARD TO '1'", -// "SHOW SHARD", -// "SET SERVER ROLE TO 'replica'", -// "SET SERVER ROLE TO 'primary'", -// "SET SERVER ROLE TO 'any'", -// "SET SERVER ROLE TO 'auto'", -// "SHOW SERVER ROLE", -// "SET PRIMARY READS TO 'on'", -// "SET PRIMARY READS TO 'off'", -// "SET PRIMARY READS TO 'default'", -// "SHOW PRIMARY READS", -// // Lower case -// "set sharding key to '1'", -// "set shard to '1'", -// "show shard", -// "set server role to 'replica'", -// "set server role to 'primary'", -// "set server role to 'any'", -// "set server role to 'auto'", -// "show server role", -// "set primary reads to 'on'", -// "set primary reads to 'OFF'", -// "set primary reads to 'deFaUlt'", -// // No quotes -// "SET SHARDING KEY TO 11235", -// "SET SHARD TO 15", -// "SET PRIMARY READS TO off", -// // Spaces and semicolon -// " SET SHARDING KEY TO 11235 ; ", -// " SET SHARD TO 15; ", -// " SET SHARDING KEY TO 11235 ;", -// " SET SERVER ROLE TO 'primary'; ", -// " SET SERVER ROLE TO 'primary' ; ", -// " SET SERVER ROLE TO 'primary' ;", -// " SET PRIMARY READS TO 'off' ;", -// ]; -// -// // Which regexes it'll match to in the list -// let matches = [ -// 0, 1, 2, 3, 3, 3, 3, 4, 5, 5, 5, 6, 0, 1, 2, 3, 3, 3, 3, 4, 5, 5, 5, 0, 1, 5, 0, 1, 0, -// 3, 3, 3, 5, -// ]; -// -// let list = CUSTOM_SQL_REGEX_LIST.get().unwrap(); -// let set = CUSTOM_SQL_REGEX_SET.get().unwrap(); -// -// for (i, test) in tests.iter().enumerate() { -// if !list[matches[i]].is_match(test) { -// println!("{} does not match {}", test, list[matches[i]]); -// assert!(false); -// } -// assert_eq!(set.matches(test).into_iter().collect::<Vec<_>>().len(), 1); -// } -// -// let bad = [ -// "SELECT * FROM table", -// "SELECT * FROM table WHERE value = 'set sharding key to 5'", // Don't capture things in the middle of the query -// ]; -// -// for query in &bad { -// assert_eq!(set.matches(query).into_iter().collect::<Vec<_>>().len(), 0); -// } -// } -// -// #[test] -// fn test_try_execute_command() { -// QueryRouter::setup(); -// let mut qr = QueryRouter::new(); -// -// // SetShardingKey -// let query = simple_query("SET SHARDING KEY TO 13"); -// assert_eq!( -// qr.try_execute_command(query), -// Some((Command::SetShardingKey, String::from("0"))) -// ); -// assert_eq!(qr.shard(), 0); -// -// // SetShard -// let query = simple_query("SET SHARD TO '1'"); -// assert_eq!( -// qr.try_execute_command(query), -// Some((Command::SetShard, String::from("1"))) -// ); -// assert_eq!(qr.shard(), 1); -// -// // ShowShard -// let query = simple_query("SHOW SHARD"); -// assert_eq!( -// qr.try_execute_command(query), -// Some((Command::ShowShard, String::from("1"))) -// ); -// -// // SetServerRole -// let roles = ["primary", "replica", "any", "auto", "primary"]; -// let verify_roles = [ -// Some(Role::GetPrimary), -// Some(Role::Replica), -// None, -// None, -// Some(Role::GetPrimary), -// ]; -// let query_parser_enabled = [false, false, false, true, false]; -// -// for (idx, role) in roles.iter().enumerate() { -// let query = simple_query(&format!("SET SERVER ROLE TO '{}'", role)); -// assert_eq!( -// qr.try_execute_command(query), -// Some((Command::SetServerRole, String::from(*role))) -// ); -// assert_eq!(qr.role(), verify_roles[idx],); -// assert_eq!(qr.query_parser_enabled(), query_parser_enabled[idx],); -// -// // ShowServerRole -// let query = simple_query("SHOW SERVER ROLE"); -// assert_eq!( -// qr.try_execute_command(query), -// Some((Command::ShowServerRole, String::from(*role))) -// ); -// } -// -// let primary_reads = ["on", "off", "default"]; -// let primary_reads_enabled = ["on", "off", "on"]; -// -// for (idx, primary_reads) in primary_reads.iter().enumerate() { -// assert_eq!( -// qr.try_execute_command(simple_query(&format!( -// "SET PRIMARY READS TO {}", -// primary_reads -// ))), -// Some((Command::SetPrimaryReads, String::from(*primary_reads))) -// ); -// assert_eq!( -// qr.try_execute_command(simple_query("SHOW PRIMARY READS")), -// Some(( -// Command::ShowPrimaryReads, -// String::from(primary_reads_enabled[idx]) -// )) -// ); -// } -// } -// -// #[test] -// fn test_enable_query_parser() { -// QueryRouter::setup(); -// let mut qr = QueryRouter::new(); -// let query = simple_query("SET SERVER ROLE TO 'auto'"); -// assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None); -// -// assert!(qr.try_execute_command(query) != None); -// assert!(qr.query_parser_enabled()); -// assert_eq!(qr.role(), None); -// -// let query = simple_query("INSERT INTO test_table VALUES (1)"); -// assert_eq!(qr.infer_role(query), true); -// assert_eq!(qr.role(), Some(Role::GetPrimary)); -// -// let query = simple_query("SELECT * FROM test_table"); -// assert_eq!(qr.infer_role(query), true); -// assert_eq!(qr.role(), Some(Role::Replica)); -// -// assert!(qr.query_parser_enabled()); -// let query = simple_query("SET SERVER ROLE TO 'default'"); -// assert!(qr.try_execute_command(query) != None); -// assert!(qr.query_parser_enabled()); -// } -// -// #[test] -// fn test_update_from_pool_settings() { -// QueryRouter::setup(); -// -// let pool_settings = PoolSettings { -// pool_mode: PoolMode::Transaction, -// shards: 0, -// user: crate::config::User::default(), -// default_role: Some(Role::Replica), -// query_parser_enabled: true, -// primary_reads_enabled: false, -// sharding_function: ShardingFunction::PgBigintHash, -// }; -// let mut qr = QueryRouter::new(); -// assert_eq!(qr.active_role, None); -// assert_eq!(qr.active_shard, None); -// assert_eq!(qr.query_parser_enabled, false); -// assert_eq!(qr.primary_reads_enabled, false); -// -// // Internal state must not be changed due to this, only defaults -// qr.update_pool_settings(pool_settings.clone()); -// -// assert_eq!(qr.active_role, None); -// assert_eq!(qr.active_shard, None); -// assert_eq!(qr.query_parser_enabled, false); -// assert_eq!(qr.primary_reads_enabled, false); -// -// let q1 = simple_query("SET SERVER ROLE TO 'primary'"); -// assert!(qr.try_execute_command(q1) != None); -// assert_eq!(qr.active_role.unwrap(), Role::GetPrimary); -// -// let q2 = simple_query("SET SERVER ROLE TO 'default'"); -// assert!(qr.try_execute_command(q2) != None); -// assert_eq!(qr.active_role.unwrap(), pool_settings.clone().default_role); -// } -//} diff --git a/lib/gat/gatling/client/client.go b/lib/gat/gatling/client/client.go deleted file mode 100644 index accc49e7591a768af87c7e4e6814a005fa26ee0a..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/client/client.go +++ /dev/null @@ -1,689 +0,0 @@ -package client - -import ( - "bufio" - "context" - "crypto/rand" - "crypto/tls" - "errors" - "fmt" - "io" - "math" - "math/big" - "net" - "reflect" - "sync" - "sync/atomic" - "time" - - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/gatling/messages" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "gfx.cafe/gfx/pggat/lib/gat/protocol/pg_error" - "gfx.cafe/gfx/pggat/lib/metrics" - "gfx.cafe/ghalliday1/pg3p" - "gfx.cafe/ghalliday1/pg3p/lex" - "git.tuxpa.in/a/zlog" - "git.tuxpa.in/a/zlog/log" -) - -type CountReader[T io.Reader] struct { - BytesRead atomic.Int64 - Reader T -} - -func NewCountReader[T io.Reader](reader T) *CountReader[T] { - return &CountReader[T]{ - Reader: reader, - } -} - -func (C *CountReader[T]) Read(p []byte) (n int, err error) { - n, err = C.Reader.Read(p) - C.BytesRead.Add(int64(n)) - return -} - -type CountWriter[T io.Writer] struct { - BytesWritten atomic.Int64 - Writer T -} - -func NewCountWriter[T io.Writer](writer T) *CountWriter[T] { - return &CountWriter[T]{ - Writer: writer, - } -} - -func (C *CountWriter[T]) Write(p []byte) (n int, err error) { - n, err = C.Writer.Write(p) - C.BytesWritten.Add(int64(n)) - return -} - -type parsedQuery struct { - request *protocol.Parse - role config.ServerRole -} - -// / client state, one per client -type Client struct { - conn net.Conn - r *CountReader[*bufio.Reader] - wr *CountWriter[*bufio.Writer] - - recv chan protocol.Packet - - options []protocol.FieldsStartupMessageParameters - - state gat.ClientState - - pid int32 - secretKey int32 - - requestTime time.Time - connectTime time.Time - - server gat.Pool - - poolName string - username string - - shardingKey string - preferredShard int - hasPreferredShard bool - - gatling gat.Gat - currentConn gat.Connection - statements map[string]parsedQuery - portals map[string]*protocol.Bind - conf *config.Global - - parser *pg3p.Parser - - log zlog.Logger - - mu sync.Mutex -} - -func (c *Client) GetOptions() []protocol.FieldsStartupMessageParameters { - return c.options -} - -func (c *Client) GetState() gat.ClientState { - c.mu.Lock() - defer c.mu.Unlock() - return c.state -} - -func (c *Client) GetAddress() net.Addr { - return c.conn.RemoteAddr() -} - -func (c *Client) GetLocalAddress() net.Addr { - return c.conn.LocalAddr() -} - -func (c *Client) GetConnectTime() time.Time { - return c.connectTime -} - -func (c *Client) startRequest() { - c.state = gat.ClientWaiting - c.requestTime = time.Now() -} - -func (c *Client) GetRequestTime() time.Time { - return c.requestTime -} - -func (c *Client) GetRemotePid() int { - return int(c.pid) -} - -func (c *Client) GetConnectionPool() gat.Pool { - return c.server -} - -func (c *Client) SetRequestedShard(shard int) { - c.preferredShard = shard - c.hasPreferredShard = true -} - -func (c *Client) UnsetRequestedShard() { - c.hasPreferredShard = false -} - -func (c *Client) GetRequestedShard() (int, bool) { - return c.preferredShard, c.hasPreferredShard -} - -func (c *Client) SetShardingKey(key string) { - c.shardingKey = key -} - -func (c *Client) GetShardingKey() string { - return c.shardingKey -} - -func NewClient( - gatling gat.Gat, - conf *config.Global, - conn net.Conn, - admin_only bool, -) *Client { - pid, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) - skey, _ := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) - - c := &Client{ - conn: conn, - r: NewCountReader(bufio.NewReader(conn)), - wr: NewCountWriter(bufio.NewWriter(conn)), - recv: make(chan protocol.Packet), - state: gat.ClientActive, - pid: int32(pid.Int64()), - secretKey: int32(skey.Int64()), - gatling: gatling, - statements: make(map[string]parsedQuery), - portals: make(map[string]*protocol.Bind), - conf: conf, - parser: pg3p.NewParser(), - } - c.log = log.With(). - Stringer("clientaddr", c.conn.RemoteAddr()).Logger() - return c -} - -func (c *Client) GetId() gat.ClientID { - return gat.ClientID{ - PID: c.pid, - SecretKey: c.secretKey, - } -} - -func (c *Client) GetCurrentConn() gat.Connection { - c.mu.Lock() - defer c.mu.Unlock() - return c.currentConn -} - -func (c *Client) SetCurrentConn(conn gat.Connection) { - c.mu.Lock() - defer c.mu.Unlock() - c.state = gat.ClientActive - c.currentConn = conn -} - -func (c *Client) Accept(ctx context.Context) error { - var cancel context.CancelFunc - ctx, cancel = context.WithCancel(ctx) - defer cancel() - - // read a packet - startup := new(protocol.StartupMessage) - err := startup.Read(c.r) - if err != nil { - return err - } - switch startup.Fields.ProtocolVersionNumber { - case 196608: - case 80877102: - return c.handle_cancel(ctx, startup) - case 80877103: - // ssl stuff now - useSsl := (c.conf.General.TlsCertificate != "") - if !useSsl { - _, err = protocol.WriteByte(c.wr, 'N') - if err != nil { - return err - } - err = c.wr.Writer.Flush() - if err != nil { - return err - } - startup = new(protocol.StartupMessage) - err = startup.Read(c.r) - if err != nil { - return err - } - } else { - _, err = protocol.WriteByte(c.wr, 'S') - if err != nil { - return err - } - err = c.wr.Writer.Flush() - if err != nil { - return err - } - //TODO: we need to do an ssl handshake here. - var cert tls.Certificate - cert, err = tls.LoadX509KeyPair(c.conf.General.TlsCertificate, c.conf.General.TlsPrivateKey) - if err != nil { - return err - } - cfg := &tls.Config{ - Certificates: []tls.Certificate{cert}, - InsecureSkipVerify: true, - } - c.conn = tls.Server(c.conn, cfg) - c.r.Reader = bufio.NewReader(c.conn) - c.wr.Writer = bufio.NewWriter(c.conn) - err = startup.Read(c.r) - if err != nil { - return err - } - } - } - c.options = make([]protocol.FieldsStartupMessageParameters, 0, len(startup.Fields.Parameters)) - for _, v := range startup.Fields.Parameters { - switch v.Name { - case "": - case "database": - c.poolName = v.Value - case "user": - c.username = v.Value - default: - c.options = append(c.options, v) - } - } - - if c.poolName == "" { - return &pg_error.Error{ - Severity: pg_error.Fatal, - Code: pg_error.InvalidAuthorizationSpecification, - Message: "param database required", - } - } - - if c.username == "" { - return &pg_error.Error{ - Severity: pg_error.Fatal, - Code: pg_error.InvalidAuthorizationSpecification, - Message: "param user required", - } - } - - admin := (c.poolName == "pgcat" || c.poolName == "pgbouncer") - - if c.conf.General.AdminOnly && !admin { - c.log.Debug().Msg("rejected non admin, since admin only mode") - return &pg_error.Error{ - Severity: pg_error.Fatal, - Code: pg_error.InvalidAuthorizationSpecification, - Message: "rejected non admin", - } - } - - // TODO: Add SASL support. - - // Perform MD5 authentication. - pkt, salt, err := messages.CreateMd5Challenge() - if err != nil { - return err - } - err = c.Send(pkt) - if err != nil { - return err - } - err = c.Flush() - if err != nil { - return err - } - - var rsp protocol.Packet - rsp, err = protocol.ReadFrontend(c.r) - if err != nil { - return err - } - var passwordResponse []byte - switch r := rsp.(type) { - case *protocol.AuthenticationResponse: - passwordResponse = r.Fields.Data - default: - return &pg_error.Error{ - Severity: pg_error.Fatal, - Code: pg_error.InvalidAuthorizationSpecification, - Message: fmt.Sprintf("wanted AuthenticationResponse packet, got '%+v'", rsp), - } - } - - pool := c.gatling.GetDatabase(c.poolName) - if pool == nil { - return fmt.Errorf("pool '%s' not found", c.poolName) - } - - // get user - var user *config.User - user = pool.GetUser(c.username) - if user == nil { - return fmt.Errorf("user '%s' not found", c.username) - } - - // Authenticate admin user. - if admin { - pw_hash := messages.Md5HashPassword(c.conf.General.AdminUsername, c.conf.General.AdminPassword, salt[:]) - if !reflect.DeepEqual(pw_hash, passwordResponse) { - return &pg_error.Error{ - Severity: pg_error.Fatal, - Code: pg_error.InvalidPassword, - Message: "invalid password", - } - } - } else { - pw_hash := messages.Md5HashPassword(c.username, user.Password, salt[:]) - if !reflect.DeepEqual(pw_hash, passwordResponse) { - return &pg_error.Error{ - Severity: pg_error.Fatal, - Code: pg_error.InvalidPassword, - Message: "invalid password", - } - } - } - - c.server = pool.WithUser(c.username) - if c.server == nil { - return fmt.Errorf("no pool for '%s'", c.username) - } - defer c.server.OnDisconnect(c) - - authOk := new(protocol.Authentication) - authOk.Fields.Code = 0 - err = c.Send(authOk) - if err != nil { - return err - } - - // - info := c.server.GetServerInfo(c) - for _, inf := range info { - err = c.Send(inf) - if err != nil { - return err - } - } - backendKeyData := new(protocol.BackendKeyData) - backendKeyData.Fields.ProcessID = c.pid - backendKeyData.Fields.SecretKey = c.secretKey - err = c.Send(backendKeyData) - if err != nil { - return err - } - readyForQuery := new(protocol.ReadyForQuery) - readyForQuery.Fields.Status = byte('I') - err = c.Send(readyForQuery) - if err != nil { - return err - } - go c.recvLoop(cancel) - open := true - for open { - err = c.Flush() - if err != nil { - return err - } - open, err = c.tick(ctx) - // add send and recv to pool - metrics.RecordBytes(c.poolName, c.username, c.wr.BytesWritten.Swap(0), c.r.BytesRead.Swap(0)) - if !open { - break - } - if err != nil { - err = c.Send(pg_error.IntoPacket(err)) - if err != nil { - return err - } - } - rq := new(protocol.ReadyForQuery) - rq.Fields.Status = 'I' - err = c.Send(rq) - if err != nil { - return err - } - } - return nil -} - -func (c *Client) recvLoop(cancel context.CancelFunc) { - defer cancel() - for { - recv, err := protocol.ReadFrontend(c.r) - if err != nil { - if !errors.Is(err, io.EOF) && !errors.Is(err, net.ErrClosed) { - log.Err(err) - } - return - } - //log.Printf("got packet(%s) %+v", reflect.TypeOf(recv), recv) - switch pkt := recv.(type) { - case *protocol.Parse: - var role config.ServerRole - role, err = c.server.GetDatabase().GetRouter().InferRole(pkt.Fields.Query) - if err != nil { - err = c.Send(pg_error.IntoPacket(err)) - if err != nil { - return - } - continue - } - c.statements[pkt.Fields.PreparedStatement] = parsedQuery{ - request: pkt, - role: role, - } - err = c.Send(new(protocol.ParseComplete)) - if err != nil { - return - } - case *protocol.Bind: - c.portals[pkt.Fields.Destination] = pkt - err = c.Send(new(protocol.BindComplete)) - if err != nil { - return - } - case *protocol.Close: - switch pkt.Fields.Which { - case 'S': - delete(c.statements, pkt.Fields.Name) - case 'P': - delete(c.portals, pkt.Fields.Name) - } - err = c.Send(new(protocol.CloseComplete)) - if err != nil { - return - } - case *protocol.Terminate: - return - default: - c.recv <- recv - } - } -} - -func (c *Client) handle_cancel(ctx context.Context, p *protocol.StartupMessage) error { - cl := c.gatling.GetClient(gat.ClientID{ - PID: p.Fields.ProcessKey, - SecretKey: p.Fields.SecretKey, - }) - if cl == nil { - return errors.New("user not found") - } - conn := cl.GetCurrentConn() - if conn == nil { - return errors.New("not connected to a server") - } - return conn.Cancel() -} - -// reads a packet from stream and handles it -func (c *Client) tick(ctx context.Context) (bool, error) { - var rsp protocol.Packet - select { - case rsp = <-c.recv: - case <-ctx.Done(): - return false, ctx.Err() - } - switch cast := rsp.(type) { - case *protocol.Describe: - return true, c.handle_describe(ctx, cast) - case *protocol.Execute: - return true, c.handle_execute(ctx, cast) - case *protocol.Sync: - return true, nil - case *protocol.Query: - return true, c.handle_query(ctx, cast) - case *protocol.FunctionCall: - return true, c.handle_function(ctx, cast) - default: - log.Printf("unhandled packet %#v", rsp) - } - return true, nil -} - -func (c *Client) handle_describe(ctx context.Context, d *protocol.Describe) error { - //log.Println("describe") - c.startRequest() - return c.server.Describe(ctx, c, d) -} - -func (c *Client) handle_execute(ctx context.Context, e *protocol.Execute) error { - //log.Println("execute") - c.startRequest() - return c.server.Execute(ctx, c, e) -} - -func (c *Client) handle_query(ctx context.Context, q *protocol.Query) error { - tokens := c.parser.Lex(q.Fields.Query) - - // we can handle empty queries here - if len(tokens) == 0 { - return c.Send(&protocol.EmptyQueryResponse{}) - } - - var lastExec = tokens[0].Position - var nestDepth = 0 - for idx, cmd := range tokens { - if nestDepth == 0 { - switch cmd.Token { - case lex.KeywordStart, lex.KeywordBegin: - if nestDepth == 0 { - // push simple query - if lastExec != cmd.Position { - err := c.handle_simple_query(ctx, q.Fields.Query[lastExec:cmd.Position]) - if err != nil { - return err - } - lastExec = cmd.Position - } - } - nestDepth += 1 - } - } else { - switch cmd.Token { - case lex.KeywordStart, lex.KeywordBegin, lex.KeywordCase: - nestDepth += 1 - case lex.KeywordEnd: - nestDepth -= 1 - if nestDepth == 0 { - var end int - if idx+1 < len(tokens) { - end = tokens[idx+1].Position - } else { - end = len(q.Fields.Query) - } - // push txn - err := c.handle_transaction(ctx, q.Fields.Query[lastExec:end]) - if err != nil { - return err - } - lastExec = end - } - } - } - } - - if lastExec != len(q.Fields.Query) { - var err error - if nestDepth > 0 { - err = c.handle_transaction(ctx, q.Fields.Query[lastExec:]) - } else { - err = c.handle_simple_query(ctx, q.Fields.Query[lastExec:]) - } - if err != nil { - return err - } - } - - return nil -} - -func (c *Client) handle_simple_query(ctx context.Context, q string) error { - //log.Println("query:", q) - c.startRequest() - return c.server.SimpleQuery(ctx, c, q) -} - -func (c *Client) handle_transaction(ctx context.Context, q string) error { - //log.Println("transaction:", q) - c.startRequest() - return c.server.Transaction(ctx, c, q) -} - -func (c *Client) handle_function(ctx context.Context, f *protocol.FunctionCall) error { - c.startRequest() - return c.server.CallFunction(ctx, c, f) -} - -func (c *Client) GetPreparedStatement(name string) *protocol.Parse { - return c.statements[name].request -} - -func (c *Client) GetPortal(name string) *protocol.Bind { - return c.portals[name] -} - -func (c *Client) GetUnderlyingPreparedStatementRole(name string) config.ServerRole { - s, ok := c.statements[name] - if !ok { - return config.SERVERROLE_REPLICA - } - return s.role -} - -func (c *Client) GetUnderlyingPortalRole(name string) config.ServerRole { - p, ok := c.portals[name] - if !ok { - return config.SERVERROLE_REPLICA - } - return c.GetUnderlyingPreparedStatementRole(p.Fields.PreparedStatement) -} - -func (c *Client) GetUnderlyingRole(describe *protocol.Describe) config.ServerRole { - switch describe.Fields.Which { - case 'S': - return c.GetUnderlyingPreparedStatementRole(describe.Fields.Name) - case 'P': - return c.GetUnderlyingPortalRole(describe.Fields.Name) - default: - return "" - } -} - -func (c *Client) Send(pkt protocol.Packet) error { - c.mu.Lock() - defer c.mu.Unlock() - //log.Printf("sent packet(%s) %+v", reflect.TypeOf(pkt), pkt) - _, err := pkt.Write(c.wr) - return err -} - -func (c *Client) Flush() error { - c.mu.Lock() - defer c.mu.Unlock() - return c.wr.Writer.Flush() -} - -func (c *Client) Recv() <-chan protocol.Packet { - return c.recv -} - -var _ gat.Client = (*Client)(nil) diff --git a/lib/gat/gatling/client/client_test.go b/lib/gat/gatling/client/client_test.go deleted file mode 100644 index e535db4ec34a8005392924edc19f5083dc0d65cf..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/client/client_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package client - -// TODO: write client tests, original had none diff --git a/lib/gat/gatling/gatling.go b/lib/gat/gatling/gatling.go deleted file mode 100644 index 0a2f2107a07e1cf26435f251008f94a6f6d9fd0a..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/gatling.go +++ /dev/null @@ -1,196 +0,0 @@ -package gatling - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "sync" - - "gfx.cafe/util/go/generic" - - "gfx.cafe/gfx/pggat/lib/gat/admin" - "gfx.cafe/gfx/pggat/lib/gat/database" - "gfx.cafe/gfx/pggat/lib/gat/gatling/server" - "gfx.cafe/gfx/pggat/lib/metrics" - - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/gatling/client" - "gfx.cafe/gfx/pggat/lib/gat/protocol/pg_error" - - "git.tuxpa.in/a/zlog/log" - - "gfx.cafe/gfx/pggat/lib/config" -) - -// a Gatling is the main runtime process for the proxy -type Gatling struct { - // config and config mutex - c *config.Global - mu sync.RWMutex - // channel that new config are delivered - chConfig chan *config.Global - - pools map[string]gat.Database - clients generic.Map[gat.ClientID, gat.Client] -} - -func NewGatling(conf *config.Global) *Gatling { - g := &Gatling{ - chConfig: make(chan *config.Global, 1), - pools: make(map[string]gat.Database), - } - // add admin pool - adminPool := admin.New(g) - g.pools["pgbouncer"] = adminPool - g.pools["pggat"] = adminPool - - err := g.ensureConfig(conf) - if err != nil { - log.Println("failed to parse config", err) - } - go g.watchConfigs() - return g -} - -func (g *Gatling) watchConfigs() { - for { - c := <-g.chConfig - err := g.ensureConfig(c) - if err != nil { - log.Println("failed to parse config", err) - } - } -} - -func (g *Gatling) GetVersion() string { - return "PgGat Gatling 0.0.1" -} - -func (g *Gatling) GetConfig() *config.Global { - return g.c -} - -func (g *Gatling) GetDatabase(name string) gat.Database { - g.mu.RLock() - defer g.mu.RUnlock() - srv, ok := g.pools[name] - if !ok { - return nil - } - return srv -} - -func (g *Gatling) GetDatabases() map[string]gat.Database { - g.mu.RLock() - defer g.mu.RUnlock() - return g.pools -} - -func (g *Gatling) GetClient(id gat.ClientID) gat.Client { - c, ok := g.clients.Load(id) - if !ok { - return nil - } - return c -} - -func (g *Gatling) GetClients() (out []gat.Client) { - g.clients.Range(func(id gat.ClientID, client gat.Client) bool { - out = append(out, client) - return true - }) - return out -} - -func (g *Gatling) ensureConfig(c *config.Global) error { - g.mu.Lock() - defer g.mu.Unlock() - - g.c = c - - if err := g.ensureGeneral(c); err != nil { - return err - } - if err := g.ensureAdmin(c); err != nil { - return err - } - if err := g.ensurePools(c); err != nil { - return err - } - - return nil -} - -// TODO: all other settings -func (g *Gatling) ensureGeneral(c *config.Global) error { - return nil -} - -// TODO: should configure the admin things, metrics, etc -func (g *Gatling) ensureAdmin(c *config.Global) error { - return nil -} - -func (g *Gatling) ensurePools(c *config.Global) error { - for name, p := range c.Pools { - if existing, ok := g.pools[name]; ok { - existing.EnsureConfig(p) - } else { - g.pools[name] = database.New(server.Dial, name, p) - } - } - return nil -} - -func (g *Gatling) ListenAndServe(ctx context.Context) error { - ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", g.c.General.Host, g.c.General.Port)) - if err != nil { - return err - } - go func() { - for { - errch := make(chan error) - go func() { - c, err := ln.Accept() - if err != nil { - errch <- err - } - metrics.RecordAcceptConnectionStatus(err) - close(errch) - g.handleConnection(ctx, c) - }() - - err = <-errch - if err != nil { - log.Println("failed to accept connection:", err) - } - } - }() - return nil -} - -// TODO: TLS -func (g *Gatling) handleConnection(ctx context.Context, c net.Conn) { - cl := client.NewClient(g, g.c, c, false) - - g.clients.Store(cl.GetId(), cl) - metrics.RecordActiveConnections(1) - defer func() { - g.clients.Delete(cl.GetId()) - metrics.RecordActiveConnections(-1) - }() - - err := cl.Accept(ctx) - if err != nil { - if !errors.Is(err, io.EOF) { - log.Println("err in connection:", err.Error()) - _ = cl.Send(pg_error.IntoPacket(err)) - _ = cl.Flush() - } - } - _ = c.Close() -} - -var _ gat.Gat = (*Gatling)(nil) diff --git a/lib/gat/gatling/messages/messages.go b/lib/gat/gatling/messages/messages.go deleted file mode 100644 index 836c72b8fd2d15f5312033b49a91be024f4d7001..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/messages/messages.go +++ /dev/null @@ -1,317 +0,0 @@ -package messages - -import ( - "crypto/md5" - "crypto/rand" - "encoding/hex" - - "gfx.cafe/gfx/pggat/lib/gat/protocol" -) - -// TODO: decide which of these we need and don't need. -// impelement the ones we need - -// / Generate md5 password challenge. -func CreateMd5Challenge() (*protocol.Authentication, [4]byte, error) { - salt := [4]byte{} - _, err := rand.Read(salt[:]) - if err != nil { - return nil, salt, err - } - - pkt := new(protocol.Authentication) - pkt.Fields.Code = 5 - pkt.Fields.Salt = salt[:] - - return pkt, salt, nil -} - -// Create md5 password hash given a salt. -func Md5HashPassword(user string, password string, salt []byte) []byte { - hsh1, hsh2 := md5.New(), md5.New() - hsh1.Write([]byte(password)) - hsh1.Write([]byte(user)) - hsh2.Write( - []byte(hex.EncodeToString(hsh1.Sum(nil))), - ) - hsh2.Write(salt) - sum := hsh2.Sum(nil) - return append([]byte("md5"+hex.EncodeToString(sum)), 0) -} - -// /// Implements a response to our custom `SET SHARDING KEY` -// /// and `SET SERVER ROLE` commands. -// /// This tells the client we're ready for the next query. -// func custom_protocol_response_ok(w io.Writer, message: &str) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// res = BytesMut::with_capacity(25) -// -// let set_complete = BytesMut::from(&format!("{}\0", message)[..]) -// let len = (set_complete.len() + 4) as i32 -// -// // CommandComplete -// res.put_u8(b'C') -// res.put_i32(len) -// res.put_slice(&set_complete[..]) -// -// write_all_half(stream, res).await? -// ready_for_query(stream).await -// } -// -// /// Send a custom error message to the client. -// /// Tell the client we are ready for the next query and no rollback is necessary. -// /// Docs on error codes: <https://www.postgresql.org/docs/12/errcodes-appendix.html>. -// func error_response(w io.Writer, message: &str) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// error_response_terminal(stream, message).await? -// ready_for_query(stream).await -// } -// -// /// Send a custom error message to the client. -// /// Tell the client we are ready for the next query and no rollback is necessary. -// /// Docs on error codes: <https://www.postgresql.org/docs/12/errcodes-appendix.html>. -// func error_response_terminal(w io.Writer, message: &str) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// error = BytesMut::new() -// -// // error level -// error.put_u8(b'S') -// error.put_slice(&b"FATAL\0"[..]) -// -// // error level (non-translatable) -// error.put_u8(b'V') -// error.put_slice(&b"FATAL\0"[..]) -// -// // error code: not sure how much this matters. -// error.put_u8(b'C') -// error.put_slice(&b"58000\0"[..]) // system_error, see Appendix A. -// -// // The short error message. -// error.put_u8(b'M') -// error.put_slice(&format!("{}\0", message).as_bytes()) -// -// // No more fields follow. -// error.put_u8(0) -// -// // Compose the two message reply. -// res = BytesMut::with_capacity(error.len() + 5) -// -// res.put_u8(b'E') -// res.put_i32(error.len() as i32 + 4) -// res.put(error) -// -// Ok(write_all_half(stream, res).await?) -// } -// -// func wrong_password(w io.Writer, user: &str) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// error = BytesMut::new() -// -// // error level -// error.put_u8(b'S') -// error.put_slice(&b"FATAL\0"[..]) -// -// // error level (non-translatable) -// error.put_u8(b'V') -// error.put_slice(&b"FATAL\0"[..]) -// -// // error code: not sure how much this matters. -// error.put_u8(b'C') -// error.put_slice(&b"28P01\0"[..]) // system_error, see Appendix A. -// -// // The short error message. -// error.put_u8(b'M') -// error.put_slice(&format!("password authentication failed for user \"{}\"\0", user).as_bytes()) -// -// // No more fields follow. -// error.put_u8(0) -// -// // Compose the two message reply. -// res = BytesMut::new() -// -// res.put_u8(b'E') -// res.put_i32(error.len() as i32 + 4) -// -// res.put(error) -// -// write_all(stream, res).await -// } -// -// /// Respond to a SHOW SHARD command. -// func show_response(w io.Writer, name: &str, value: &str) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// // A SELECT response consists of: -// // 1. RowDescription -// // 2. One or more DataRow -// // 3. CommandComplete -// // 4. ReadyForQuery -// -// // The final messages sent to the client -// res = BytesMut::new() -// -// // RowDescription -// res.put(row_description(&vec![(name, DataType::Text)])) -// -// // DataRow -// res.put(data_row(&vec![value.to_string()])) -// -// // CommandComplete -// res.put(command_complete("SELECT 1")) -// -// write_all_half(stream, res).await? -// ready_for_query(stream).await -// } -// -// pub fn row_description(columns: &Vec<(&str, DataType)>) BytesMut { -// res = BytesMut::new() -// row_desc = BytesMut::new() -// -// // how many colums we are storing -// row_desc.put_i16(columns.len() as i16) -// -// for (name, data_type) in columns { -// // Column name -// row_desc.put_slice(&format!("{}\0", name).as_bytes()) -// -// // Doesn't belong to any table -// row_desc.put_i32(0) -// -// // Doesn't belong to any table -// row_desc.put_i16(0) -// -// // Text -// row_desc.put_i32(data_type.into()) -// -// // Text size = variable (-1) -// let type_size = match data_type { -// DataType::Text => -1, -// DataType::Int4 => 4, -// DataType::Numeric => -1, -// } -// -// row_desc.put_i16(type_size) -// -// // Type modifier: none that I know -// row_desc.put_i32(-1) -// -// // Format being used: text (0), binary (1) -// row_desc.put_i16(0) -// } -// -// res.put_u8(b'T') -// res.put_i32(row_desc.len() as i32 + 4) -// res.put(row_desc) -// -// res -// } -// -// /// Create a DataRow message. -// -// pub fn data_row(row: &Vec<String>) BytesMut { -// res = BytesMut::new() -// data_row = BytesMut::new() -// -// data_row.put_i16(row.len() as i16) -// -// for column in row { -// let column = column.as_bytes() -// data_row.put_i32(column.len() as i32) -// data_row.put_slice(&column) -// } -// -// res.put_u8(b'D') -// res.put_i32(data_row.len() as i32 + 4) -// res.put(data_row) -// -// res -// } -// -// /// Create a CommandComplete message. -// -// pub fn command_complete(command: &str) BytesMut { -// let cmd = BytesMut::from(format!("{}\0", command).as_bytes()) -// res = BytesMut::new() -// res.put_u8(b'C') -// res.put_i32(cmd.len() as i32 + 4) -// res.put(cmd) -// res -// } -// -// /// Write all data in the buffer to the TcpStream. -// func write_all(w io.Writer, buf: BytesMut) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// match stream.write_all(&buf).await { -// Ok(_) => Ok(()), -// Err(_) => return Err(error::Socketerror), -// } -// } -// -// /// Write all the data in the buffer to the TcpStream, write owned half (see mpsc). -// func write_all_half(w io.Writer, buf: BytesMut) error -// where -// -// S: tokio::io::AsyncWrite + std::marker::Unpin, -// -// { -// match stream.write_all(&buf).await { -// Ok(_) => Ok(()), -// Err(_) => return Err(error::Socketerror), -// } -// } -// -// /// Read a complete message from the socket. -// func read_message(w io.Writer) Result<BytesMut, error -// where -// -// S: tokio::io::AsyncRead + std::marker::Unpin, -// -// { -// let code = match stream.read_u8().await { -// Ok(code) => code, -// Err(_) => return Err(error::Socketerror), -// } -// -// let len = match stream.read_i32().await { -// Ok(len) => len, -// Err(_) => return Err(error::Socketerror), -// } -// -// buf = vec![0u8 len as usize - 4] -// -// match stream.read_exact(&mut buf).await { -// Ok(_) => (), -// Err(_) => return Err(error::Socketerror), -// } -// -// bytes = BytesMut::with_capacity(len as usize + 1) -// -// bytes.put_u8(code) -// bytes.put_i32(len) -// bytes.put_slice(&buf) -// -// Ok(bytes) -// } diff --git a/lib/gat/gatling/messages/messages_test.go b/lib/gat/gatling/messages/messages_test.go deleted file mode 100644 index 297aa0343ecbae0e1ac156e01a5876e2e0c71ebc..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/messages/messages_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package messages - -// TODO: once we decide on what messages, write the relevant test diff --git a/lib/gat/gatling/server/server.go b/lib/gat/gatling/server/server.go deleted file mode 100644 index 5906e5fac7425f301540c6c20456923e3a82c8c4..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/server/server.go +++ /dev/null @@ -1,808 +0,0 @@ -package server - -import ( - "bufio" - "errors" - "fmt" - "io" - "net" - "reflect" - "sync" - "time" - - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/protocol/pg_error" - - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "gfx.cafe/gfx/pggat/lib/util/slices" - "gfx.cafe/util/go/bufpool" - - "gfx.cafe/gfx/pggat/lib/auth/sasl" - "gfx.cafe/gfx/pggat/lib/auth/scram" - "gfx.cafe/gfx/pggat/lib/config" - "git.tuxpa.in/a/zlog" - "git.tuxpa.in/a/zlog/log" - "golang.org/x/net/context" -) - -type Server struct { - addr string - port uint16 - conn net.Conn - r *bufio.Reader - wr *bufio.Writer - recv <-chan protocol.Packet - - client gat.Client - state gat.ConnectionState - - options []protocol.FieldsStartupMessageParameters - - serverInfo []*protocol.ParameterStatus - - processId int32 - secretKey int32 - - connectedAt time.Time - lastActivity time.Time - - boundPreparedStatments map[string]*protocol.Parse - boundPortals map[string]*protocol.Bind - - // constants - db string - dbuser string - dbpass string - user config.User - - awaitingSync bool - readyForQuery bool - copying bool - - log zlog.Logger - - closed chan struct{} - mu sync.Mutex -} - -func Dial(ctx context.Context, options []protocol.FieldsStartupMessageParameters, user *config.User, shard *config.Shard, server *config.Server) (gat.Connection, error) { - s := &Server{ - addr: server.Host, - port: server.Port, - - state: gat.ConnectionNew, - - options: options, - - boundPreparedStatments: make(map[string]*protocol.Parse), - boundPortals: make(map[string]*protocol.Bind), - - dbuser: server.Username, - dbpass: server.Password, - - closed: make(chan struct{}), - } - var err error - s.conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", server.Host, server.Port)) - if err != nil { - return nil, err - } - s.r = bufio.NewReader(s.conn) - s.wr = bufio.NewWriter(s.conn) - recv := make(chan protocol.Packet, 1024) - s.recv = recv - go func() { - for { - p, err := protocol.ReadBackend(s.r) - if err != nil { - _ = s.Close() - break - } - recv <- p - } - }() - s.user = *user - s.db = shard.Database - - s.log = log.With(). - Stringer("addr", s.conn.RemoteAddr()). - Str("user", user.Name). - Str("db", shard.Database). - Logger() - return s, s.connect(ctx) -} - -func (s *Server) Cancel() error { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", s.addr, s.port)) - if err != nil { - return err - } - cancel := new(protocol.StartupMessage) - cancel.Fields.ProtocolVersionNumber = 80877102 - cancel.Fields.ProcessKey = s.processId - cancel.Fields.SecretKey = s.secretKey - _, err = cancel.Write(conn) - _ = conn.Close() - return err -} - -func (s *Server) GetDatabase() string { - return s.db -} - -func (s *Server) GetState() gat.ConnectionState { - s.mu.Lock() - defer s.mu.Unlock() - return s.state -} - -func (s *Server) GetHost() string { - return s.addr -} - -func (s *Server) GetPort() int { - return int(s.port) -} - -func (s *Server) GetAddress() net.Addr { - s.mu.Lock() - defer s.mu.Unlock() - return s.conn.RemoteAddr() -} - -func (s *Server) GetLocalAddress() net.Addr { - s.mu.Lock() - defer s.mu.Unlock() - return s.conn.LocalAddr() -} - -func (s *Server) GetConnectTime() time.Time { - s.mu.Lock() - defer s.mu.Unlock() - return s.connectedAt -} - -func (s *Server) GetRequestTime() time.Time { - s.mu.Lock() - defer s.mu.Unlock() - return s.lastActivity -} - -func (s *Server) failHealthCheck(err error) { - log.Println("Server failed a health check!!!!", err) - _ = s.Close() -} - -func (s *Server) healthCheck() { - if !s.readyForQuery { - s.failHealthCheck(errors.New("expected server to be ready for query")) - } -} - -func (s *Server) IsCloseNeeded() bool { - select { - case <-s.closed: - return true - default: - return !s.readyForQuery - } -} - -func (s *Server) GetClient() gat.Client { - s.mu.Lock() - defer s.mu.Unlock() - return s.client -} - -func (s *Server) SetClient(client gat.Client) { - s.mu.Lock() - defer s.mu.Unlock() - s.lastActivity = time.Now() - if client != nil { - s.state = gat.ConnectionActive - } else { - // client no longer needs this connection, perform a health check - s.healthCheck() - s.state = gat.ConnectionIdle - } - s.client = client -} - -func (s *Server) GetRemotePid() int { - s.mu.Lock() - defer s.mu.Unlock() - return int(s.processId) -} - -func (s *Server) GetTLS() string { - return "" // TODO -} - -func (s *Server) GetServerInfo() []*protocol.ParameterStatus { - s.mu.Lock() - defer s.mu.Unlock() - return s.serverInfo -} - -func (s *Server) startup(ctx context.Context) error { - s.log.Debug().Msg("sending startup") - start := new(protocol.StartupMessage) - start.Fields.ProtocolVersionNumber = 196608 - start.Fields.Parameters = append( - s.options, - protocol.FieldsStartupMessageParameters{ - Name: "user", - Value: s.dbuser, - }, - protocol.FieldsStartupMessageParameters{ - Name: "database", - Value: s.db, - }, - protocol.FieldsStartupMessageParameters{}, - ) - err := s.writePacket(start) - if err != nil { - return err - } - return s.flush() -} - -func (s *Server) connect(ctx context.Context) error { - err := s.startup(ctx) - if err != nil { - return err - } - s.log.Debug().Msg("beginning connection") - var scrm sasl.Mechanism - var sm sasl.StateMachine - for { - var pkt protocol.Packet - pkt, err = s.readPacket(ctx) - if err != nil { - return err - } - switch p := pkt.(type) { - case *protocol.Authentication: - switch p.Fields.Code { - case 5: //MD5_ENCRYPTED_PASSWORD - case 0: // AUTH SUCCESS - case 10: // SASL - if slices.Contains(p.Fields.SASLMechanism, scram.SHA256.Name()) { - s.log.Debug().Str("method", "scram256").Msg("valid protocol") - } else { - return fmt.Errorf("unsupported scram version: %s", p.Fields.SASLMechanism) - } - scrm, err = scram.Mechanism(scram.SHA256, s.dbuser, s.dbpass) - if err != nil { - return err - } - var bts []byte - sm, bts, err = scrm.Start(ctx) - if err != nil { - return err - } - - rsp := new(protocol.AuthenticationResponse) - buf := bufpool.Get(len(scrm.Name()) + 1 + 4 + len(bts)) - buf.Reset() - _, _ = protocol.WriteString(buf, scrm.Name()) - _, _ = protocol.WriteInt32(buf, int32(len(bts))) - buf.Write(bts) - rsp.Fields.Data = buf.Bytes() - err = s.writePacket(rsp) - bufpool.Put(buf) - if err != nil { - return err - } - err = s.flush() - if err != nil { - return err - } - case 11: // SASL_CONTINUE - s.log.Debug().Str("method", "scram256").Msg("sasl continue") - var bts []byte - _, bts, err = sm.Next(ctx, p.Fields.SASLChallenge) - - rsp := new(protocol.AuthenticationResponse) - rsp.Fields.Data = bts - err = s.writePacket(rsp) - if err != nil { - return err - } - err = s.flush() - if err != nil { - return err - } - case 12: // SASL_FINAL - s.log.Debug().Str("method", "scram256").Msg("sasl final") - var done bool - done, _, err = sm.Next(ctx, p.Fields.SASLAdditionalData) - if err != nil { - return err - } - if !done { - return fmt.Errorf("sasl authentication failed") - } - - s.log.Debug().Str("method", "scram256").Msg("sasl success") - } - case *protocol.ErrorResponse: - pgErr := new(pg_error.Error) - pgErr.Read(p) - return pgErr - case *protocol.ParameterStatus: - s.serverInfo = append(s.serverInfo, p) - case *protocol.BackendKeyData: - s.processId = p.Fields.ProcessID - s.secretKey = p.Fields.SecretKey - case *protocol.ReadyForQuery: - s.lastActivity = time.Now() - s.connectedAt = time.Now().UTC() - s.state = "idle" - s.readyForQuery = true - return nil - } - } -} - -func (s *Server) writePacket(pkt protocol.Packet) error { - //log.Printf("out %#v", pkt) - select { - case <-s.closed: - return io.ErrClosedPipe - default: - _, err := pkt.Write(s.wr) - if err != nil { - _ = s.Close() - } - return err - } -} - -func (s *Server) flush() error { - select { - case <-s.closed: - return io.ErrClosedPipe - default: - err := s.wr.Flush() - if err != nil { - _ = s.Close() - } - return err - } -} - -func (s *Server) readPacket(ctx context.Context) (protocol.Packet, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-s.closed: - return nil, net.ErrClosed - case pk := <-s.recv: - return pk, nil - } -} - -func (s *Server) stabilize() error { - if s.readyForQuery { - return nil - } - - select { - case <-s.closed: - // it's closed, there's nothing we can do - return net.ErrClosed - default: - // try to stabilize connection - } - - //log.Println("connection is unstable, attempting to restabilize it") - if s.copying { - //log.Println("failing copy") - s.copying = false - err := s.writePacket(new(protocol.CopyFail)) - if err != nil { - return err - } - } - if s.awaitingSync { - //log.Println("syncing") - s.awaitingSync = false - err := s.writePacket(new(protocol.Sync)) - if err != nil { - return err - } - } - err := s.flush() - if err != nil { - return err - } - - err = s.Cancel() - if err != nil { - return err - } - - for { - var pkt protocol.Packet - pkt, err = s.readPacket(context.Background()) - if err != nil { - return err - } - - //log.Printf("received %+v", pkt) - - switch pk := pkt.(type) { - case *protocol.ReadyForQuery: - if pk.Fields.Status == 'I' { - s.readyForQuery = true - return nil - } else { - query := new(protocol.Query) - query.Fields.Query = "end" - err = s.writePacket(query) - if err != nil { - return err - } - err = s.flush() - if err != nil { - return err - } - } - case *protocol.CopyInResponse, *protocol.CopyBothResponse: - fail := new(protocol.CopyFail) - err = s.writePacket(fail) - if err != nil { - return err - } - err = s.flush() - if err != nil { - return err - } - } - } -} - -func (s *Server) ensurePreparedStatement(ctx context.Context, client gat.Client, name string) error { - s.awaitingSync = true - // send prepared statement - stmt := client.GetPreparedStatement(name) - if stmt == nil { - return &pg_error.Error{ - Severity: pg_error.Err, - Code: pg_error.ProtocolViolation, - Message: fmt.Sprintf("prepared statement '%s' does not exist", name), - } - } - - if name != "" { - // test if prepared statement is the same - if prev, ok := s.boundPreparedStatments[name]; ok { - if reflect.DeepEqual(prev, stmt) { - // we don't need to bind, we're good - return nil - } - - // there is a statement bound that needs to be unbound - s.destructPreparedStatement(ctx, name) - } - } - - s.boundPreparedStatments[name] = stmt - - // send prepared statement to server - return s.writePacket(stmt) -} - -func (s *Server) ensurePortal(ctx context.Context, client gat.Client, name string) error { - s.awaitingSync = true - portal := client.GetPortal(name) - if portal == nil { - return &pg_error.Error{ - Severity: pg_error.Err, - Code: pg_error.ProtocolViolation, - Message: fmt.Sprintf("portal '%s' does not exist", name), - } - } - - err := s.ensurePreparedStatement(ctx, client, portal.Fields.PreparedStatement) - if err != nil { - return err - } - - if name != "" { - if prev, ok := s.boundPortals[name]; ok { - if reflect.DeepEqual(prev, portal) { - return nil - } - } - } - - s.boundPortals[name] = portal - return s.writePacket(portal) -} - -func (s *Server) destructPreparedStatement(ctx context.Context, name string) { - if name == "" { - return - } - delete(s.boundPreparedStatments, name) - closeRequest := new(protocol.Close) - closeRequest.Fields.Which = 'S' - closeRequest.Fields.Name = name - _ = s.writePacket(closeRequest) - _ = s.flush() - // await server ready - for { - r, _ := s.readPacket(ctx) - if _, ok := r.(*protocol.ReadyForQuery); ok { - return - } - } -} - -func (s *Server) destructPortal(ctx context.Context, name string) { - portal, ok := s.boundPortals[name] - if !ok { - return - } - delete(s.boundPortals, name) - s.destructPreparedStatement(ctx, portal.Fields.PreparedStatement) -} - -func (s *Server) handleRecv(ctx context.Context, client gat.Client, packet protocol.Packet) error { - switch pkt := packet.(type) { - case *protocol.FunctionCall, *protocol.Query: - err := s.writePacket(packet) - if err != nil { - return err - } - err = s.flush() - if err != nil { - return err - } - case *protocol.Describe: - s.awaitingSync = true - switch pkt.Fields.Which { - case 'S': // prepared statement - err := s.ensurePreparedStatement(ctx, client, pkt.Fields.Name) - if err != nil { - return err - } - case 'P': // portal - err := s.ensurePortal(ctx, client, pkt.Fields.Name) - if err != nil { - return err - } - default: - return &pg_error.Error{ - Severity: pg_error.Err, - Code: pg_error.ProtocolViolation, - Message: fmt.Sprintf("expected 'S' or 'P' for describe target, got '%c'", pkt.Fields.Which), - } - } - - // now we actually execute the thing the client wants - err := s.writePacket(packet) - if err != nil { - return err - } - case *protocol.Execute: - s.awaitingSync = true - err := s.ensurePortal(ctx, client, pkt.Fields.Name) - if err != nil { - return err - } - - err = s.writePacket(pkt) - if err != nil { - return err - } - case *protocol.Sync: - s.awaitingSync = false - err := s.writePacket(packet) - if err != nil { - return err - } - err = s.flush() - if err != nil { - return err - } - default: - return fmt.Errorf("don't know how to handle %T", packet) - } - return nil -} - -func (s *Server) sendAndLink(ctx context.Context, client gat.Client, initial protocol.Packet) error { - s.readyForQuery = false - err := s.handleRecv(ctx, client, initial) - if err != nil { - return err - } - err = s.awaitSync(ctx, client) - if err != nil { - return err - } - return s.link(ctx, client) -} - -func (s *Server) link(ctx context.Context, client gat.Client) error { - defer func() { - err := s.stabilize() - if err != nil { - log.Println("failed to stabilize connection:", err) - } - }() - for { - pkt, err := s.readPacket(ctx) - if err != nil { - return err - } - - switch p := pkt.(type) { - case *protocol.BindComplete, *protocol.ParseComplete: - // ignore, it is because we bound stuff - case *protocol.ReadyForQuery: - if p.Fields.Status == 'I' { - // this client is done - s.awaitingSync = false - s.copying = false - s.readyForQuery = true - return nil - } - - err = client.Send(p) - if err != nil { - return err - } - err = client.Flush() - if err != nil { - return err - } - - err = s.handleClientPacket(ctx, client) - if err != nil { - return err - } - err = s.awaitSync(ctx, client) - if err != nil { - return err - } - case *protocol.CopyInResponse, *protocol.CopyBothResponse: - err = client.Send(p) - if err != nil { - return err - } - err = client.Flush() - if err != nil { - return err - } - err = s.CopyIn(ctx, client) - if err != nil { - return err - } - default: - err = client.Send(p) - if err != nil { - return err - } - } - } -} - -func (s *Server) handleClientPacket(ctx context.Context, client gat.Client) error { - select { - case pkt := <-client.Recv(): - return s.handleRecv(ctx, client, pkt) - case <-ctx.Done(): - return ctx.Err() - } -} - -func (s *Server) awaitSync(ctx context.Context, client gat.Client) error { - for s.awaitingSync { - err := s.handleClientPacket(ctx, client) - if err != nil { - return err - } - } - return nil -} - -func (s *Server) Describe(ctx context.Context, client gat.Client, d *protocol.Describe) error { - return s.sendAndLink(ctx, client, d) -} - -func (s *Server) Execute(ctx context.Context, client gat.Client, e *protocol.Execute) error { - return s.sendAndLink(ctx, client, e) -} - -func (s *Server) SimpleQuery(ctx context.Context, client gat.Client, query string) error { - // send to server - q := new(protocol.Query) - q.Fields.Query = query - return s.sendAndLink(ctx, client, q) -} - -func (s *Server) Transaction(ctx context.Context, client gat.Client, query string) error { - q := new(protocol.Query) - q.Fields.Query = query - return s.sendAndLink(ctx, client, q) -} - -func (s *Server) CopyIn(ctx context.Context, client gat.Client) error { - s.copying = true - err := client.Flush() - if err != nil { - return err - } - for { - var pkt protocol.Packet - // receive a packet, or done if the ctx gets canceled - select { - case pkt = <-client.Recv(): - case <-ctx.Done(): - return ctx.Err() - } - err = s.writePacket(pkt) - if err != nil { - return err - } - - switch pkt.(type) { - case *protocol.CopyDone, *protocol.CopyFail: - s.copying = false - // don't error on copyfail because the client is the one that errored, it already knows - return s.flush() - } - } -} - -func (s *Server) CallFunction(ctx context.Context, client gat.Client, payload *protocol.FunctionCall) error { - return s.sendAndLink(ctx, client, payload) -} - -func (s *Server) Close() error { - select { - case <-s.closed: - return io.ErrClosedPipe - default: - s.readyForQuery = false - close(s.closed) - _ = s.writePacket(&protocol.Terminate{}) - return s.conn.Close() - } -} - -var _ gat.Connection = (*Server)(nil) - -//impl Drop for Server { -// /// Try to do a clean shut down. Best effort because -// /// the socket is in non-blocking mode, so it may not be ready -// /// for a write. -// fn drop(&mut self) { -// self.stats -// .server_disconnecting(self.processId(), self.address.id); -// -// let mut bytes = BytesMut::with_capacity(4); -// bytes.put_u8(b'X'); -// bytes.put_i32(4); -// -// match self.write.try_write(&bytes) { -// Ok(_) => (), -// Err(_) => debug!("Dirty shutdown"), -// }; -// -// // Should not matter. -// self.bad = true; -// -// let now = chrono::offset::Utc::now().naive_utc(); -// let duration = now - self.connectedAt; -// -// info!( -// "Server connection closed, session duration: {}", -// crate::format_duration(&duration) -// ); -// } -//} diff --git a/lib/gat/gatling/server/server_test.go b/lib/gat/gatling/server/server_test.go deleted file mode 100644 index ab0463f48625e080bb6cd740410a39fb6d3de638..0000000000000000000000000000000000000000 --- a/lib/gat/gatling/server/server_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package server - -import ( - "context" - "git.tuxpa.in/a/zlog/log" - "testing" - - "gfx.cafe/gfx/pggat/lib/config" -) - -var test_server = config.Server{ - Host: "localhost", - Port: 5432, -} - -var test_shard = config.Shard{} - -var test_user = config.User{ - Name: "postgres", - Password: "password", - PoolSize: 4, - StatementTimeout: 250, -} - -func TestServerDial(t *testing.T) { - srv, err := Dial(context.TODO(), nil, &test_user, &test_shard, &test_server) - if err != nil { - t.Error(err) - } - log.Println(srv) -} diff --git a/lib/gat/interfaces.go b/lib/gat/interfaces.go deleted file mode 100644 index 7f998c680a50890a5c1343a089ebbf7893efca29..0000000000000000000000000000000000000000 --- a/lib/gat/interfaces.go +++ /dev/null @@ -1,145 +0,0 @@ -package gat - -import ( - "context" - "net" - "time" - - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat/protocol" -) - -type ClientID struct { - PID int32 - SecretKey int32 -} - -type ClientState string - -const ( - ClientActive ClientState = "active" - ClientWaiting = "waiting" -) - -type Client interface { - GetId() ClientID - - GetOptions() []protocol.FieldsStartupMessageParameters - - GetPreparedStatement(name string) *protocol.Parse - GetPortal(name string) *protocol.Bind - GetUnderlyingPreparedStatementRole(name string) config.ServerRole - GetUnderlyingPortalRole(name string) config.ServerRole - GetUnderlyingRole(describe *protocol.Describe) config.ServerRole - GetCurrentConn() Connection - SetCurrentConn(conn Connection) - - GetConnectionPool() Pool - - GetState() ClientState - GetAddress() net.Addr - GetLocalAddress() net.Addr - GetConnectTime() time.Time - GetRequestTime() time.Time - GetRemotePid() int - - // sharding - SetRequestedShard(shard int) - UnsetRequestedShard() - GetRequestedShard() (int, bool) - SetShardingKey(key string) - GetShardingKey() string - - Send(pkt protocol.Packet) error - Flush() error - Recv() <-chan protocol.Packet -} - -type Gat interface { - GetVersion() string - GetConfig() *config.Global - GetDatabase(name string) Database - GetDatabases() map[string]Database - GetClient(id ClientID) Client - GetClients() []Client -} - -type Database interface { - GetUser(name string) *config.User - GetRouter() QueryRouter - GetName() string - - WithUser(name string) Pool - GetPools() []Pool - - EnsureConfig(c *config.Pool) -} - -type QueryRouter interface { - InferRole(query string) (config.ServerRole, error) - // TryHandle the client's query string. If we handled it, return true - TryHandle(client Client, query string) (bool, error) -} - -type Pool interface { - GetUser() *config.User - GetServerInfo(client Client) []*protocol.ParameterStatus - - GetDatabase() Database - - EnsureConfig(p *config.Pool, u *config.User) - - OnDisconnect(client Client) - - // extended queries - Describe(ctx context.Context, client Client, describe *protocol.Describe) error - Execute(ctx context.Context, client Client, execute *protocol.Execute) error - - // simple queries - SimpleQuery(ctx context.Context, client Client, query string) error - Transaction(ctx context.Context, client Client, query string) error - CallFunction(ctx context.Context, client Client, payload *protocol.FunctionCall) error -} - -type ConnectionState string - -const ( - ConnectionActive ConnectionState = "active" - ConnectionIdle = "idle" - ConnectionUsed = "used" - ConnectionTested = "tested" - ConnectionNew = "new" -) - -type Dialer = func(context.Context, []protocol.FieldsStartupMessageParameters, *config.User, *config.Shard, *config.Server) (Connection, error) - -type Connection interface { - GetServerInfo() []*protocol.ParameterStatus - - GetDatabase() string - GetState() ConnectionState - GetHost() string - GetPort() int - GetAddress() net.Addr - GetLocalAddress() net.Addr - GetConnectTime() time.Time - GetRequestTime() time.Time - GetClient() Client - SetClient(client Client) - GetRemotePid() int - GetTLS() string - - // IsCloseNeeded returns whether this connection failed a health check - IsCloseNeeded() bool - Close() error - - // actions - Describe(ctx context.Context, client Client, payload *protocol.Describe) error - Execute(ctx context.Context, client Client, payload *protocol.Execute) error - CallFunction(ctx context.Context, client Client, payload *protocol.FunctionCall) error - SimpleQuery(ctx context.Context, client Client, payload string) error - Transaction(ctx context.Context, client Client, payload string) error - - // Cancel the current running query - Cancel() error -} diff --git a/lib/gat/pool/session/pool.go b/lib/gat/pool/session/pool.go deleted file mode 100644 index 5d401d8aa5c9a895015eb1367bfe773ee1636618..0000000000000000000000000000000000000000 --- a/lib/gat/pool/session/pool.go +++ /dev/null @@ -1,147 +0,0 @@ -package session - -import ( - "context" - "runtime" - "sync/atomic" - - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "gfx.cafe/util/go/generic" -) - -type Pool struct { - c atomic.Pointer[config.Pool] - user atomic.Pointer[config.User] - database gat.Database - - dialer gat.Dialer - - assigned generic.Map[gat.ClientID, gat.Connection] - - servers chan gat.Connection -} - -func New(database gat.Database, dialer gat.Dialer, conf *config.Pool, user *config.User) *Pool { - p := &Pool{ - database: database, - - dialer: dialer, - - servers: make(chan gat.Connection, 1+runtime.NumCPU()*4), - } - p.EnsureConfig(conf, user) - return p -} - -func (p *Pool) getConnection(client gat.Client) (gat.Connection, error) { - for { - select { - case c := <-p.servers: - if c.IsCloseNeeded() { - _ = c.Close() - continue - } - return c, nil - default: - shard := p.c.Load().Shards[0] - return p.dialer(context.TODO(), client.GetOptions(), p.user.Load(), shard, shard.Servers[0]) - } - } -} - -func (p *Pool) returnConnection(c gat.Connection) { - p.servers <- c -} - -func (p *Pool) getOrAssign(client gat.Client) (gat.Connection, error) { - cid := client.GetId() - c, ok := p.assigned.Load(cid) - if !ok { - get, err := p.getConnection(client) - if err != nil { - return nil, err - } - p.assigned.Store(cid, get) - return get, err - } - return c, nil -} - -func (p *Pool) GetDatabase() gat.Database { - return p.database -} - -func (p *Pool) EnsureConfig(c *config.Pool, u *config.User) { - p.c.Store(c) - p.user.Store(u) -} - -func (p *Pool) OnDisconnect(client gat.Client) { - cid := client.GetId() - c, ok := p.assigned.LoadAndDelete(cid) - if !ok { - return - } - if c.IsCloseNeeded() { - _ = c.Close() - return - } - p.servers <- c -} - -func (p *Pool) GetUser() *config.User { - return p.user.Load() -} - -func (p *Pool) GetServerInfo(client gat.Client) []*protocol.ParameterStatus { - c, err := p.getConnection(client) - if err != nil { - return nil - } - defer p.returnConnection(c) - return c.GetServerInfo() -} - -func (p *Pool) Describe(ctx context.Context, client gat.Client, describe *protocol.Describe) error { - c, err := p.getOrAssign(client) - if err != nil { - return err - } - return c.Describe(ctx, client, describe) -} - -func (p *Pool) Execute(ctx context.Context, client gat.Client, execute *protocol.Execute) error { - c, err := p.getOrAssign(client) - if err != nil { - return err - } - return c.Execute(ctx, client, execute) -} - -func (p *Pool) SimpleQuery(ctx context.Context, client gat.Client, query string) error { - c, err := p.getOrAssign(client) - if err != nil { - return err - } - return c.SimpleQuery(ctx, client, query) -} - -func (p *Pool) Transaction(ctx context.Context, client gat.Client, query string) error { - c, err := p.getOrAssign(client) - if err != nil { - return err - } - return c.Transaction(ctx, client, query) -} - -func (p *Pool) CallFunction(ctx context.Context, client gat.Client, payload *protocol.FunctionCall) error { - c, err := p.getOrAssign(client) - if err != nil { - return err - } - return c.CallFunction(ctx, client, payload) -} - -var _ gat.Pool = (*Pool)(nil) diff --git a/lib/gat/pool/transaction/pool.go b/lib/gat/pool/transaction/pool.go deleted file mode 100644 index c875cf98414137faa5b0f04711dbc9e0dc96c9fd..0000000000000000000000000000000000000000 --- a/lib/gat/pool/transaction/pool.go +++ /dev/null @@ -1,326 +0,0 @@ -package transaction - -import ( - "context" - "errors" - "fmt" - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/pool/transaction/shard" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "gfx.cafe/gfx/pggat/lib/metrics" - "math/rand" - "sync" - "time" -) - -type Pool struct { - // config - c *config.Pool - user *config.User - - shards []*shard.Shard - mu sync.RWMutex - - // these never change - database gat.Database - dialer gat.Dialer -} - -func New(database gat.Database, dialer gat.Dialer, conf *config.Pool, user *config.User) *Pool { - p := &Pool{ - database: database, - dialer: dialer, - } - p.EnsureConfig(conf, user) - return p -} - -func (c *Pool) GetDatabase() gat.Database { - return c.database -} - -func (c *Pool) fetchShard(client gat.Client, n int) *shard.Shard { - c.mu.Lock() - defer c.mu.Unlock() - - n = n % len(c.shards) - - if c.shards[n] != nil { - return c.shards[n] - } - - c.shards[n] = shard.FromConfig(c.dialer, client.GetOptions(), c.c, c.user, c.c.Shards[n], c.database) - return c.shards[n] -} - -func (c *Pool) chooseShard(client gat.Client) *shard.Shard { - preferred := -1 - if client != nil { - if p, ok := client.GetRequestedShard(); ok { - preferred = p - } - - key := client.GetShardingKey() - if key != "" { - // TODO do sharding function on key - } - } - - c.mu.RLock() - if preferred == -1 { - preferred = rand.Intn(len(c.shards)) - } else { - preferred = preferred % len(c.shards) - } - s := c.shards[preferred] - c.mu.RUnlock() - if s != nil { - return s - } - - return c.fetchShard(client, preferred) -} - -func (c *Pool) EnsureConfig(p *config.Pool, u *config.User) { - c.mu.Lock() - defer c.mu.Unlock() - c.c = p - c.user = u - - c.shards = make([]*shard.Shard, len(p.Shards)) -} - -func (c *Pool) OnDisconnect(_ gat.Client) {} - -func (c *Pool) GetUser() *config.User { - return c.user -} - -var errNoServer = errors.New("fail: no server") -var errPermissionDenied = errors.New("permission denied") - -func (c *Pool) GetServerInfo(client gat.Client) []*protocol.ParameterStatus { - s := c.chooseShard(client) - conn := s.Choose(config.SERVERROLE_PRIMARY) - if conn == nil { - return nil - } - defer s.Return(conn) - conn.SetClient(client) - client.SetCurrentConn(conn) - defer conn.SetClient(nil) - defer client.SetCurrentConn(nil) - return conn.GetServerInfo() -} - -func (c *Pool) Describe(ctx context.Context, client gat.Client, d *protocol.Describe) error { - if c.user.StatementTimeout != 0 { - var done context.CancelFunc - ctx, done = context.WithTimeout(ctx, time.Duration(c.user.StatementTimeout)*time.Millisecond) - defer done() - } - - start := time.Now() - defer func() { - metrics.RecordTransactionTime(c.GetDatabase().GetName(), c.user.Name, time.Since(start)) - }() - - which := client.GetUnderlyingRole(d) - if !c.user.Role.CanUse(which) { - return errPermissionDenied - } - - s := c.chooseShard(client) - conn := s.Choose(which) - if conn == nil { - return errNoServer - } - conn.SetClient(client) - client.SetCurrentConn(conn) - err := conn.Describe(ctx, client, d) - conn.SetClient(nil) - client.SetCurrentConn(nil) - s.Return(conn) - - select { - case <-ctx.Done(): - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, ctx.Err()) - return ctx.Err() - default: - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, err) - return err - } -} - -func (c *Pool) Execute(ctx context.Context, client gat.Client, e *protocol.Execute) error { - if c.user.StatementTimeout != 0 { - var done context.CancelFunc - ctx, done = context.WithTimeout(ctx, time.Duration(c.user.StatementTimeout)*time.Millisecond) - defer done() - } - - start := time.Now() - defer func() { - metrics.RecordTransactionTime(c.GetDatabase().GetName(), c.user.Name, time.Since(start)) - }() - - which := client.GetUnderlyingPortalRole(e.Fields.Name) - if !c.user.Role.CanUse(which) { - return errPermissionDenied - } - - s := c.chooseShard(client) - conn := s.Choose(which) - if conn == nil { - return errNoServer - } - conn.SetClient(client) - client.SetCurrentConn(conn) - err := conn.Execute(ctx, client, e) - conn.SetClient(nil) - client.SetCurrentConn(nil) - s.Return(conn) - - select { - case <-ctx.Done(): - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, ctx.Err()) - return ctx.Err() - default: - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, err) - return err - } -} - -func (c *Pool) SimpleQuery(ctx context.Context, client gat.Client, q string) error { - // see if the database router can handle it - handled, err := c.database.GetRouter().TryHandle(client, q) - if err != nil { - return err - } - if handled { - return nil - } - - if c.user.StatementTimeout != 0 { - var done context.CancelFunc - ctx, done = context.WithTimeout(ctx, time.Duration(c.user.StatementTimeout)*time.Millisecond) - defer done() - } - - start := time.Now() - defer func() { - metrics.RecordQueryTime(c.GetDatabase().GetName(), c.user.Name, time.Since(start)) - }() - - which, err := c.database.GetRouter().InferRole(q) - if err != nil { - return fmt.Errorf("error parsing '%s': %w", q, err) - } - if !c.user.Role.CanUse(which) { - return errPermissionDenied - } - - s := c.chooseShard(client) - conn := s.Choose(which) - if conn == nil { - return errNoServer - } - conn.SetClient(client) - client.SetCurrentConn(conn) - err = conn.SimpleQuery(ctx, client, q) - conn.SetClient(nil) - client.SetCurrentConn(nil) - s.Return(conn) - - select { - case <-ctx.Done(): - metrics.RecordQueryError(c.GetDatabase().GetName(), c.user.Name, ctx.Err()) - return ctx.Err() - default: - metrics.RecordQueryError(c.GetDatabase().GetName(), c.user.Name, err) - return err - } -} - -func (c *Pool) Transaction(ctx context.Context, client gat.Client, q string) error { - if c.user.StatementTimeout != 0 { - var done context.CancelFunc - ctx, done = context.WithTimeout(ctx, time.Duration(c.user.StatementTimeout)*time.Millisecond) - defer done() - } - - start := time.Now() - defer func() { - metrics.RecordTransactionTime(c.GetDatabase().GetName(), c.user.Name, time.Since(start)) - }() - - which, err := c.database.GetRouter().InferRole(q) - if err != nil { - return fmt.Errorf("error parsing '%s': %w", q, err) - } - if !c.user.Role.CanUse(which) { - return errPermissionDenied - } - - s := c.chooseShard(client) - conn := s.Choose(which) - if conn == nil { - return errNoServer - } - conn.SetClient(client) - client.SetCurrentConn(conn) - err = conn.Transaction(ctx, client, q) - conn.SetClient(nil) - client.SetCurrentConn(nil) - s.Return(conn) - - select { - case <-ctx.Done(): - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, ctx.Err()) - return ctx.Err() - default: - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, err) - return err - } -} - -func (c *Pool) CallFunction(ctx context.Context, client gat.Client, f *protocol.FunctionCall) error { - if c.user.StatementTimeout != 0 { - var done context.CancelFunc - ctx, done = context.WithTimeout(ctx, time.Duration(c.user.StatementTimeout)*time.Millisecond) - defer done() - } - - start := time.Now() - defer func() { - metrics.RecordQueryTime(c.GetDatabase().GetName(), c.user.Name, time.Since(start)) - }() - - if !c.user.Role.CanUse(config.SERVERROLE_PRIMARY) { - return errPermissionDenied - } - - s := c.chooseShard(client) - conn := s.Choose(config.SERVERROLE_PRIMARY) - if conn == nil { - return errNoServer - } - conn.SetClient(client) - client.SetCurrentConn(conn) - err := conn.CallFunction(ctx, client, f) - conn.SetClient(nil) - client.SetCurrentConn(nil) - s.Return(conn) - - select { - case <-ctx.Done(): - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, ctx.Err()) - return ctx.Err() - default: - metrics.RecordTransactionError(c.GetDatabase().GetName(), c.user.Name, err) - return err - } -} - -var _ gat.Pool = (*Pool)(nil) diff --git a/lib/gat/pool/transaction/shard/conn.go b/lib/gat/pool/transaction/shard/conn.go deleted file mode 100644 index 5262affe039efe235ab57c3708cb7a0bee3d1d21..0000000000000000000000000000000000000000 --- a/lib/gat/pool/transaction/shard/conn.go +++ /dev/null @@ -1,124 +0,0 @@ -package shard - -import ( - "context" - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "log" - "net" - "time" -) - -type Conn struct { - conn gat.Connection - conf *config.Server - replicaId int - s *Shard -} - -func (s *Conn) GetServerInfo() []*protocol.ParameterStatus { - return s.conn.GetServerInfo() -} - -func (s *Conn) GetDatabase() string { - return s.conn.GetDatabase() -} - -func (s *Conn) GetState() gat.ConnectionState { - return s.conn.GetState() -} - -func (s *Conn) GetHost() string { - return s.conn.GetHost() -} - -func (s *Conn) GetPort() int { - return s.conn.GetPort() -} - -func (s *Conn) GetAddress() net.Addr { - return s.conn.GetAddress() -} - -func (s *Conn) GetLocalAddress() net.Addr { - return s.conn.GetLocalAddress() -} - -func (s *Conn) GetConnectTime() time.Time { - return s.conn.GetConnectTime() -} - -func (s *Conn) GetRequestTime() time.Time { - return s.conn.GetRequestTime() -} - -func (s *Conn) GetClient() gat.Client { - return s.conn.GetClient() -} - -func (s *Conn) SetClient(client gat.Client) { - s.conn.SetClient(client) -} - -func (s *Conn) GetRemotePid() int { - return s.conn.GetRemotePid() -} - -func (s *Conn) GetTLS() string { - return s.conn.GetTLS() -} - -func (s *Conn) IsCloseNeeded() bool { - return s.conn.IsCloseNeeded() -} - -func (s *Conn) Close() error { - return s.conn.Close() -} - -func (s *Conn) Describe(ctx context.Context, client gat.Client, payload *protocol.Describe) error { - return s.conn.Describe(ctx, client, payload) -} - -func (s *Conn) Execute(ctx context.Context, client gat.Client, payload *protocol.Execute) error { - return s.conn.Execute(ctx, client, payload) -} - -func (s *Conn) CallFunction(ctx context.Context, client gat.Client, payload *protocol.FunctionCall) error { - return s.conn.CallFunction(ctx, client, payload) -} - -func (s *Conn) SimpleQuery(ctx context.Context, client gat.Client, payload string) error { - return s.conn.SimpleQuery(ctx, client, payload) -} - -func (s *Conn) Transaction(ctx context.Context, client gat.Client, payload string) error { - return s.conn.Transaction(ctx, client, payload) -} - -func (s *Conn) Cancel() error { - return s.conn.Cancel() -} - -func (s *Conn) connect() { - if s.s == nil || s.conf == nil { - return - } - if s.conn != nil { - _ = s.conn.Close() - } - var err error - s.conn, err = s.s.dialer(context.TODO(), s.s.options, s.s.user, s.s.conf, s.conf) - if err != nil { - log.Println("error connecting to server:", err) - } - return -} - -func (s *Conn) acquire() *Conn { - if s.conn == nil || s.conn.IsCloseNeeded() { - s.connect() - } - return s -} diff --git a/lib/gat/pool/transaction/shard/pool.go b/lib/gat/pool/transaction/shard/pool.go deleted file mode 100644 index 985f36262f6a505ad504cd60902575671fc88f1d..0000000000000000000000000000000000000000 --- a/lib/gat/pool/transaction/shard/pool.go +++ /dev/null @@ -1,34 +0,0 @@ -package shard - -type Pool[T any] interface { - TryGet() (T, bool) - Get() T - Put(T) -} - -type ChannelPool[T any] struct { - ch chan T -} - -func NewChannelPool[T any](size int) *ChannelPool[T] { - return &ChannelPool[T]{ - ch: make(chan T, size*10), - } -} - -func (c *ChannelPool[T]) Get() T { - return <-c.ch -} - -func (c *ChannelPool[T]) TryGet() (T, bool) { - select { - case out := <-c.ch: - return out, true - default: - return *new(T), false - } -} - -func (c *ChannelPool[T]) Put(t T) { - c.ch <- t -} diff --git a/lib/gat/pool/transaction/shard/shard.go b/lib/gat/pool/transaction/shard/shard.go deleted file mode 100644 index aedb5157e69200b6a780be7a5fb34a275ecdcdee..0000000000000000000000000000000000000000 --- a/lib/gat/pool/transaction/shard/shard.go +++ /dev/null @@ -1,134 +0,0 @@ -package shard - -import ( - "gfx.cafe/gfx/pggat/lib/config" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "gfx.cafe/gfx/pggat/lib/metrics" - "math/rand" - "sync" - "time" -) - -type Shard struct { - primary Pool[*Conn] - replicas []Pool[*Conn] - - pool *config.Pool - user *config.User - conf *config.Shard - - database gat.Database - - options []protocol.FieldsStartupMessageParameters - - dialer gat.Dialer - - mu sync.RWMutex -} - -func FromConfig(dialer gat.Dialer, options []protocol.FieldsStartupMessageParameters, pool *config.Pool, user *config.User, conf *config.Shard, database gat.Database) *Shard { - out := &Shard{ - pool: pool, - user: user, - conf: conf, - - database: database, - - options: options, - - dialer: dialer, - } - out.init() - return out -} - -func (s *Shard) newConn(conf *config.Server, replicaId int) *Conn { - return &Conn{ - conf: conf, - replicaId: replicaId, - s: s, - } -} - -func (s *Shard) init() { - s.mu.Lock() - defer s.mu.Unlock() - poolSize := s.user.PoolSize - for _, serv := range s.conf.Servers { - pool := NewChannelPool[*Conn](poolSize) - for i := 0; i < poolSize; i++ { - pool.Put(s.newConn(serv, len(s.replicas))) - } - switch serv.Role { - case config.SERVERROLE_PRIMARY: - s.primary = pool - case config.SERVERROLE_REPLICA: - s.replicas = append(s.replicas, pool) - } - } -} - -func (s *Shard) tryAcquireAvailableReplica() *Conn { - // try to get any available conn - for _, replica := range s.replicas { - c, ok := replica.TryGet() - if ok { - return c.acquire() - } - } - - return nil -} - -func (s *Shard) Choose(role config.ServerRole) *Conn { - s.mu.RLock() - defer s.mu.RUnlock() - start := time.Now() - defer func() { - metrics.RecordWaitTime(s.database.GetName(), s.user.Name, time.Since(start)) - }() - switch role { - case config.SERVERROLE_PRIMARY: - return s.primary.Get().acquire() - case config.SERVERROLE_REPLICA: - if len(s.replicas) == 0 { - // only return primary if primary reads are enabled - if s.pool.PrimaryReadsEnabled { - return s.primary.Get().acquire() - } - return nil - } - - // read from a random replica - acq := s.tryAcquireAvailableReplica() - if acq != nil { - return acq - } - - // try to fall back to primary if there is available resources there - if s.pool.PrimaryReadsEnabled { - c, ok := s.primary.TryGet() - if ok { - return c.acquire() - } - } - - // wait on a random conn - return s.replicas[rand.Intn(len(s.replicas))].Get().acquire() - default: - return nil - } -} - -func (s *Shard) Return(conn *Conn) { - s.mu.RLock() - defer s.mu.RUnlock() - switch conn.conf.Role { - case config.SERVERROLE_PRIMARY: - s.primary.Put(conn) - case config.SERVERROLE_REPLICA: - s.replicas[conn.replicaId].Put(conn) - default: - } -} diff --git a/lib/gat/protocol/backend.go b/lib/gat/protocol/backend.go deleted file mode 100644 index 8e4e9dcf2855f558e78f42170b50102e8f52b9be..0000000000000000000000000000000000000000 --- a/lib/gat/protocol/backend.go +++ /dev/null @@ -1,1945 +0,0 @@ -package protocol - -import ( - "bytes" - "gfx.cafe/util/go/bufpool" - "io" -) - -// codegen: modify for debug only - -var _ bytes.Buffer -var _ io.Reader - -type FieldsAuthentication struct { - Code int32 - Salt []byte - GSSAuthData []byte - SASLMechanism []string - SASLChallenge []byte - SASLAdditionalData []byte -} - -func (T *FieldsAuthentication) Read(payloadLength int, reader io.Reader) (err error) { - T.Code, err = ReadInt32(reader) - if err != nil { - return - } - if T.Code == 5 { - SaltLength := 4 - T.Salt = make([]byte, int(SaltLength)) - for i := 0; i < int(SaltLength); i++ { - T.Salt[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - if T.Code == 8 { - GSSAuthDataLength := payloadLength - 4 - T.GSSAuthData = make([]byte, int(GSSAuthDataLength)) - for i := 0; i < int(GSSAuthDataLength); i++ { - T.GSSAuthData[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - if T.Code == 10 { - var P string - for ok := true; ok; ok = P != "" { - var newP string - newP, err = ReadString(reader) - if err != nil { - return - } - T.SASLMechanism = append(T.SASLMechanism, newP) - P = newP - } - } - if T.Code == 11 { - SASLChallengeLength := payloadLength - 4 - T.SASLChallenge = make([]byte, int(SASLChallengeLength)) - for i := 0; i < int(SASLChallengeLength); i++ { - T.SASLChallenge[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - if T.Code == 12 { - SASLAdditionalDataLength := payloadLength - 4 - T.SASLAdditionalData = make([]byte, int(SASLAdditionalDataLength)) - for i := 0; i < int(SASLAdditionalDataLength); i++ { - T.SASLAdditionalData[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsAuthentication) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.Code) - if err != nil { - return - } - length += temp - if T.Code == 5 { - for _, v := range T.Salt { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - } - if T.Code == 8 { - for _, v := range T.GSSAuthData { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - } - if T.Code == 10 { - for _, v := range T.SASLMechanism { - temp, err = WriteString(writer, v) - if err != nil { - return - } - length += temp - } - } - if T.Code == 11 { - for _, v := range T.SASLChallenge { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - } - if T.Code == 12 { - for _, v := range T.SASLAdditionalData { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - } - _ = temp - return -} - -type Authentication struct { - Fields FieldsAuthentication -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Authentication) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Authentication) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('R')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Authentication)(nil) - -type FieldsBackendKeyData struct { - ProcessID int32 - SecretKey int32 -} - -func (T *FieldsBackendKeyData) Read(payloadLength int, reader io.Reader) (err error) { - T.ProcessID, err = ReadInt32(reader) - if err != nil { - return - } - T.SecretKey, err = ReadInt32(reader) - if err != nil { - return - } - return -} - -func (T *FieldsBackendKeyData) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.ProcessID) - if err != nil { - return - } - length += temp - temp, err = WriteInt32(writer, T.SecretKey) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type BackendKeyData struct { - Fields FieldsBackendKeyData -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *BackendKeyData) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *BackendKeyData) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('K')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*BackendKeyData)(nil) - -type FieldsBindComplete struct { -} - -func (T *FieldsBindComplete) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsBindComplete) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type BindComplete struct { - Fields FieldsBindComplete -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *BindComplete) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *BindComplete) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('2')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*BindComplete)(nil) - -type FieldsCloseComplete struct { -} - -func (T *FieldsCloseComplete) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsCloseComplete) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type CloseComplete struct { - Fields FieldsCloseComplete -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CloseComplete) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CloseComplete) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('3')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CloseComplete)(nil) - -type FieldsCommandComplete struct { - Data string -} - -func (T *FieldsCommandComplete) Read(payloadLength int, reader io.Reader) (err error) { - T.Data, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsCommandComplete) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Data) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type CommandComplete struct { - Fields FieldsCommandComplete -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CommandComplete) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CommandComplete) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('C')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CommandComplete)(nil) - -type FieldsCopyBothResponse struct { - Format int8 - ColumnFormats []int16 -} - -func (T *FieldsCopyBothResponse) Read(payloadLength int, reader io.Reader) (err error) { - T.Format, err = ReadInt8(reader) - if err != nil { - return - } - var ColumnFormatsLength int16 - ColumnFormatsLength, err = ReadInt16(reader) - if err != nil { - return - } - if ColumnFormatsLength == int16(-1) { - T.ColumnFormats = nil - } else { - T.ColumnFormats = make([]int16, int(ColumnFormatsLength)) - for i := 0; i < int(ColumnFormatsLength); i++ { - T.ColumnFormats[i], err = ReadInt16(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsCopyBothResponse) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt8(writer, T.Format) - if err != nil { - return - } - length += temp - if T.ColumnFormats == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ColumnFormats))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ColumnFormats { - temp, err = WriteInt16(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type CopyBothResponse struct { - Fields FieldsCopyBothResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CopyBothResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CopyBothResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('W')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CopyBothResponse)(nil) - -type FieldsCopyInResponse struct { - Format int8 - ColumnFormats []int16 -} - -func (T *FieldsCopyInResponse) Read(payloadLength int, reader io.Reader) (err error) { - T.Format, err = ReadInt8(reader) - if err != nil { - return - } - var ColumnFormatsLength int16 - ColumnFormatsLength, err = ReadInt16(reader) - if err != nil { - return - } - if ColumnFormatsLength == int16(-1) { - T.ColumnFormats = nil - } else { - T.ColumnFormats = make([]int16, int(ColumnFormatsLength)) - for i := 0; i < int(ColumnFormatsLength); i++ { - T.ColumnFormats[i], err = ReadInt16(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsCopyInResponse) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt8(writer, T.Format) - if err != nil { - return - } - length += temp - if T.ColumnFormats == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ColumnFormats))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ColumnFormats { - temp, err = WriteInt16(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type CopyInResponse struct { - Fields FieldsCopyInResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CopyInResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CopyInResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('G')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CopyInResponse)(nil) - -type FieldsCopyOutResponse struct { - Format int8 - ColumnFormats []int16 -} - -func (T *FieldsCopyOutResponse) Read(payloadLength int, reader io.Reader) (err error) { - T.Format, err = ReadInt8(reader) - if err != nil { - return - } - var ColumnFormatsLength int16 - ColumnFormatsLength, err = ReadInt16(reader) - if err != nil { - return - } - if ColumnFormatsLength == int16(-1) { - T.ColumnFormats = nil - } else { - T.ColumnFormats = make([]int16, int(ColumnFormatsLength)) - for i := 0; i < int(ColumnFormatsLength); i++ { - T.ColumnFormats[i], err = ReadInt16(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsCopyOutResponse) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt8(writer, T.Format) - if err != nil { - return - } - length += temp - if T.ColumnFormats == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ColumnFormats))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ColumnFormats { - temp, err = WriteInt16(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type CopyOutResponse struct { - Fields FieldsCopyOutResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CopyOutResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CopyOutResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('H')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CopyOutResponse)(nil) - -type FieldsDataRowColumns struct { - Bytes []byte -} - -func (T *FieldsDataRowColumns) Read(payloadLength int, reader io.Reader) (err error) { - var BytesLength int32 - BytesLength, err = ReadInt32(reader) - if err != nil { - return - } - if BytesLength == int32(-1) { - T.Bytes = nil - } else { - T.Bytes = make([]byte, int(BytesLength)) - for i := 0; i < int(BytesLength); i++ { - T.Bytes[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsDataRowColumns) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Bytes == nil { - temp, err = WriteInt32(writer, int32(-1)) - } else { - temp, err = WriteInt32(writer, int32(len(T.Bytes))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Bytes { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FieldsDataRow struct { - Columns []FieldsDataRowColumns -} - -func (T *FieldsDataRow) Read(payloadLength int, reader io.Reader) (err error) { - var ColumnsLength int16 - ColumnsLength, err = ReadInt16(reader) - if err != nil { - return - } - if ColumnsLength == int16(-1) { - T.Columns = nil - } else { - T.Columns = make([]FieldsDataRowColumns, int(ColumnsLength)) - for i := 0; i < int(ColumnsLength); i++ { - err = T.Columns[i].Read(payloadLength, reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsDataRow) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Columns == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.Columns))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Columns { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type DataRow struct { - Fields FieldsDataRow -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *DataRow) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *DataRow) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('D')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*DataRow)(nil) - -type FieldsEmptyQueryResponse struct { -} - -func (T *FieldsEmptyQueryResponse) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsEmptyQueryResponse) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type EmptyQueryResponse struct { - Fields FieldsEmptyQueryResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *EmptyQueryResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *EmptyQueryResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('I')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*EmptyQueryResponse)(nil) - -type FieldsErrorResponseResponses struct { - Code byte - Value string -} - -func (T *FieldsErrorResponseResponses) Read(payloadLength int, reader io.Reader) (err error) { - T.Code, err = ReadByte(reader) - if err != nil { - return - } - if T.Code != 0 { - T.Value, err = ReadString(reader) - if err != nil { - return - } - } - return -} - -func (T *FieldsErrorResponseResponses) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteByte(writer, T.Code) - if err != nil { - return - } - length += temp - if T.Code != 0 { - temp, err = WriteString(writer, T.Value) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FieldsErrorResponse struct { - Responses []FieldsErrorResponseResponses -} - -func (T *FieldsErrorResponse) Read(payloadLength int, reader io.Reader) (err error) { - var P FieldsErrorResponseResponses - for ok := true; ok; ok = P.Code != 0 { - var newP FieldsErrorResponseResponses - err = newP.Read(payloadLength, reader) - if err != nil { - return - } - T.Responses = append(T.Responses, newP) - P = newP - } - return -} - -func (T *FieldsErrorResponse) Write(writer io.Writer) (length int, err error) { - var temp int - for _, v := range T.Responses { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type ErrorResponse struct { - Fields FieldsErrorResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *ErrorResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *ErrorResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('E')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*ErrorResponse)(nil) - -type FieldsFunctionCallResponse struct { - Result []byte -} - -func (T *FieldsFunctionCallResponse) Read(payloadLength int, reader io.Reader) (err error) { - var ResultLength int32 - ResultLength, err = ReadInt32(reader) - if err != nil { - return - } - if ResultLength == int32(-1) { - T.Result = nil - } else { - T.Result = make([]byte, int(ResultLength)) - for i := 0; i < int(ResultLength); i++ { - T.Result[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsFunctionCallResponse) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Result == nil { - temp, err = WriteInt32(writer, int32(-1)) - } else { - temp, err = WriteInt32(writer, int32(len(T.Result))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Result { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FunctionCallResponse struct { - Fields FieldsFunctionCallResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *FunctionCallResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *FunctionCallResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('V')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*FunctionCallResponse)(nil) - -type FieldsNegotiateProtocolVersion struct { - MinorVersion int32 - NotRecognized []string -} - -func (T *FieldsNegotiateProtocolVersion) Read(payloadLength int, reader io.Reader) (err error) { - T.MinorVersion, err = ReadInt32(reader) - if err != nil { - return - } - var NotRecognizedLength int32 - NotRecognizedLength, err = ReadInt32(reader) - if err != nil { - return - } - if NotRecognizedLength == int32(-1) { - T.NotRecognized = nil - } else { - T.NotRecognized = make([]string, int(NotRecognizedLength)) - for i := 0; i < int(NotRecognizedLength); i++ { - T.NotRecognized[i], err = ReadString(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsNegotiateProtocolVersion) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.MinorVersion) - if err != nil { - return - } - length += temp - if T.NotRecognized == nil { - temp, err = WriteInt32(writer, int32(-1)) - } else { - temp, err = WriteInt32(writer, int32(len(T.NotRecognized))) - } - if err != nil { - return - } - length += temp - for _, v := range T.NotRecognized { - temp, err = WriteString(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type NegotiateProtocolVersion struct { - Fields FieldsNegotiateProtocolVersion -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *NegotiateProtocolVersion) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *NegotiateProtocolVersion) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('v')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*NegotiateProtocolVersion)(nil) - -type FieldsNoData struct { -} - -func (T *FieldsNoData) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsNoData) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type NoData struct { - Fields FieldsNoData -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *NoData) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *NoData) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('n')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*NoData)(nil) - -type FieldsNoticeResponseResponses struct { - Code byte - Value string -} - -func (T *FieldsNoticeResponseResponses) Read(payloadLength int, reader io.Reader) (err error) { - T.Code, err = ReadByte(reader) - if err != nil { - return - } - if T.Code != 0 { - T.Value, err = ReadString(reader) - if err != nil { - return - } - } - return -} - -func (T *FieldsNoticeResponseResponses) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteByte(writer, T.Code) - if err != nil { - return - } - length += temp - if T.Code != 0 { - temp, err = WriteString(writer, T.Value) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FieldsNoticeResponse struct { - Responses []FieldsNoticeResponseResponses -} - -func (T *FieldsNoticeResponse) Read(payloadLength int, reader io.Reader) (err error) { - var P FieldsNoticeResponseResponses - for ok := true; ok; ok = P.Code != 0 { - var newP FieldsNoticeResponseResponses - err = newP.Read(payloadLength, reader) - if err != nil { - return - } - T.Responses = append(T.Responses, newP) - P = newP - } - return -} - -func (T *FieldsNoticeResponse) Write(writer io.Writer) (length int, err error) { - var temp int - for _, v := range T.Responses { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type NoticeResponse struct { - Fields FieldsNoticeResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *NoticeResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *NoticeResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('N')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*NoticeResponse)(nil) - -type FieldsNotificationResponse struct { - ProcessID int32 - Channel string - Payload string -} - -func (T *FieldsNotificationResponse) Read(payloadLength int, reader io.Reader) (err error) { - T.ProcessID, err = ReadInt32(reader) - if err != nil { - return - } - T.Channel, err = ReadString(reader) - if err != nil { - return - } - T.Payload, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsNotificationResponse) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.ProcessID) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.Channel) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.Payload) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type NotificationResponse struct { - Fields FieldsNotificationResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *NotificationResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *NotificationResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('A')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*NotificationResponse)(nil) - -type FieldsParameterDescription struct { - Parameters []int32 -} - -func (T *FieldsParameterDescription) Read(payloadLength int, reader io.Reader) (err error) { - var ParametersLength int16 - ParametersLength, err = ReadInt16(reader) - if err != nil { - return - } - if ParametersLength == int16(-1) { - T.Parameters = nil - } else { - T.Parameters = make([]int32, int(ParametersLength)) - for i := 0; i < int(ParametersLength); i++ { - T.Parameters[i], err = ReadInt32(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsParameterDescription) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Parameters == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.Parameters))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Parameters { - temp, err = WriteInt32(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type ParameterDescription struct { - Fields FieldsParameterDescription -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *ParameterDescription) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *ParameterDescription) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('t')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*ParameterDescription)(nil) - -type FieldsParameterStatus struct { - Parameter string - Value string -} - -func (T *FieldsParameterStatus) Read(payloadLength int, reader io.Reader) (err error) { - T.Parameter, err = ReadString(reader) - if err != nil { - return - } - T.Value, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsParameterStatus) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Parameter) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.Value) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type ParameterStatus struct { - Fields FieldsParameterStatus -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *ParameterStatus) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *ParameterStatus) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('S')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*ParameterStatus)(nil) - -type FieldsParseComplete struct { -} - -func (T *FieldsParseComplete) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsParseComplete) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type ParseComplete struct { - Fields FieldsParseComplete -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *ParseComplete) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *ParseComplete) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('1')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*ParseComplete)(nil) - -type FieldsPortalSuspended struct { -} - -func (T *FieldsPortalSuspended) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsPortalSuspended) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type PortalSuspended struct { - Fields FieldsPortalSuspended -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *PortalSuspended) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *PortalSuspended) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('s')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*PortalSuspended)(nil) - -type FieldsReadyForQuery struct { - Status byte -} - -func (T *FieldsReadyForQuery) Read(payloadLength int, reader io.Reader) (err error) { - T.Status, err = ReadByte(reader) - if err != nil { - return - } - return -} - -func (T *FieldsReadyForQuery) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteByte(writer, T.Status) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type ReadyForQuery struct { - Fields FieldsReadyForQuery -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *ReadyForQuery) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *ReadyForQuery) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('Z')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*ReadyForQuery)(nil) - -type FieldsRowDescriptionFields struct { - Name string - TableId int32 - AttributeNumber int16 - DataType int32 - DataTypeSize int16 - TypeModifier int32 - FormatCode int16 -} - -func (T *FieldsRowDescriptionFields) Read(payloadLength int, reader io.Reader) (err error) { - T.Name, err = ReadString(reader) - if err != nil { - return - } - T.TableId, err = ReadInt32(reader) - if err != nil { - return - } - T.AttributeNumber, err = ReadInt16(reader) - if err != nil { - return - } - T.DataType, err = ReadInt32(reader) - if err != nil { - return - } - T.DataTypeSize, err = ReadInt16(reader) - if err != nil { - return - } - T.TypeModifier, err = ReadInt32(reader) - if err != nil { - return - } - T.FormatCode, err = ReadInt16(reader) - if err != nil { - return - } - return -} - -func (T *FieldsRowDescriptionFields) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Name) - if err != nil { - return - } - length += temp - temp, err = WriteInt32(writer, T.TableId) - if err != nil { - return - } - length += temp - temp, err = WriteInt16(writer, T.AttributeNumber) - if err != nil { - return - } - length += temp - temp, err = WriteInt32(writer, T.DataType) - if err != nil { - return - } - length += temp - temp, err = WriteInt16(writer, T.DataTypeSize) - if err != nil { - return - } - length += temp - temp, err = WriteInt32(writer, T.TypeModifier) - if err != nil { - return - } - length += temp - temp, err = WriteInt16(writer, T.FormatCode) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type FieldsRowDescription struct { - Fields []FieldsRowDescriptionFields -} - -func (T *FieldsRowDescription) Read(payloadLength int, reader io.Reader) (err error) { - var FieldsLength int16 - FieldsLength, err = ReadInt16(reader) - if err != nil { - return - } - if FieldsLength == int16(-1) { - T.Fields = nil - } else { - T.Fields = make([]FieldsRowDescriptionFields, int(FieldsLength)) - for i := 0; i < int(FieldsLength); i++ { - err = T.Fields[i].Read(payloadLength, reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsRowDescription) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Fields == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.Fields))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Fields { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type RowDescription struct { - Fields FieldsRowDescription -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *RowDescription) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *RowDescription) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('T')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*RowDescription)(nil) diff --git a/lib/gat/protocol/frontend.go b/lib/gat/protocol/frontend.go deleted file mode 100644 index b22a6f155f55fae8137c1acb6395473d9ae2f5f5..0000000000000000000000000000000000000000 --- a/lib/gat/protocol/frontend.go +++ /dev/null @@ -1,1367 +0,0 @@ -package protocol - -import ( - "bytes" - "gfx.cafe/util/go/bufpool" - "io" -) - -// codegen: modify for debug only - -var _ bytes.Buffer -var _ io.Reader - -type FieldsAuthenticationResponse struct { - Data []byte -} - -func (T *FieldsAuthenticationResponse) Read(payloadLength int, reader io.Reader) (err error) { - DataLength := payloadLength - T.Data = make([]byte, int(DataLength)) - for i := 0; i < int(DataLength); i++ { - T.Data[i], err = ReadByte(reader) - if err != nil { - return - } - } - return -} - -func (T *FieldsAuthenticationResponse) Write(writer io.Writer) (length int, err error) { - var temp int - for _, v := range T.Data { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type AuthenticationResponse struct { - Fields FieldsAuthenticationResponse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *AuthenticationResponse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *AuthenticationResponse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('p')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*AuthenticationResponse)(nil) - -type FieldsBindParameterValues struct { - Value []byte -} - -func (T *FieldsBindParameterValues) Read(payloadLength int, reader io.Reader) (err error) { - var ValueLength int32 - ValueLength, err = ReadInt32(reader) - if err != nil { - return - } - if ValueLength == int32(-1) { - T.Value = nil - } else { - T.Value = make([]byte, int(ValueLength)) - for i := 0; i < int(ValueLength); i++ { - T.Value[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsBindParameterValues) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Value == nil { - temp, err = WriteInt32(writer, int32(-1)) - } else { - temp, err = WriteInt32(writer, int32(len(T.Value))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Value { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FieldsBind struct { - Destination string - PreparedStatement string - ParameterFormatCodes []int16 - ParameterValues []FieldsBindParameterValues - ResultFormatCodes []int16 -} - -func (T *FieldsBind) Read(payloadLength int, reader io.Reader) (err error) { - T.Destination, err = ReadString(reader) - if err != nil { - return - } - T.PreparedStatement, err = ReadString(reader) - if err != nil { - return - } - var ParameterFormatCodesLength int16 - ParameterFormatCodesLength, err = ReadInt16(reader) - if err != nil { - return - } - if ParameterFormatCodesLength == int16(-1) { - T.ParameterFormatCodes = nil - } else { - T.ParameterFormatCodes = make([]int16, int(ParameterFormatCodesLength)) - for i := 0; i < int(ParameterFormatCodesLength); i++ { - T.ParameterFormatCodes[i], err = ReadInt16(reader) - if err != nil { - return - } - } - } - var ParameterValuesLength int16 - ParameterValuesLength, err = ReadInt16(reader) - if err != nil { - return - } - if ParameterValuesLength == int16(-1) { - T.ParameterValues = nil - } else { - T.ParameterValues = make([]FieldsBindParameterValues, int(ParameterValuesLength)) - for i := 0; i < int(ParameterValuesLength); i++ { - err = T.ParameterValues[i].Read(payloadLength, reader) - if err != nil { - return - } - } - } - var ResultFormatCodesLength int16 - ResultFormatCodesLength, err = ReadInt16(reader) - if err != nil { - return - } - if ResultFormatCodesLength == int16(-1) { - T.ResultFormatCodes = nil - } else { - T.ResultFormatCodes = make([]int16, int(ResultFormatCodesLength)) - for i := 0; i < int(ResultFormatCodesLength); i++ { - T.ResultFormatCodes[i], err = ReadInt16(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsBind) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Destination) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.PreparedStatement) - if err != nil { - return - } - length += temp - if T.ParameterFormatCodes == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ParameterFormatCodes))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ParameterFormatCodes { - temp, err = WriteInt16(writer, v) - if err != nil { - return - } - length += temp - } - if T.ParameterValues == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ParameterValues))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ParameterValues { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - if T.ResultFormatCodes == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ResultFormatCodes))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ResultFormatCodes { - temp, err = WriteInt16(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type Bind struct { - Fields FieldsBind -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Bind) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Bind) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('B')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Bind)(nil) - -type FieldsClose struct { - Which byte - Name string -} - -func (T *FieldsClose) Read(payloadLength int, reader io.Reader) (err error) { - T.Which, err = ReadByte(reader) - if err != nil { - return - } - T.Name, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsClose) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteByte(writer, T.Which) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.Name) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type Close struct { - Fields FieldsClose -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Close) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Close) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('C')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Close)(nil) - -type FieldsCopyFail struct { - Cause string -} - -func (T *FieldsCopyFail) Read(payloadLength int, reader io.Reader) (err error) { - T.Cause, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsCopyFail) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Cause) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type CopyFail struct { - Fields FieldsCopyFail -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CopyFail) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CopyFail) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('f')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CopyFail)(nil) - -type FieldsDescribe struct { - Which byte - Name string -} - -func (T *FieldsDescribe) Read(payloadLength int, reader io.Reader) (err error) { - T.Which, err = ReadByte(reader) - if err != nil { - return - } - T.Name, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsDescribe) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteByte(writer, T.Which) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.Name) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type Describe struct { - Fields FieldsDescribe -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Describe) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Describe) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('D')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Describe)(nil) - -type FieldsExecute struct { - Name string - MaxRows int32 -} - -func (T *FieldsExecute) Read(payloadLength int, reader io.Reader) (err error) { - T.Name, err = ReadString(reader) - if err != nil { - return - } - T.MaxRows, err = ReadInt32(reader) - if err != nil { - return - } - return -} - -func (T *FieldsExecute) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Name) - if err != nil { - return - } - length += temp - temp, err = WriteInt32(writer, T.MaxRows) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type Execute struct { - Fields FieldsExecute -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Execute) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Execute) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('E')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Execute)(nil) - -type FieldsFlush struct { -} - -func (T *FieldsFlush) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsFlush) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type Flush struct { - Fields FieldsFlush -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Flush) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Flush) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('H')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Flush)(nil) - -type FieldsFunctionCallArguments struct { - Value []byte -} - -func (T *FieldsFunctionCallArguments) Read(payloadLength int, reader io.Reader) (err error) { - var ValueLength int32 - ValueLength, err = ReadInt32(reader) - if err != nil { - return - } - if ValueLength == int32(-1) { - T.Value = nil - } else { - T.Value = make([]byte, int(ValueLength)) - for i := 0; i < int(ValueLength); i++ { - T.Value[i], err = ReadByte(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsFunctionCallArguments) Write(writer io.Writer) (length int, err error) { - var temp int - if T.Value == nil { - temp, err = WriteInt32(writer, int32(-1)) - } else { - temp, err = WriteInt32(writer, int32(len(T.Value))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Value { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FieldsFunctionCall struct { - Function int32 - ArgumentFormatCodes []int16 - Arguments []FieldsFunctionCallArguments - ResultFormatCode int16 -} - -func (T *FieldsFunctionCall) Read(payloadLength int, reader io.Reader) (err error) { - T.Function, err = ReadInt32(reader) - if err != nil { - return - } - var ArgumentFormatCodesLength int16 - ArgumentFormatCodesLength, err = ReadInt16(reader) - if err != nil { - return - } - if ArgumentFormatCodesLength == int16(-1) { - T.ArgumentFormatCodes = nil - } else { - T.ArgumentFormatCodes = make([]int16, int(ArgumentFormatCodesLength)) - for i := 0; i < int(ArgumentFormatCodesLength); i++ { - T.ArgumentFormatCodes[i], err = ReadInt16(reader) - if err != nil { - return - } - } - } - var ArgumentsLength int16 - ArgumentsLength, err = ReadInt16(reader) - if err != nil { - return - } - if ArgumentsLength == int16(-1) { - T.Arguments = nil - } else { - T.Arguments = make([]FieldsFunctionCallArguments, int(ArgumentsLength)) - for i := 0; i < int(ArgumentsLength); i++ { - err = T.Arguments[i].Read(payloadLength, reader) - if err != nil { - return - } - } - } - T.ResultFormatCode, err = ReadInt16(reader) - if err != nil { - return - } - return -} - -func (T *FieldsFunctionCall) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.Function) - if err != nil { - return - } - length += temp - if T.ArgumentFormatCodes == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ArgumentFormatCodes))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ArgumentFormatCodes { - temp, err = WriteInt16(writer, v) - if err != nil { - return - } - length += temp - } - if T.Arguments == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.Arguments))) - } - if err != nil { - return - } - length += temp - for _, v := range T.Arguments { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - temp, err = WriteInt16(writer, T.ResultFormatCode) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type FunctionCall struct { - Fields FieldsFunctionCall -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *FunctionCall) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *FunctionCall) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('F')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*FunctionCall)(nil) - -type FieldsGSSENCRequest struct { - EncryptionRequestCode int32 -} - -func (T *FieldsGSSENCRequest) Read(payloadLength int, reader io.Reader) (err error) { - T.EncryptionRequestCode, err = ReadInt32(reader) - if err != nil { - return - } - return -} - -func (T *FieldsGSSENCRequest) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.EncryptionRequestCode) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type GSSENCRequest struct { - Fields FieldsGSSENCRequest -} - -// Read reads all but the packet identifier -func (T *GSSENCRequest) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *GSSENCRequest) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*GSSENCRequest)(nil) - -type FieldsParse struct { - PreparedStatement string - Query string - ParameterDataTypes []int32 -} - -func (T *FieldsParse) Read(payloadLength int, reader io.Reader) (err error) { - T.PreparedStatement, err = ReadString(reader) - if err != nil { - return - } - T.Query, err = ReadString(reader) - if err != nil { - return - } - var ParameterDataTypesLength int16 - ParameterDataTypesLength, err = ReadInt16(reader) - if err != nil { - return - } - if ParameterDataTypesLength == int16(-1) { - T.ParameterDataTypes = nil - } else { - T.ParameterDataTypes = make([]int32, int(ParameterDataTypesLength)) - for i := 0; i < int(ParameterDataTypesLength); i++ { - T.ParameterDataTypes[i], err = ReadInt32(reader) - if err != nil { - return - } - } - } - return -} - -func (T *FieldsParse) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.PreparedStatement) - if err != nil { - return - } - length += temp - temp, err = WriteString(writer, T.Query) - if err != nil { - return - } - length += temp - if T.ParameterDataTypes == nil { - temp, err = WriteInt16(writer, int16(-1)) - } else { - temp, err = WriteInt16(writer, int16(len(T.ParameterDataTypes))) - } - if err != nil { - return - } - length += temp - for _, v := range T.ParameterDataTypes { - temp, err = WriteInt32(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type Parse struct { - Fields FieldsParse -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Parse) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Parse) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('P')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Parse)(nil) - -type FieldsQuery struct { - Query string -} - -func (T *FieldsQuery) Read(payloadLength int, reader io.Reader) (err error) { - T.Query, err = ReadString(reader) - if err != nil { - return - } - return -} - -func (T *FieldsQuery) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Query) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type Query struct { - Fields FieldsQuery -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Query) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Query) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('Q')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Query)(nil) - -type FieldsSSLRequest struct { - SSLRequestCode int32 -} - -func (T *FieldsSSLRequest) Read(payloadLength int, reader io.Reader) (err error) { - T.SSLRequestCode, err = ReadInt32(reader) - if err != nil { - return - } - return -} - -func (T *FieldsSSLRequest) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.SSLRequestCode) - if err != nil { - return - } - length += temp - _ = temp - return -} - -type SSLRequest struct { - Fields FieldsSSLRequest -} - -// Read reads all but the packet identifier -func (T *SSLRequest) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *SSLRequest) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*SSLRequest)(nil) - -type FieldsStartupMessageParameters struct { - Name string - Value string -} - -func (T *FieldsStartupMessageParameters) Read(payloadLength int, reader io.Reader) (err error) { - T.Name, err = ReadString(reader) - if err != nil { - return - } - if T.Name != "" { - T.Value, err = ReadString(reader) - if err != nil { - return - } - } - return -} - -func (T *FieldsStartupMessageParameters) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteString(writer, T.Name) - if err != nil { - return - } - length += temp - if T.Name != "" { - temp, err = WriteString(writer, T.Value) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type FieldsStartupMessage struct { - ProtocolVersionNumber int32 - ProcessKey int32 - SecretKey int32 - Parameters []FieldsStartupMessageParameters -} - -func (T *FieldsStartupMessage) Read(payloadLength int, reader io.Reader) (err error) { - T.ProtocolVersionNumber, err = ReadInt32(reader) - if err != nil { - return - } - if T.ProtocolVersionNumber == 80877102 { - T.ProcessKey, err = ReadInt32(reader) - if err != nil { - return - } - } - if T.ProtocolVersionNumber == 80877102 { - T.SecretKey, err = ReadInt32(reader) - if err != nil { - return - } - } - if T.ProtocolVersionNumber == 196608 { - var P FieldsStartupMessageParameters - for ok := true; ok; ok = P.Name != "" { - var newP FieldsStartupMessageParameters - err = newP.Read(payloadLength, reader) - if err != nil { - return - } - T.Parameters = append(T.Parameters, newP) - P = newP - } - } - return -} - -func (T *FieldsStartupMessage) Write(writer io.Writer) (length int, err error) { - var temp int - temp, err = WriteInt32(writer, T.ProtocolVersionNumber) - if err != nil { - return - } - length += temp - if T.ProtocolVersionNumber == 80877102 { - temp, err = WriteInt32(writer, T.ProcessKey) - if err != nil { - return - } - length += temp - } - if T.ProtocolVersionNumber == 80877102 { - temp, err = WriteInt32(writer, T.SecretKey) - if err != nil { - return - } - length += temp - } - if T.ProtocolVersionNumber == 196608 { - for _, v := range T.Parameters { - temp, err = v.Write(writer) - if err != nil { - return - } - length += temp - } - } - _ = temp - return -} - -type StartupMessage struct { - Fields FieldsStartupMessage -} - -// Read reads all but the packet identifier -func (T *StartupMessage) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *StartupMessage) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*StartupMessage)(nil) - -type FieldsSync struct { -} - -func (T *FieldsSync) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsSync) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type Sync struct { - Fields FieldsSync -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Sync) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Sync) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('S')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Sync)(nil) - -type FieldsTerminate struct { -} - -func (T *FieldsTerminate) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsTerminate) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type Terminate struct { - Fields FieldsTerminate -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *Terminate) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *Terminate) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('X')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*Terminate)(nil) diff --git a/lib/gat/protocol/io.go b/lib/gat/protocol/io.go deleted file mode 100644 index cd2084c15cbb48822f3cd6eaf105c4642b0f4aa0..0000000000000000000000000000000000000000 --- a/lib/gat/protocol/io.go +++ /dev/null @@ -1,117 +0,0 @@ -package protocol - -import ( - "encoding/binary" - "io" - "strings" -) - -func ReadByte(reader io.Reader) (byte, error) { - var b [1]byte - _, err := io.ReadFull(reader, b[:]) - return b[0], err -} - -func ReadInt8(reader io.Reader) (int8, error) { - b, err := ReadByte(reader) - return int8(b), err -} - -func ReadUint16(reader io.Reader) (uint16, error) { - var b [2]byte - _, err := io.ReadFull(reader, b[:]) - return binary.BigEndian.Uint16(b[:]), err -} - -func ReadInt16(reader io.Reader) (int16, error) { - b, err := ReadUint16(reader) - return int16(b), err -} - -func ReadUint32(reader io.Reader) (uint32, error) { - var b [4]byte - _, err := io.ReadFull(reader, b[:]) - return binary.BigEndian.Uint32(b[:]), err -} - -func ReadInt32(reader io.Reader) (int32, error) { - b, err := ReadUint32(reader) - return int32(b), err -} - -func ReadUint64(reader io.Reader) (uint64, error) { - var b [8]byte - _, err := io.ReadFull(reader, b[:]) - return binary.BigEndian.Uint64(b[:]), err -} - -func ReadInt64(reader io.Reader) (int64, error) { - b, err := ReadUint64(reader) - return int64(b), err -} - -func ReadString(reader io.Reader) (string, error) { - var builder strings.Builder - for { - b, err := ReadByte(reader) - if err != nil { - return "", err - } - if b == 0 { - return builder.String(), nil - } - builder.WriteByte(b) - } -} - -func WriteByte(writer io.Writer, value byte) (int, error) { - var b [1]byte - b[0] = value - return writer.Write(b[:]) -} - -func WriteInt8(writer io.Writer, value int8) (int, error) { - return WriteByte(writer, byte(value)) -} - -func WriteUint16(writer io.Writer, value uint16) (int, error) { - var b [2]byte - binary.BigEndian.PutUint16(b[:], value) - return writer.Write(b[:]) -} - -func WriteInt16(writer io.Writer, value int16) (int, error) { - return WriteUint16(writer, uint16(value)) -} - -func WriteUint32(writer io.Writer, value uint32) (int, error) { - var b [4]byte - binary.BigEndian.PutUint32(b[:], value) - return writer.Write(b[:]) -} - -func WriteInt32(writer io.Writer, value int32) (int, error) { - return WriteUint32(writer, uint32(value)) -} - -func WriteUint64(writer io.Writer, value uint64) (int, error) { - var b [8]byte - binary.BigEndian.PutUint64(b[:], value) - return writer.Write(b[:]) -} - -func WriteInt64(writer io.Writer, value int64) (int, error) { - return WriteUint64(writer, uint64(value)) -} - -func WriteString(writer io.Writer, value string) (int, error) { - _, err := writer.Write([]byte(value)) - if err != nil { - return 0, err - } - _, err = WriteByte(writer, 0) - if err != nil { - return len(value), err - } - return len(value) + 1, nil -} diff --git a/lib/gat/protocol/mod.go b/lib/gat/protocol/mod.go deleted file mode 100644 index c8a833273da82a631f75dd51f716c7882b5fcc5d..0000000000000000000000000000000000000000 --- a/lib/gat/protocol/mod.go +++ /dev/null @@ -1,135 +0,0 @@ -package protocol - -import ( - "fmt" - "io" -) - -// codegen: modify for debug only - -type Packet interface { - Read(reader io.Reader) error - Write(writer io.Writer) (int, error) -} - -// ReadFrontend switches on frontend packet identifiers and returns the matching packet -// DO NOT call this function if the packet in queue does not have an identifier -func ReadFrontend(reader io.Reader) (packet Packet, err error) { - var identifier byte - identifier, err = ReadByte(reader) - if err != nil { - return - } - - switch identifier { - case byte('p'): - packet = new(AuthenticationResponse) - case byte('B'): - packet = new(Bind) - case byte('C'): - packet = new(Close) - case byte('d'): - packet = new(CopyData) - case byte('c'): - packet = new(CopyDone) - case byte('f'): - packet = new(CopyFail) - case byte('D'): - packet = new(Describe) - case byte('E'): - packet = new(Execute) - case byte('H'): - packet = new(Flush) - case byte('F'): - packet = new(FunctionCall) - case byte('P'): - packet = new(Parse) - case byte('Q'): - packet = new(Query) - case byte('S'): - packet = new(Sync) - case byte('X'): - packet = new(Terminate) - default: - err = fmt.Errorf("no such packet with identifier 0x%x in Frontend", identifier) - return - } - - err = packet.Read(reader) - if err != nil { - return - } - - return -} - -// ReadBackend switches on backend packet identifier and returns the matching packet -// DO NOT call this function if the packet in queue does not have an identifier -func ReadBackend(reader io.Reader) (packet Packet, err error) { - var identifier byte - identifier, err = ReadByte(reader) - if err != nil { - return - } - - switch identifier { - case byte('R'): - packet = new(Authentication) - case byte('K'): - packet = new(BackendKeyData) - case byte('2'): - packet = new(BindComplete) - case byte('3'): - packet = new(CloseComplete) - case byte('C'): - packet = new(CommandComplete) - case byte('W'): - packet = new(CopyBothResponse) - case byte('d'): - packet = new(CopyData) - case byte('c'): - packet = new(CopyDone) - case byte('G'): - packet = new(CopyInResponse) - case byte('H'): - packet = new(CopyOutResponse) - case byte('D'): - packet = new(DataRow) - case byte('I'): - packet = new(EmptyQueryResponse) - case byte('E'): - packet = new(ErrorResponse) - case byte('V'): - packet = new(FunctionCallResponse) - case byte('v'): - packet = new(NegotiateProtocolVersion) - case byte('n'): - packet = new(NoData) - case byte('N'): - packet = new(NoticeResponse) - case byte('A'): - packet = new(NotificationResponse) - case byte('t'): - packet = new(ParameterDescription) - case byte('S'): - packet = new(ParameterStatus) - case byte('1'): - packet = new(ParseComplete) - case byte('s'): - packet = new(PortalSuspended) - case byte('Z'): - packet = new(ReadyForQuery) - case byte('T'): - packet = new(RowDescription) - default: - err = fmt.Errorf("no such packet with identifier 0x%x in Backend", identifier) - return - } - - err = packet.Read(reader) - if err != nil { - return - } - - return -} diff --git a/lib/gat/protocol/pg_error/error.go b/lib/gat/protocol/pg_error/error.go deleted file mode 100644 index e3a7647b3a609ca20239c37f106b9a9d11f93633..0000000000000000000000000000000000000000 --- a/lib/gat/protocol/pg_error/error.go +++ /dev/null @@ -1,467 +0,0 @@ -package pg_error - -import ( - "fmt" - "gfx.cafe/gfx/pggat/lib/gat/protocol" - "strconv" -) - -// error codes format. See https://www.postgresql.org/docs/current/protocol-error-fields.html - -type Severity string - -const ( - Err Severity = "ERROR" - Fatal = "FATAL" - Panic = "PANIC" - Warn = "WARNING" - Notice = "NOTICE" - Debug = "DEBUG" - Info = "INFO" - Log = "LOG" -) - -type Code string - -const ( - SuccessfulCompletion Code = "00000" - Warning = "01000" - DynamicResultSetsReturned = "0100C" - ImplicitZeroBitPadding = "01008" - NullValueEliminatedInSetFunction = "01003" - PrivilegeNotGranted = "01007" - PrivilegeNotRevoked = "01006" - WarnStringDataRightTruncation = "01004" - DeprecatedFeature = "01P01" - NoData = "02000" - NoAdditionalDynamicResultSetsReturned = "02001" - SqlStatementNotYetComplete = "03000" - ConnectionException = "08000" - ConnectionDoesNotExist = "08003" - ConnectionFailure = "08006" - SqlclientUnableToEstablishSqlconnection = "08001" - SqlserverRejectedEstablishmentOfSqlconnection = "08004" - TransactionResolutionUnknown = "08007" - ProtocolViolation = "08P01" - TriggeredActionException = "09000" - FeatureNotSupported = "0A000" - InvalidTransactionInitiation = "0B000" - LocatorException = "0F000" - InvalidLocatorSpecification = "0F001" - InvalidGrantor = "0L000" - InvalidGrantOperation = "0LP01" - InvalidRoleSpecification = "0P000" - DiagnosticsException = "0Z000" - StackedDiagnosticsAccessedWithoutActiveHandler = "0Z002" - CaseNotFound = "20000" - CardinalityViolation = "21000" - DataException = "22000" - ArraySubscriptError = "2202E" - CharacterNotInRepertoire = "22021" - DatetimeFieldOverflow = "22008" - DivisionByZero = "22012" - ErrorInAssignment = "22005" - EscapeCharacterConflict = "2200B" - IndicatorOverflow = "22022" - IntervalFieldOverflow = "22015" - InvalidArgumentForLogarithm = "2201E" - InvalidArgumentForNtileFunction = "22014" - InvalidArgumentForNthValueFunction = "22016" - InvalidArgumentForPowerFunction = "2201F" - InvalidArgumentForWidthBucketFunction = "2201G" - InvalidCharacterValueForCast = "22018" - InvalidDatetimeFormat = "22007" - InvalidEscapeCharacter = "22019" - InvalidEscapeOctet = "2200D" - InvalidEscapeSequence = "22025" - NonstandardUseOfEscapeCharacter = "22P06" - InvalidIndicatorParameterValue = "22010" - InvalidParameterValue = "22023" - InvalidPrecedingOrFollowingSize = "22013" - InvalidRegularExpression = "2201B" - InvalidRowCountInLimitClause = "2201W" - InvalidRowCountInResultOffsetClause = "2201X" - InvalidTablesampleArgument = "2202H" - InvalidTablesampleRepeat = "2202G" - InvalidTimeZoneDisplacementValue = "22009" - InvalidUseOfEscapeCharacter = "2200C" - MostSpecificTypeMismatch = "2200G" - DataExceptionNullValueNotAllowed = "22004" - NullValueNoIndicatorParameter = "22002" - NumericValueOutOfRange = "22003" - SequenceGeneratorLimitExceeded = "2200H" - StringDataLengthMismatch = "22026" - DataExceptionStringDataRightTruncation = "22001" - SubstringError = "22011" - TrimError = "22027" - UnterminatedCString = "22024" - ZeroLengthCharacterString = "2200F" - FloatingPointException = "22P01" - InvalidTextRepresentation = "22P02" - InvalidBinaryRepresentation = "22P03" - BadCopyFileFormat = "22P04" - UntranslatableCharacter = "22P05" - NotAnXmlDocument = "2200L" - InvalidXmlDocument = "2200M" - InvalidXmlContent = "2200N" - InvalidXmlComment = "2200S" - InvalidXmlProcessingInstruction = "2200T" - DuplicateJsonObjectKeyValue = "22030" - InvalidArgumentForSqlJsonDatetimeFunction = "22031" - InvalidJsonText = "22032" - InvalidSqlJsonSubscript = "22033" - MoreThanOneSqlJsonItem = "22034" - NoSqlJsonItem = "22035" - NonNumericSqlJsonItem = "22036" - NonUniqueKeysInAJsonObject = "22037" - SingletonSqlJsonItemRequired = "22038" - SqlJsonArrayNotFound = "22039" - SqlJsonMemberNotFound = "2203A" - SqlJsonNumberNotFound = "2203B" - SqlJsonObjectNotFound = "2203C" - TooManyJsonArrayElements = "2203D" - TooManyJsonObjectMembers = "2203E" - SqlJsonScalarRequired = "2203F" - IntegrityConstraintViolation = "23000" - RestrictViolation = "23001" - NotNullViolation = "23502" - ForeignKeyViolation = "23503" - UniqueViolation = "23505" - CheckViolation = "23514" - ExclusionViolation = "23P01" - InvalidCursorState = "24000" - InvalidTransactionState = "25000" - ActiveSqlTransaction = "25001" - BranchTransactionAlreadyActive = "25002" - HeldCursorRequiresSameIsolationLevel = "25008" - InappropriateAccessModeForBranchTransaction = "25003" - InappropriateIsolationLevelForBranchTransaction = "25004" - NoActiveSqlTransactionForBranchTransaction = "25005" - ReadOnlySqlTransaction = "25006" - SchemaAndDataStatementMixingNotSupported = "25007" - NoActiveSqlTransaction = "25P01" - InFailedSqlTransaction = "25P02" - IdleInTransactionSessionTimeout = "25P03" - InvalidSqlStatementName = "26000" - TriggeredDataChangeViolation = "27000" - InvalidAuthorizationSpecification = "28000" - InvalidPassword = "28P01" - DependentPrivilegeDescriptorsStillExist = "2B000" - DependentObjectsStillExist = "2BP01" - InvalidTransactionTermination = "2D000" - SqlRoutineException = "2F000" - FunctionExecutedNoReturnStatement = "2F005" - SQLRoutineExceptionModifyingSqlDataNotPermitted = "2F002" - SQLRoutineExceptionProhibitedSqlStatementAttempted = "2F003" - SQLRoutineExceptionReadingSqlDataNotPermitted = "2F004" - InvalidCursorName = "34000" - ExternalRoutineException = "38000" - ContainingSqlNotPermitted = "38001" - ExternalRoutineExceptionModifyingSqlDataNotPermitted = "38002" - ExternalRoutineExceptionProhibitedSqlStatementAttempted = "38003" - ExternalRoutineExceptionReadingSqlDataNotPermitted = "38004" - ExternalRoutineInvocationException = "39000" - InvalidSqlstateReturned = "39001" - ERIENullValueNotAllowed = "39004" - TriggerProtocolViolated = "39P01" - SrfProtocolViolated = "39P02" - EventTriggerProtocolViolated = "39P03" - SavepointException = "3B000" - InvalidSavepointSpecification = "3B001" - InvalidCatalogName = "3D000" - InvalidSchemaName = "3F000" - TransactionRollback = "40000" - TransactionIntegrityConstraintViolation = "40002" - SerializationFailure = "40001" - StatementCompletionUnknown = "40003" - DeadlockDetected = "40P01" - SyntaxErrorOrAccessRuleViolation = "42000" - SyntaxError = "42601" - InsufficientPrivilege = "42501" - CannotCoerce = "42846" - GroupingError = "42803" - WindowingError = "42P20" - InvalidRecursion = "42P19" - InvalidForeignKey = "42830" - InvalidName = "42602" - NameTooLong = "42622" - ReservedName = "42939" - DatatypeMismatch = "42804" - IndeterminateDatatype = "42P18" - CollationMismatch = "42P21" - IndeterminateCollation = "42P22" - WrongObjectType = "42809" - GeneratedAlways = "428C9" - UndefinedColumn = "42703" - UndefinedFunction = "42883" - UndefinedTable = "42P01" - UndefinedParameter = "42P02" - UndefinedObject = "42704" - DuplicateColumn = "42701" - DuplicateCursor = "42P03" - DuplicateDatabase = "42P04" - DuplicateFunction = "42723" - DuplicatePreparedStatement = "42P05" - DuplicateSchema = "42P06" - DuplicateTable = "42P07" - DuplicateAlias = "42712" - DuplicateObject = "42710" - AmbiguousColumn = "42702" - AmbiguousFunction = "42725" - AmbiguousParameter = "42P08" - AmbiguousAlias = "42P09" - InvalidColumnReference = "42P10" - InvalidColumnDefinition = "42611" - InvalidCursorDefinition = "42P11" - InvalidDatabaseDefinition = "42P12" - InvalidFunctionDefinition = "42P13" - InvalidPreparedStatementDefinition = "42P14" - InvalidSchemaDefinition = "42P15" - InvalidTableDefinition = "42P16" - InvalidObjectDefinition = "42P17" - WithCheckOptionViolation = "44000" - InsufficientResources = "53000" - DiskFull = "53100" - OutOfMemory = "53200" - TooManyConnections = "53300" - ConfigurationLimitExceeded = "53400" - ProgramLimitExceeded = "54000" - StatementTooComplex = "54001" - TooManyColumns = "54011" - TooManyArguments = "54023" - ObjectNotInPrerequisiteState = "55000" - ObjectInUse = "55006" - CantChangeRuntimeParam = "55P02" - LockNotAvailable = "55P03" - UnsafeNewEnumValueUsage = "55P04" - OperatorIntervention = "57000" - QueryCanceled = "57014" - AdminShutdown = "57P01" - CrashShutdown = "57P02" - CannotConnectNow = "57P03" - DatabaseDropped = "57P04" - IdleSessionTimeout = "57P05" - SystemError = "58000" - IoError = "58030" - UndefinedFile = "58P01" - DuplicateFile = "58P02" - SnapshotTooOld = "72000" - ConfigFileError = "F0000" - LockFileExists = "F0001" - FdwError = "HV000" - FdwColumnNameNotFound = "HV005" - FdwDynamicParameterValueNeeded = "HV002" - FdwFunctionSequenceError = "HV010" - FdwInconsistentDescriptorInformation = "HV021" - FdwInvalidAttributeValue = "HV024" - FdwInvalidColumnName = "HV007" - FdwInvalidColumnNumber = "HV008" - FdwInvalidDataType = "HV004" - FdwInvalidDataTypeDescriptors = "HV006" - FdwInvalidDescriptorFieldIdentifier = "HV091" - FdwInvalidHandle = "HV00B" - FdwInvalidOptionIndex = "HV00C" - FdwInvalidOptionName = "HV00D" - FdwInvalidStringLengthOrBufferLength = "HV090" - FdwInvalidStringFormat = "HV00A" - FdwInvalidUseOfNullPointer = "HV009" - FdwTooManyHandles = "HV014" - FdwOutOfMemory = "HV001" - FdwNoSchemas = "HV00P" - FdwOptionNameNotFound = "HV00J" - FdwReplyHandle = "HV00K" - FdwSchemaNotFound = "HV00Q" - FdwTableNotFound = "HV00R" - FdwUnableToCreateExecution = "HV00L" - FdwUnableToCreateReply = "HV00M" - FdwUnableToEstablishConnection = "HV00N" - PlpgsqlError = "P0000" - RaiseException = "P0001" - NoDataFound = "P0002" - TooManyRows = "P0003" - AssertFailure = "P0004" - InternalError = "XX000" - DataCorrupted = "XX001" - IndexCorrupted = "XX002" -) - -type Error struct { - Severity Severity - Code Code - Message string - Detail string - Hint string - Position int - InternalPosition int - InternalQuery string - Where string - Schema string - Table string - Column string - DataType string - Constraint string - File string - Line int - Routine string -} - -func (E *Error) Read(pkt *protocol.ErrorResponse) { - for _, field := range pkt.Fields.Responses { - switch field.Code { - case byte('S'): - E.Severity = Severity(field.Value) - case byte('C'): - E.Code = Code(field.Value) - case byte('M'): - E.Message = field.Value - case byte('D'): - E.Detail = field.Value - case byte('H'): - E.Hint = field.Value - case byte('P'): - E.Position, _ = strconv.Atoi(field.Value) - case byte('p'): - E.InternalPosition, _ = strconv.Atoi(field.Value) - case byte('q'): - E.InternalQuery = field.Value - case byte('W'): - E.Where = field.Value - case byte('s'): - E.Schema = field.Value - case byte('t'): - E.Table = field.Value - case byte('c'): - E.Column = field.Value - case byte('d'): - E.DataType = field.Value - case byte('n'): - E.Constraint = field.Value - case byte('F'): - E.File = field.Value - case byte('L'): - E.Line, _ = strconv.Atoi(field.Value) - case byte('R'): - E.Routine = field.Value - } - } -} - -func (E *Error) Packet() *protocol.ErrorResponse { - var fields []protocol.FieldsErrorResponseResponses - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('S'), - Value: string(E.Severity), - }, protocol.FieldsErrorResponseResponses{ - Code: byte('C'), - Value: string(E.Code), - }, protocol.FieldsErrorResponseResponses{ - Code: byte('M'), - Value: E.Message, - }) - if E.Detail != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('D'), - Value: E.Detail, - }) - } - if E.Hint != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('H'), - Value: E.Hint, - }) - } - if E.Position != 0 { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('P'), - Value: strconv.Itoa(E.Position), - }) - } - if E.InternalPosition != 0 { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('p'), - Value: strconv.Itoa(E.InternalPosition), - }) - } - if E.InternalQuery != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('q'), - Value: E.InternalQuery, - }) - } - if E.Where != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('W'), - Value: E.Where, - }) - } - if E.Schema != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('s'), - Value: E.Schema, - }) - } - if E.Table != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('t'), - Value: E.Table, - }) - } - if E.Column != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('c'), - Value: E.Column, - }) - } - if E.DataType != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('d'), - Value: E.DataType, - }) - } - if E.Constraint != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('n'), - Value: E.Constraint, - }) - } - if E.File != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('F'), - Value: E.File, - }) - } - if E.Line != 0 { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('L'), - Value: strconv.Itoa(E.Line), - }) - } - if E.Routine != "" { - fields = append(fields, protocol.FieldsErrorResponseResponses{ - Code: byte('R'), - Value: E.Routine, - }) - } - fields = append(fields, protocol.FieldsErrorResponseResponses{}) - pkt := new(protocol.ErrorResponse) - pkt.Fields.Responses = fields - return pkt -} - -func (E *Error) Error() string { - return fmt.Sprintf("%s: %s", E.Severity, E.Message) -} - -func IntoPacket(err error) *protocol.ErrorResponse { - switch e := err.(type) { - case *Error: - return e.Packet() - default: - er := Error{ - Severity: Err, - Code: InternalError, - Message: e.Error(), - } - return er.Packet() - } -} diff --git a/lib/gat/protocol/shared.go b/lib/gat/protocol/shared.go deleted file mode 100644 index f3c8a66e0e3affd066c8d9802455c27c42a52cba..0000000000000000000000000000000000000000 --- a/lib/gat/protocol/shared.go +++ /dev/null @@ -1,136 +0,0 @@ -package protocol - -import ( - "bytes" - "gfx.cafe/util/go/bufpool" - "io" -) - -// codegen: modify for debug only - -var _ bytes.Buffer -var _ io.Reader - -type FieldsCopyData struct { - Data []byte -} - -func (T *FieldsCopyData) Read(payloadLength int, reader io.Reader) (err error) { - DataLength := payloadLength - T.Data = make([]byte, int(DataLength)) - for i := 0; i < int(DataLength); i++ { - T.Data[i], err = ReadByte(reader) - if err != nil { - return - } - } - return -} - -func (T *FieldsCopyData) Write(writer io.Writer) (length int, err error) { - var temp int - for _, v := range T.Data { - temp, err = WriteByte(writer, v) - if err != nil { - return - } - length += temp - } - _ = temp - return -} - -type CopyData struct { - Fields FieldsCopyData -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CopyData) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CopyData) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('d')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CopyData)(nil) - -type FieldsCopyDone struct { -} - -func (T *FieldsCopyDone) Read(payloadLength int, reader io.Reader) (err error) { - return -} - -func (T *FieldsCopyDone) Write(writer io.Writer) (length int, err error) { - var temp int - _ = temp - return -} - -type CopyDone struct { - Fields FieldsCopyDone -} - -// Read reads all but the packet identifier -// WARNING: This packet DOES have an identifier. Call protocol.Read or trim the identifier first! -func (T *CopyDone) Read(reader io.Reader) (err error) { - var length int32 - length, err = ReadInt32(reader) - if err != nil { - return - } - return T.Fields.Read(int(length-4), reader) -} - -func (T *CopyDone) Write(writer io.Writer) (length int, err error) { - buf := bufpool.Get(0) - buf.Reset() - defer bufpool.Put(buf) - length, err = T.Fields.Write(buf) - if err != nil { - length = 0 - return - } - _, err = WriteByte(writer, byte('c')) - if err != nil { - length = 1 - return - } - _, err = WriteInt32(writer, int32(length)+4) - if err != nil { - length += 5 - return - } - length += 5 - _, err = writer.Write(buf.Bytes()) - return -} - -var _ Packet = (*CopyDone)(nil) diff --git a/lib/gat/readme.md b/lib/gat/readme.md deleted file mode 100644 index 7ea18a8f4579ab0eb2793e7097c161c7b8d4e011..0000000000000000000000000000000000000000 --- a/lib/gat/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -## business logic - -gatling is the main proxy and router, but other pool routers may be added in the future following the same interfaces - -protocol is code generated! every file in there. don't edit it! diff --git a/lib/gat2/pools/session.go b/lib/gat2/pools/session.go deleted file mode 100644 index 9573007bec42c8462418d0396153c99230db1d1d..0000000000000000000000000000000000000000 --- a/lib/gat2/pools/session.go +++ /dev/null @@ -1,97 +0,0 @@ -package pools - -import ( - "sync" - - "github.com/google/uuid" - - "gfx.cafe/gfx/pggat/lib/gat2" - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -type Session struct { - id uuid.UUID - - free []gat2.Sink - inuse map[uuid.UUID]gat2.Sink - mu sync.RWMutex -} - -func NewSession(sinks []gat2.Sink) *Session { - return &Session{ - id: uuid.New(), - - free: sinks, - inuse: make(map[uuid.UUID]gat2.Sink), - } -} - -func (T *Session) ID() uuid.UUID { - return T.id -} - -func (T *Session) bySource(src gat2.Source) gat2.Sink { - T.mu.RLock() - defer T.mu.RUnlock() - sink, _ := T.inuse[src.ID()] - return sink -} - -func (T *Session) cleanup(src gat2.Source) { - id := src.ID() - - T.mu.Lock() - defer T.mu.Unlock() - - sink := T.inuse[id] - delete(T.inuse, id) - T.free = append(T.free, sink) -} - -func (T *Session) assign(src gat2.Source) gat2.Sink { - T.mu.Lock() - defer T.mu.Unlock() - - if len(T.free) > 0 { - // just grab free - sink := T.free[len(T.free)-1] - T.free = T.free[:len(T.free)-1] - T.inuse[src.ID()] = sink - - return sink - } - - return nil -} - -func (T *Session) Route(w gat2.Work) iter.Iter[chan<- gat2.Work] { - src := w.Source() - sink := T.bySource(src) - if sink != nil { - return sink.Route(w) - } - sink = T.assign(src) - if sink != nil { - return sink.Route(w) - } - return iter.Empty[chan<- gat2.Work]() -} - -func (T *Session) KillSource(source gat2.Source) { - id := source.ID() - - T.mu.Lock() - defer T.mu.Unlock() - - sink, ok := T.inuse[id] - if !ok { - return - } - delete(T.inuse, id) - sink.KillSource(source) - - // return sink to free pool - T.free = append(T.free, sink) -} - -var _ gat2.Sink = (*Session)(nil) diff --git a/lib/gat2/pools/transaction.go b/lib/gat2/pools/transaction.go deleted file mode 100644 index f10818c3d312b1c04a8ec268f8bb2f107a3023c5..0000000000000000000000000000000000000000 --- a/lib/gat2/pools/transaction.go +++ /dev/null @@ -1,43 +0,0 @@ -package pools - -import ( - "github.com/google/uuid" - - "gfx.cafe/gfx/pggat/lib/gat2" - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -type Transaction struct { - id uuid.UUID - sinks []gat2.Sink -} - -func NewTransaction(sinks []gat2.Sink) *Transaction { - return &Transaction{ - id: uuid.New(), - sinks: sinks, - } -} - -func (T *Transaction) ID() uuid.UUID { - return T.id -} - -func (T *Transaction) Route(w gat2.Work) iter.Iter[chan<- gat2.Work] { - return iter.Flatten( - iter.Map( - iter.Slice(T.sinks), - func(s gat2.Sink) iter.Iter[chan<- gat2.Work] { - return s.Route(w) - }, - ), - ) -} - -func (T *Transaction) KillSource(source gat2.Source) { - for _, sink := range T.sinks { - sink.KillSource(source) - } -} - -var _ gat2.Sink = (*Transaction)(nil) diff --git a/lib/gat2/router.go b/lib/gat2/router.go deleted file mode 100644 index b572efe556748d22d49ad31415a1c02d35977013..0000000000000000000000000000000000000000 --- a/lib/gat2/router.go +++ /dev/null @@ -1,4 +0,0 @@ -package gat2 - -type Router interface { -} diff --git a/lib/gat2/routers/v0/router.go b/lib/gat2/routers/v0/router.go deleted file mode 100644 index 5d4f245afe0f843cdf611d1b028ec5dbcaf9efdb..0000000000000000000000000000000000000000 --- a/lib/gat2/routers/v0/router.go +++ /dev/null @@ -1,104 +0,0 @@ -package routers - -import ( - "sync" - - "gfx.cafe/gfx/pggat/lib/gat2" - "gfx.cafe/gfx/pggat/lib/util/iter" - "gfx.cafe/gfx/pggat/lib/util/race" -) - -type Router struct { - sinks []gat2.Sink - sources []gat2.Source - mu sync.RWMutex -} - -func NewRouter(sinks []gat2.Sink, sources []gat2.Source) *Router { - return &Router{ - sinks: sinks, - sources: sources, - } -} - -func (T *Router) removesrc(idx int) gat2.Source { - T.mu.Lock() - defer T.mu.Unlock() - source := T.sources[idx] - for i := idx; i < len(T.sources)-1; i++ { - T.sources[i] = T.sources[i+1] - } - T.sources = T.sources[:len(T.sources)-1] - return source -} - -// srcdead should be called to clean up resources related to a source when the source dies -func (T *Router) srcdead(idx int) { - source := T.removesrc(idx) - - for _, sink := range T.sinks { - sink.KillSource(source) - } -} - -// srcrecv is basically a huge select statement on all clients.Out() -func (T *Router) srcrecv() (work gat2.Work, idx int, ok bool) { - T.mu.RLock() - defer T.mu.RUnlock() - if len(T.sources) == 0 { - return nil, -1, true - } - - // receive work - return race.Recv( - iter.Map( - iter.Slice(T.sources), - func(source gat2.Source) <-chan gat2.Work { - return source.Out() - }, - ), - ) -} - -// recv receives the next unit of work from the sources -func (T *Router) recv() gat2.Work { - for { - work, idx, ok := T.srcrecv() - if ok { - return work - } - - // T.sources[idx] died, remove it - T.srcdead(idx) - } -} - -// send tries to get a unit of work done -func (T *Router) send(work gat2.Work) { - // send work - race.Send( - iter.Flatten( - iter.Map( - iter.Slice(T.sinks), - func(sink gat2.Sink) iter.Iter[chan<- gat2.Work] { - return sink.Route(work) - }, - ), - ), - work, - ) -} - -func (T *Router) route() { - work := T.recv() - if work == nil { - return - } - T.send(work) -} - -func (T *Router) Run() { - for { - T.route() - } -} diff --git a/lib/gat2/routers/v0/router_test.go b/lib/gat2/routers/v0/router_test.go deleted file mode 100644 index 26440c84e4e3c70c267b508a058d7e340de19663..0000000000000000000000000000000000000000 --- a/lib/gat2/routers/v0/router_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package routers - -import ( - "log" - "testing" - "time" - - "github.com/google/uuid" - - "gfx.cafe/gfx/pggat/lib/gat2" - "gfx.cafe/gfx/pggat/lib/gat2/pools" - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -type DummyWork struct { - id uuid.UUID - src gat2.Source -} - -func NewDummyWork(src gat2.Source) *DummyWork { - return &DummyWork{ - id: uuid.New(), - src: src, - } -} - -func (T *DummyWork) ID() uuid.UUID { - return T.id -} - -func (T *DummyWork) Source() gat2.Source { - return T.src -} - -var _ gat2.Work = (*DummyWork)(nil) - -type DummySink struct { - id uuid.UUID - in chan gat2.Work -} - -func NewDummySink() *DummySink { - s := &DummySink{ - id: uuid.New(), - in: make(chan gat2.Work), - } - go func() { - for { - w := <-s.in - log.Println("received work", w.ID()) - } - }() - return s -} - -func (T *DummySink) ID() uuid.UUID { - return T.id -} - -func (T *DummySink) Route(_ gat2.Work) iter.Iter[chan<- gat2.Work] { - return iter.Single[chan<- gat2.Work](T.in) -} - -func (T *DummySink) KillSource(_ gat2.Source) {} - -var _ gat2.Sink = (*DummySink)(nil) - -type DummySource struct { - id uuid.UUID - out chan gat2.Work -} - -func NewDummySource() *DummySource { - src := &DummySource{ - id: uuid.New(), - out: make(chan gat2.Work), - } - return src -} - -func (T *DummySource) QueueWork() { - go func() { - T.out <- NewDummyWork(T) - }() -} - -func (T *DummySource) ID() uuid.UUID { - return T.id -} - -func (T *DummySource) Out() <-chan gat2.Work { - return T.out -} - -func (T *DummySource) Close() { - close(T.out) -} - -var _ gat2.Source = (*DummySource)(nil) - -func TestRouter(t *testing.T) { - s1 := NewDummySource() - s2 := NewDummySource() - router := NewRouter( - []gat2.Sink{ - pools.NewSession([]gat2.Sink{ - NewDummySink(), - }), - }, - []gat2.Source{ - s1, - s2, - }, - ) - - s1.QueueWork() - router.route() - s1.Close() - s2.QueueWork() - router.route() - - <-time.After(1 * time.Second) -} diff --git a/lib/gat2/sink.go b/lib/gat2/sink.go deleted file mode 100644 index f4b486b1d04b4b64467df0bbee8aa8f9c92802a2..0000000000000000000000000000000000000000 --- a/lib/gat2/sink.go +++ /dev/null @@ -1,19 +0,0 @@ -package gat2 - -import ( - "github.com/google/uuid" - - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -type Sink interface { - ID() uuid.UUID - - // Route will return an iter.Iter of channels equipped to handle the Work. - // If Sink dies, it should return iter.Empty. If one of the underlying channels dies, they should remain open - // but not accept work. - Route(Work) iter.Iter[chan<- Work] - - // KillSource will be called when a source dies. This can be used to free resources related to the Source - KillSource(Source) -} diff --git a/lib/gat2/source.go b/lib/gat2/source.go deleted file mode 100644 index 81a00615d33491f4530b6aa1197566dcb3f0dbe7..0000000000000000000000000000000000000000 --- a/lib/gat2/source.go +++ /dev/null @@ -1,11 +0,0 @@ -package gat2 - -import ( - "github.com/google/uuid" -) - -type Source interface { - ID() uuid.UUID - - Out() <-chan Work -} diff --git a/lib/gat2/work.go b/lib/gat2/work.go deleted file mode 100644 index 0a65226b7d61ed2763c0edd3a3b08526de7b72e2..0000000000000000000000000000000000000000 --- a/lib/gat2/work.go +++ /dev/null @@ -1,11 +0,0 @@ -package gat2 - -import ( - "github.com/google/uuid" -) - -type Work interface { - ID() uuid.UUID - - Source() Source -} diff --git a/lib/metrics/buckets.go b/lib/metrics/buckets.go deleted file mode 100644 index 7d84ff21a41c74a39b4dc9e1f22a22bea8feeb2e..0000000000000000000000000000000000000000 --- a/lib/metrics/buckets.go +++ /dev/null @@ -1,6 +0,0 @@ -package metrics - -var bucketsUs = []float64{1000, 3000, 5000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000, 150000, 300000, 500000, 5000000, 10000000, 30000000, 45000000, 60000000, 75000000, 100000000, 125000000, 150000000, 200000000, 250000000} - -// var requestDurationHistBucketNs = []float64{1000000, 3000000, 5000000, 10000000, 20000000, 30000000, 40000000, 50000000, 60000000, 70000000, 80000000, 90000000, 100000000, 150000000, 300000000, 500000000, 5000000000, 10000000000, 30000000000, 45000000000, 60000000000} -// var bucketsNs = []float64{100000, 300000, 500000, 1000000, 2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000, 10000000, 15000000, 30000000, 50000000, 500000000, 1000000000, 3000000000, 4500000000, 6000000000} diff --git a/lib/metrics/gat.go b/lib/metrics/gat.go deleted file mode 100644 index 7d245c55d48065fa17b324dd1e057c909e0c1634..0000000000000000000000000000000000000000 --- a/lib/metrics/gat.go +++ /dev/null @@ -1,66 +0,0 @@ -package metrics - -import ( - "os" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -type gatMetrics struct { - ConnectionCounter prometheus.Counter - ConnectionErrorCounter *prometheus.CounterVec - ActiveConnections prometheus.Gauge -} - -func GatMetrics() *gatMetrics { - s.Lock() - defer s.Unlock() - if s.gat == nil { - s.gat = newGatmetrics() - } - return s.gat -} - -func newGatmetrics() *gatMetrics { - hostname := os.Getenv("HOSTNAME") - if hostname == "" { - hostname = "default" - } - o := &gatMetrics{ - ConnectionCounter: promauto.NewCounter(prometheus.CounterOpts{ - Name: "pggat_connection_count_total", - Help: "total number of connections initiated with pggat", - ConstLabels: prometheus.Labels{ - "pod": hostname, - }, - }), - ActiveConnections: promauto.NewGauge(prometheus.GaugeOpts{ - Name: "pggat_current_connection_count", - Help: "number of connections to pggat currently", - ConstLabels: prometheus.Labels{ - "pod": hostname, - }, - }), - } - return o -} - -func RecordAcceptConnectionStatus(err error) { - if !On() { - return - } - g := GatMetrics() - if err != nil { - g.ConnectionErrorCounter.WithLabelValues(err.Error()).Inc() - } - g.ConnectionCounter.Inc() -} - -func RecordActiveConnections(change int) { - if !On() { - return - } - g := GatMetrics() - g.ActiveConnections.Add(float64(change)) -} diff --git a/lib/metrics/metrics.go b/lib/metrics/metrics.go deleted file mode 100644 index 0c6503106ecdf11a168d361a45f8999c277c940d..0000000000000000000000000000000000000000 --- a/lib/metrics/metrics.go +++ /dev/null @@ -1,98 +0,0 @@ -package metrics - -import ( - "sync" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -func On() bool { - return true -} - -var s = &metrics{ - counters: map[string]prometheus.Counter{}, - histos: map[string]prometheus.Histogram{}, - pools: make(map[string]poolMetrics), -} - -type metrics struct { - counters map[string]prometheus.Counter - histos map[string]prometheus.Histogram - pools map[string]poolMetrics - gat *gatMetrics - sync.RWMutex -} - -func Counter(bucket string, name string, help string) prometheus.Counter { - full := bucket + "_" + name - s.Lock() - c, ok := s.counters[full] - if !ok { - c = newCounter(bucket, name, help) - s.counters[full] = c - } - s.Unlock() - return c -} - -func Hist(bucket string, name string, help string) prometheus.Histogram { - full := bucket + "_" + name - s.Lock() - c, ok := s.histos[full] - if !ok { - c = newHistogram(bucket, name, help) - s.histos[full] = c - } - s.Unlock() - return c -} - -func Inc(bucket string, name string, help string) { - if !On() { - return - } - Counter(bucket, name, help).Inc() -} -func Add(bucket string, name string, help string, entry float64) { - if !On() { - return - } - Hist(bucket, name, help).Observe(entry) -} - -func init() { - t1s := time.NewTicker(1 * time.Second) - t15s := time.NewTicker(15 * time.Second) - t1m := time.NewTicker(1 * time.Minute) - t15m := time.NewTicker(15 * time.Minute) - go func() { - for { - select { - case <-t1s.C: - case <-t15s.C: - case <-t1m.C: - case <-t15m.C: - } - } - }() -} - -func newCounter(app string, name string, help string) prometheus.Counter { - return promauto.NewCounter(prometheus.CounterOpts{ - Name: app + "_" + name, - Help: help, - }) -} - -var defaultHist = []float64{0.001, 0.005, 0.01, 0.025, 0.035, 0.045, 0.05, 0.1, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 1, 2.5, 5, 10, 20, 35, 50, 75, 100, 125, 250, 500, 750, 1000, 10000} - -func newHistogram(app string, name string, help string) prometheus.Histogram { - return promauto.NewHistogram(prometheus.HistogramOpts{ - Name: app + "_" + name, - Help: help, - Buckets: defaultHist, - }) -} diff --git a/lib/metrics/pool.go b/lib/metrics/pool.go deleted file mode 100644 index df2a9ca66689d190eec08ebc1c19d72d1aba8b3f..0000000000000000000000000000000000000000 --- a/lib/metrics/pool.go +++ /dev/null @@ -1,163 +0,0 @@ -package metrics - -import ( - "os" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -type poolMetrics struct { - name string - TxLatency *prometheus.HistogramVec - QueryLatency *prometheus.HistogramVec - TxErrorCounts *prometheus.CounterVec - QueryErrorCounts *prometheus.CounterVec - WaitLatency *prometheus.HistogramVec - ReceivedBytes *prometheus.CounterVec - SentBytes *prometheus.CounterVec -} - -func PoolMetrics(db string, user string) poolMetrics { - s.Lock() - defer s.Unlock() - pool, ok := s.pools[db+user] - if !ok { - pool = newPoolMetrics(db, user) - s.pools[db+user] = pool - } - return pool -} - -func newPoolMetrics(db string, user string) poolMetrics { - hostname := os.Getenv("HOSTNAME") - if hostname == "" { - hostname = "default" - } - o := poolMetrics{ - name: db + user, - TxLatency: promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "pggat_db_transaction_latency", - Help: "transaction latency", - Buckets: bucketsUs, - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{}), - QueryLatency: promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "pggat_db_query_latency", - Help: "query latency", - Buckets: bucketsUs, - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{}), - TxErrorCounts: promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "pggat_db_transaction_error_count_total", - Help: "transaction latency", - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{"error"}), - QueryErrorCounts: promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "pggat_db_query_error_count_total", - Help: "transaction latency", - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{"error"}), - WaitLatency: promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "pggat_db_wait_latency", - Help: "wait latency", - Buckets: bucketsUs, - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{}), - ReceivedBytes: promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "pggat_received_bytes_total", - Help: "total number of bytes received", - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{}), - SentBytes: promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "pggat_sent_bytes_total", - Help: "total number of bytes received", - ConstLabels: prometheus.Labels{ - "db": db, - "user": user, - "pod": hostname, - }, - }, []string{}), - } - return o -} - -func RecordBytes(db string, user string, sent, received int64) { - if !On() { - return - } - p := PoolMetrics(db, user) - p.SentBytes.WithLabelValues().Add(float64(sent)) - p.ReceivedBytes.WithLabelValues().Add(float64(received)) -} - -func RecordQueryTime(db string, user string, dur time.Duration) { - if !On() { - return - } - p := PoolMetrics(db, user) - p.QueryLatency.WithLabelValues().Observe(float64(dur.Nanoseconds())) -} - -func RecordTransactionTime(db string, user string, dur time.Duration) { - if !On() { - return - } - p := PoolMetrics(db, user) - p.TxLatency.WithLabelValues().Observe(float64(dur.Nanoseconds())) -} - -func RecordWaitTime(db string, user string, dur time.Duration) { - if !On() { - return - } - p := PoolMetrics(db, user) - p.WaitLatency.WithLabelValues().Observe(float64(dur.Nanoseconds())) -} - -func RecordTransactionError(db string, user string, err error) { - if !On() { - return - } - p := PoolMetrics(db, user) - if err == nil { - return - } - p.TxErrorCounts.WithLabelValues(err.Error()).Inc() -} - -func RecordQueryError(db string, user string, err error) { - if !On() { - return - } - p := PoolMetrics(db, user) - if err == nil { - return - } - p.QueryErrorCounts.WithLabelValues(err.Error()).Inc() -} diff --git a/lib/parse/README.md b/lib/parse/README.md deleted file mode 100644 index 12f54a24441dfb4f8621853478f0daf1e48f5d7c..0000000000000000000000000000000000000000 --- a/lib/parse/README.md +++ /dev/null @@ -1,5 +0,0 @@ -parsing sql in a single pass since 4:21pm on thursday - -a basic SQL query parser (specifically postgres) - -this is **not** intended to parse arguments correctly or verify sql commands, the only goal of this is to split an sql query into its statements in a single pass. Arguments are only split by spaces and may be wrong if they have operators, function calls, etc. \ No newline at end of file diff --git a/lib/parse/parse.go b/lib/parse/parse.go deleted file mode 100644 index bb8301d8e154b46d2388153f8ba92634fdfa8c0d..0000000000000000000000000000000000000000 --- a/lib/parse/parse.go +++ /dev/null @@ -1,303 +0,0 @@ -package parse - -import ( - "errors" - "fmt" - "unicode" - "unicode/utf8" -) - -type Command struct { - // start index in parsed SQL string - Begin int - // end index in parsed SQL string - End int - // the subtext SQL. Same as src[Begin:End] - SQL string - Command string - Arguments []string -} - -type reader struct { - v string - p int -} - -var EndOfSQL = errors.New("end of sql") -var EndOfStatement = errors.New("end of statement") -var NotThisToken = errors.New("end of token") -var UnexpectedCharacter = errors.New("unexpected character") - -func newUnexpectedCharacter(c rune) error { - return fmt.Errorf("%w: '%c'", UnexpectedCharacter, c) -} - -func (r *reader) nextRune() (rune, bool) { - if r.p >= len(r.v) { - return '-', false - } - c, l := utf8.DecodeRuneInString(r.v[r.p:]) - r.p += l - return c, true -} - -func (r *reader) nextComment() error { - c, ok := r.nextRune() - if !ok { - return EndOfSQL - } - switch { - case c == ';': - return EndOfStatement - case c == '-': - // we good - default: - return NotThisToken - } - - return r.nextString("\n") -} - -func (r *reader) nextMultiLineComment() error { - c, ok := r.nextRune() - if !ok { - return EndOfSQL - } - switch { - case c == ';': - return EndOfStatement - case c == '*': - // we good - default: - return NotThisToken - } - - return r.nextString("*/") -} - -func (r *reader) nextIdentifier() (string, error) { - start := r.p - - for { - pre := r.p - - c, ok := r.nextRune() - if !ok { - break - } - switch { - case c == ';': - return r.v[start:pre], EndOfStatement - case unicode.IsSpace(c): - if pre == start { - start = r.p - continue - } - - // this identifier is done - return r.v[start:pre], nil - case unicode.IsDigit(c): - if pre == start { - return "", newUnexpectedCharacter(c) - } - fallthrough - case unicode.IsLetter(c), c == '_', c == '$': - case c == '-' && pre == start: - if r.nextComment() != nil { - return "", newUnexpectedCharacter(c) - } - start = r.p - case c == '/' && pre == start: - if r.nextMultiLineComment() != nil { - return "", newUnexpectedCharacter(c) - } - start = r.p - default: - return "", newUnexpectedCharacter(c) - } - } - - return r.v[start:r.p], EndOfSQL -} - -func (r *reader) nextString(delim string) error { - di := 0 - escaping := false - for { - d, l := utf8.DecodeRuneInString(delim[di:]) - c, ok := r.nextRune() - if !ok { - return EndOfSQL - } - - switch c { - case d: - di += l - if di >= len(delim) { - di = 0 - if !escaping { - return nil - } - } - escaping = false - case '\\': - escaping = true - di = 0 - default: - di = 0 - escaping = false - } - } -} - -func (r *reader) nextDollarIdentifier() error { - start := r.p - for { - pre := r.p - c, ok := r.nextRune() - if !ok { - return EndOfSQL - } - - switch { - case c == ';': - return EndOfStatement - case unicode.IsDigit(c): - if start == pre { - return NotThisToken - } - case unicode.IsLetter(c), c == '_': - case c == '$': - return nil - default: - return NotThisToken - } - } -} - -func (r *reader) nextArgument() (string, error) { - // just read everything up to spaces or the end token, being mindful of strings and end of statements - start := r.p - - for { - pre := r.p - c, ok := r.nextRune() - if !ok { - break - } - - switch { - case unicode.IsSpace(c): - if pre == start { - start = r.p - continue - } - - // this argument is done - return r.v[start:pre], nil - case c == ';': - return r.v[start:pre], EndOfStatement - case c == '\'': - err := r.nextString("'") - if err != nil { - return r.v[start:r.p], err - } - case c == '"': - err := r.nextString("\"") - if err != nil { - return r.v[start:r.p], err - } - case c == '$' && pre == start: - // try the dollar string - err := r.nextDollarIdentifier() - if err != nil { - if err == NotThisToken { - err = nil - continue - } - return r.v[start:r.p], err - } - - err = r.nextString(r.v[pre:r.p]) - if err != nil { - return r.v[start:r.p], err - } - case c == '-' && pre == start: - err := r.nextComment() - if err != nil { - if err == NotThisToken { - err = nil - continue - } - return r.v[start:r.p], err - } - case c == '/': - err := r.nextMultiLineComment() - if err != nil { - if err == NotThisToken { - err = nil - continue - } - return r.v[start:r.p], err - } - } - } - - return r.v[start:], EndOfSQL -} - -func (r *reader) nextCommand() (cmd Command, err error) { - cmd.Begin = r.p - defer func() { - cmd.End = r.p - cmd.SQL = r.v[cmd.Begin:cmd.End] - }() - - cmd.Command, err = r.nextIdentifier() - if err != nil { - if err == EndOfStatement { - err = nil - } - return - } - - for { - var arg string - arg, err = r.nextArgument() - - if arg != "" { - cmd.Arguments = append(cmd.Arguments, arg) - } - - if err != nil { - if err == EndOfStatement { - err = nil - } - return - } - } -} - -// Parse parses an sql query in a single pass (with no look aheads or look behinds). -// Because all we really care about is the commands, this can be very fast -// based on https://www.postgresql.org/docs/14/sql-syntax-lexical.html -func Parse(sql string) (cmds []Command, err error) { - r := reader{ - v: sql, - } - for { - var cmd Command - cmd, err = r.nextCommand() - - if cmd.Command != "" { - cmds = append(cmds, cmd) - } - - if err != nil { - if err == EndOfSQL { - err = nil - } - return - } - } -} diff --git a/lib/parse/parse_test.go b/lib/parse/parse_test.go deleted file mode 100644 index 6c60cf6b495838f06c422777ee0f76aaf3e85a88..0000000000000000000000000000000000000000 --- a/lib/parse/parse_test.go +++ /dev/null @@ -1,199 +0,0 @@ -package parse - -import ( - "fmt" - "gfx.cafe/ghalliday1/pg3p" - parser2 "gfx.cafe/ghalliday1/pgparser/parser" - "github.com/antlr/antlr4/runtime/Go/antlr/v4" - "github.com/auxten/postgresql-parser/pkg/sql/parser" - "testing" -) - -const TestQuery1 = "SELECT * FROM Customers WHERE (CustomerName LIKE 'L%'\nOR CustomerName LIKE 'R%' /*OR CustomerName LIKE 'S%'\nOR CustomerName LIKE 'T%'*/ OR CustomerName LIKE 'W%')\nAND Country='USA'\nORDER BY CustomerName;\n" -const TestQuery2 = "SELECT 'test string ;;;!!!WOW' \"double quote test comment\\\" with escape\" $abc$dollar sign quote $with$ nested dollars $with$ wow $abc$ normal" -const TestQuery3 = "SELECT to_date('20201230', 'YYYYMMDD') AS data_dt, COALESCE(t4.party_id, t.sign_org) AS org_id, t.agmt_id, " + - "t1.fcy_spec_acct_id_type AS fcy_spec_acct_id_type, t.agmt_mdfr, t.categ_cd, COALESCE(NULL, '') AS retail_ind, " + - "t.item_id, CASE WHEN t.categ_cd IN ('1013', '1021') THEN '1101' WHEN t.categ_cd IN ('4009',) THEN '1102' WHEN " + - "t.categ_cd IN ('4013',) THEN '1201' WHEN (t.categ_cd IN ('4010',)) AND (t3.inform_deposit_categ IN ('SLA', 'EDA')) " + - "THEN '1202' WHEN (t.categ_cd IN ('4012',)) AND (t3.inform_deposit_categ NOT IN ('NRI1', 'NRI7', 'IMM7', 'REN7')) " + - "THEN '1301' WHEN (t.categ_cd IN ('4012',)) AND (t3.inform_deposit_categ IN ('NRI1', 'NRI7')) THEN '1302' WHEN " + - "(t.categ_cd IN ('4012',)) AND (t3.inform_deposit_categ IN ('IMM7', 'REN7')) THEN '1303' ELSE '9999' END AS prod_cd, " + - "t3.inform_deposit_categ AS sub_prod_cd, t.party_id AS cust_id, t.categ_cd AS acct_categ_cd, t.agmt_categ_cd AS " + - "acct_type_cd, CASE WHEN t.categ_cd = '1604' THEN '2' ELSE '1' END AS acct_stat_cd, CASE WHEN " + - "t1.sleep_acct_ind = 'Y' THEN '1' ELSE '0' END AS dormancy_ind, CASE WHEN t1.dep_exchg_ind = 'Y' THEN '1' " + - "ELSE '0' END AS dep_exchg_ind, COALESCE(t11.intr, 0.00) AS mature_intr, " + - "COALESCE(t.sign_dt, to_date('${NULLDATE}', 'YYYYMMDD')) AS open_acct_dt, " + - "COALESCE(t.st_int_dt, to_date('${NULLDATE}', 'YYYYMMDD')) AS st_int_dt, " + - "COALESCE(t.mature_dt, to_date('${MAXDATE}', 'YYYYMMDD')) AS mature_dt, CASE WHEN (t.src_sys = 'S04_ACCT_CLOSED') " + - "AND (t.agmt_stat_cd = 'XH') THEN t.close_dt ELSE to_date('${MAXDATE}', 'YYYYMMDD') " + - "END AS close_acct_dt, t9.agenter_nm AS agenter_nm, CASE WHEN t9.agenter_ident_info_categ_cd = 'CD_018' " + - "THEN t9.agenter_ident_info_categ_cd ELSE '' END AS agenter_cert_type_cd, t9.agenter_ident_info_content AS " + - "agenter_cert_id, t9.agenter_nationality_cd AS agenter_nationality, t9.agenter_tel AS agenter_tel, " + - "t9.agent_open_acct_verify_situati AS agenter_open_acct_verify_rslt, t.ccy_cd, t.open_acct_amt, " + - "COALESCE(substr(t5.tid, 1, 3), '') AS ftz_actype, CASE WHEN (t5.tid IS NOT NULL) OR (t5.tid != '') " + - "THEN '1' ELSE '0' END AS ftz_act_ind, CASE WHEN t.categ_cd = '4012' THEN 'D' ELSE " + - "COALESCE(t6.term_unit_cd, '') END AS term_type_cd, CASE WHEN t.categ_cd = '4012' " + - "THEN to_number(substr(sub_prod_cd, 4, 1), '9') ELSE COALESCE(t6.term, 0) END AS " + - "deposit_periods, COALESCE(CASE WHEN ((((prod_cd = '1101') OR (t.item_id IN ('14002', '15002', '16002'))) " + - "OR (COALESCE(t.sign_dt, to_date('${NULLDATE}', 'YYYYMMDD')) = to_date('${NULLDATE}', 'YYYYMMDD'))) OR " + - "(COALESCE(t.mature_dt, to_date('${MAXDATE}', 'YYYYMMDD')) = to_date('${NULLDATE}', 'YYYYMMDD'))) OR " + - "(COALESCE(t.mature_dt, to_date('${MAXDATE}', 'YYYYMMDD')) = to_date('${MAXDATE}', 'YYYYMMDD')) THEN " + - "0 ELSE COALESCE(t.mature_dt, to_date('${MAXDATE}', 'YYYYMMDD')) - t.st_int_dt END, 0) AS term_days, " + - "CASE WHEN prod_cd = '1101' THEN '' WHEN t.item_id IN ('06003', '011', '01014', '01015', '01016', '01017', '099')" + - " THEN 'M' WHEN t.item_id IN ('4002', '5002', '6002') THEN 'D' ELSE (CASE WHEN t6.term_unit_cd IS NOT NULL " + - "THEN t6.term_unit_cd ELSE (CASE WHEN term_days < 7 THEN '' WHEN (term_days >= 7) AND (term_days < 28) THEN " + - "'D' ELSE 'M' END) END) END AS adj_term_type_cd, CASE WHEN prod_cd = '1101' THEN 0 WHEN t.item_id = '1006003'" + - " THEN 60 WHEN t.item_id = '10011' THEN 3 WHEN t.item_id IN ('1001014', '1001015', '1001016', '1099') " + - "THEN 12 WHEN t.item_id = '1001017' THEN 24 ELSE (CASE WHEN deposit_periods > 0 THEN deposit_periods ELSE" + - " (CASE WHEN term_days < 7 THEN 0 WHEN (term_days >= 7) AND (term_days < 28) THEN 7 WHEN (term_days >= 28) " + - "AND (term_days <= 31) THEN 1 WHEN (term_days > 31) AND (term_days <= 92) THEN 3 WHEN (term_days > 92) AND " + - "(term_days <= 184) THEN 6 WHEN (term_days > 184) AND (term_days <= 366) THEN 12 WHEN (term_days > 366) AND " + - "(term_days <= 731) THEN 24 WHEN (term_days > 731) AND (term_days <= 1096) THEN 36 WHEN term_days > 1096 " + - "THEN 60 END) END) END AS adj_deposit_periods, COALESCE(NULL, '') AS product_code, COALESCE(NULL, '') AS " + - "lmt_lnk_ind, COALESCE(t.cur_bal, 0.00) AS open_cleared_bal, t7.assoc_agmt_id AS limit_ref, " + - "COALESCE(t8.cash_pool_group, '') AS cash_pool_group, COALESCE(t10.medium_id, '') AS card_id FROM " + - "agmt_item_temp AS t LEFT JOIN pviewdb.t03_acct AS t1 ON t.agmt_id = t1.agmt_id LEFT JOIN " + - "pviewdb.t03_inform_dep_acct AS t3 ON ((t3.agmt_id = t.agmt_id) AND (t3.st_dt <= to_date('20201230', 'YYYYMMDD')))" + - " AND (t3.end_dt > to_date('20201230', 'YYYYMMDD')) LEFT JOIN t03_agmt_pty_rela_h_temp AS t4 ON" + - " t4.agmt_id = t.agmt_id LEFT JOIN s04_zmq_acc_cur AS t5 ON t5.customer = t.party_id LEFT JOIN acct_term_temp" + - " AS t6 ON t.agmt_id = t6.agmt_id LEFT JOIN t03_agmt_rela_h_temp AS t7 ON t.agmt_id = t7.agmt_id " + - "LEFT JOIN agmt_cash_pool_temp AS t8 ON t.agmt_id = t8.tid LEFT JOIN pviewdb.t03_agmt_agent_h AS t9" + - " ON ((t.agmt_id = t9.agmt_id) AND (t9.st_dt <= to_date('20201230', 'YYYYMMDD'))) AND" + - " (t9.end_dt > to_date('20201230', 'YYYYMMDD')) LEFT JOIN pviewdb.t03_agmt_medium_rela_h " + - "AS t10 ON (((t.agmt_id = t10.agmt_id) AND (t10.st_dt <= to_date('20201230', 'YYYYMMDD'))) " + - "AND (t10.end_dt > to_date('20201230', 'YYYYMMDD'))) AND (t10.agmt_medium_rela_type_cd = '2')" + - " LEFT JOIN pviewdb.t03_agmt_int_h AS t11 ON ((((t.agmt_id = t11.agmt_id) AND (t.agmt_mdfr = t11.agmt_mdfr)) " + - "AND (t11.st_dt <= to_date('20201230', 'YYYYMMDD'))) AND (t11.end_dt > to_date('20201230', 'YYYYMMDD'))) " + - "AND (t11.int_type_cd = '7');" -const TestQuery4 = ` - /* test various things - * like if this comment will be parsed correctly - * or if it will make the thing crash and burn - */ - select concat('one'); -` - -func testParse1() error { - sql, err := Parse(TestQuery1) - if err != nil { - return err - } - if len(sql) != 1 { - return fmt.Errorf("expected 1 commands, got %d", len(sql)) - } - //panic(fmt.Sprintf("%#v", sql)) - return nil -} - -func testParse3() error { - sql, err := Parse(TestQuery3) - if err != nil { - return err - } - if len(sql) != 1 { - return fmt.Errorf("expected 1 commands, got %d", len(sql)) - } - //panic(fmt.Sprintf("%#v", sql)) - return nil -} - -func TestParse1(t *testing.T) { - err := testParse1() - if err != nil { - t.Error(t) - } -} - -func TestComment(t *testing.T) { - sql, err := Parse(TestQuery2) - if err != nil { - t.Error(err) - } - //panic(fmt.Sprintf("%#v", sql)) - _ = sql -} - -func TestBig(t *testing.T) { - err := testParse3() - if err != nil { - t.Error(t) - } -} - -func TestMultiLineComment(t *testing.T) { - sql, err := Parse(TestQuery4) - if err != nil { - t.Error(err) - } - _ = sql -} - -func BenchmarkInternal1(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - err := testParse1() - if err != nil { - b.Error(err) - } - } -} - -func BenchmarkInternal3(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - err := testParse3() - if err != nil { - b.Error(err) - } - } -} - -func BenchmarkAuxten1(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - _, err := parser.Parse(TestQuery1) - if err != nil { - b.Error(err) - } - } -} - -func BenchmarkAuxten3(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - _, err := parser.Parse(TestQuery3) - if err != nil { - b.Error(err) - } - } -} - -func BenchmarkAntlr3(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - input := antlr.NewInputStream(TestQuery3) - lexer := parser2.NewPostgreSQLLexer(input) - stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) - p := parser2.NewPostgreSQLParser(stream) - p.GetInterpreter().SetPredictionMode(antlr.PredictionModeSLL) - p.BuildParseTrees = true - tree := p.Root() - antlr.ParseTreeWalkerDefault.Walk(&parser2.BasePostgreSQLParserListener{}, tree) - } -} - -func BenchmarkPg3p1(b *testing.B) { - b.ReportAllocs() - p := pg3p.NewParser() - for i := 0; i < b.N; i++ { - p.Lex(TestQuery1) - } -} - -func BenchmarkPg3p3(b *testing.B) { - b.ReportAllocs() - p := pg3p.NewParser() - for i := 0; i < b.N; i++ { - p.Lex(TestQuery3) - } -} diff --git a/lib/util/cmux/cmux.go b/lib/util/cmux/cmux.go deleted file mode 100644 index e21eadcfdfb0fee567f3435cd750c3a0d81b38dd..0000000000000000000000000000000000000000 --- a/lib/util/cmux/cmux.go +++ /dev/null @@ -1,158 +0,0 @@ -package cmux - -import ( - "strings" - "sync" - - "github.com/looplab/fsm" -) - -type Mux[IN, OUT any] interface { - Register([]string, func(IN, []string) OUT) - Call(IN, []string) (OUT, bool) -} - -type MapMux[IN, OUT any] struct { - sub map[string]*MapMux[IN, OUT] - fn func(IN, []string) OUT -} - -func NewMapMux[IN, OUT any]() *MapMux[IN, OUT] { - return &MapMux[IN, OUT]{ - sub: make(map[string]*MapMux[IN, OUT]), - } -} - -func (m *MapMux[IN, OUT]) Register(path []string, fn func(IN, []string) OUT) { - mux := m - for { - if len(path) == 0 { - mux.fn = fn - return - } - - var ok bool - if _, ok = mux.sub[path[0]]; !ok { - mux.sub[path[0]] = NewMapMux[IN, OUT]() - } - mux = mux.sub[path[0]] - path = path[1:] - } -} - -func (m *MapMux[IN, OUT]) Call(arg IN, path []string) (o OUT, exists bool) { - mux := m - for { - if len(path) != 0 { - if sub, ok := mux.sub[path[0]]; ok { - mux = sub - path = path[1:] - continue - } - } - - if mux.fn != nil { - o = mux.fn(arg, path) - exists = true - } - return - } -} - -type funcSet[IN, OUT any] struct { - Ref []string - Call func(IN, []string) OUT -} - -type FsmMux[IN, OUT any] struct { - f *fsm.FSM - funcs map[string]funcSet[IN, OUT] - - sync.RWMutex -} - -func (f *FsmMux[IN, OUT]) Register(path []string, fn func(IN, []string) OUT) { - execkey := strings.Join(path, "|") - f.funcs[execkey] = funcSet[IN, OUT]{ - Ref: path, - Call: fn, - } - f.construct() -} - -func (f *FsmMux[IN, OUT]) construct() { - evts := fsm.Events{} - cbs := fsm.Callbacks{} - for _, fset := range f.funcs { - path := fset.Ref - lp := len(path) - switch lp { - case 0: - case 1: - evts = append(evts, fsm.EventDesc{ - Name: path[0], - Src: []string{"_"}, - Dst: path[0], - }) - default: - evts = append(evts, fsm.EventDesc{ - Name: path[0], - Src: []string{"_"}, - Dst: path[0], - }) - for i := 1; i < len(path); i++ { - ee := fsm.EventDesc{ - Name: path[i], - Src: []string{path[i-1]}, - Dst: path[i], - } - evts = append(evts, ee) - } - } - } - f.f = fsm.NewFSM("_", evts, cbs) -} - -func (f *FsmMux[IN, OUT]) Call(arg IN, k []string) (r OUT, matched bool) { - var fn func(IN, []string) OUT - args := k - path := k - lp := len(path) - switch lp { - case 0: - case 1: - args = args[1:] - fn = f.funcs[k[0]].Call - default: - f.Lock() - f.f.SetState("_") - for i := 0; i < len(path); i++ { - key := strings.Join(path[:i], "|") - if mb, ok := f.funcs[key]; ok { - fn = mb.Call - } - if f.f.Can(path[i]) { - f.f.Event(path[i]) - } else { - key := strings.Join(path[:i], "|") - if _, ok := f.funcs[key]; ok { - args = args[i:] - break - } - } - } - f.Unlock() - } - if fn != nil { - r = fn(arg, args) - matched = true - } - return -} - -func NewFsmMux[IN, OUT any]() Mux[IN, OUT] { - o := &FsmMux[IN, OUT]{ - funcs: map[string]funcSet[IN, OUT]{}, - } - return o -} diff --git a/lib/util/cmux/cmux_test.go b/lib/util/cmux/cmux_test.go deleted file mode 100644 index e0ff58884f94532c21cc9d78df4a61b192a6eff1..0000000000000000000000000000000000000000 --- a/lib/util/cmux/cmux_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package cmux - -import ( - "log" - "testing" -) - -func TestFsm(t *testing.T) { - m := NewFsmMux[any, error]() - - m.Register([]string{"set", "shard", "to"}, func(_ any, s []string) error { - log.Println(s) - return nil - }) - m.Register([]string{"set", "sharding", "key", "to"}, func(_ any, s []string) error { - log.Println(s) - return nil - }) - - m.Call(nil, []string{"set", "shard", "to", "doggo", "wow", "this", "works"}) - m.Call(nil, []string{"set", "sharding", "key", "to", "doggo", "wow", "this", "works2"}) - -} diff --git a/lib/util/fsm/fsm.go b/lib/util/fsm/fsm.go deleted file mode 100644 index 225a120e8bef75c3ae784563e59bce51975afeb9..0000000000000000000000000000000000000000 --- a/lib/util/fsm/fsm.go +++ /dev/null @@ -1,231 +0,0 @@ -package fsm - -import ( - "fmt" - "sync" -) - -// TransitionRuleSet is a set of allowed transitions. This uses map of struct{} -// to implement a set. -type TransitionRuleSet map[string]struct{} - -// Copy copies the TransitionRuleSet in to a different TransitionRuleSet. -func (trs TransitionRuleSet) Copy() TransitionRuleSet { - srt := make(TransitionRuleSet) - - for rule, value := range trs { - srt[rule] = value - } - - return srt -} - -// CallbackHandler is an interface type defining the interface for receiving callbacks. -type CallbackHandler interface { - StateTransitionCallback(string) error -} - -// Machine is the state machine. -type Machine struct { - state string - mu sync.RWMutex - - transitions map[string]TransitionRuleSet - - callback CallbackHandler - syncCallback bool -} - -func (m *Machine) Clone() *Machine { - return &Machine{ - state: m.state, - transitions: m.transitions, - callback: m.callback, - syncCallback: m.syncCallback, - } -} - -// CurrentState returns the machine's current state. If the State returned is -// "", then the machine has not been given an initial state. -func (m *Machine) CurrentState() string { - m.mu.RLock() - defer m.mu.RUnlock() - - return m.state -} - -// StateTransitionRules returns the allowed states for -func (m *Machine) StateTransitionRules(state string) (TransitionRuleSet, error) { - m.mu.RLock() - defer m.mu.RUnlock() - - if m.transitions == nil { - return nil, newErrorStruct("the machine has not been fully initialized", ErrorMachineNotInitialized) - } - - // ensure the state has been registered - if _, ok := m.transitions[state]; !ok { - return nil, newErrorStruct(fmt.Sprintf("state %s has not been registered", state), ErrorStateUndefined) - } - - return m.transitions[state].Copy(), nil -} - -// AddStateTransitionRules is a function for adding valid state transitions to the machine. -// This allows you to define which states any given state can be transitioned to. -func (m *Machine) AddStateTransitionRules(sourceState string, destinationStates ...string) error { - m.mu.Lock() - defer m.mu.Unlock() - - // if the transitions map is nil, we need to allocate it - if m.transitions == nil { - m.transitions = make(map[string]TransitionRuleSet) - } - - // if the map for the source state does not exist, allocate it - if m.transitions[sourceState] == nil { - m.transitions[sourceState] = make(TransitionRuleSet) - } - - // get a reference to the map we care about - // avoids doing the map lookup for each iteration - mp := m.transitions[sourceState] - - for _, dest := range destinationStates { - mp[dest] = struct{}{} - } - - return nil -} - -// SetStateTransitionCallback for the state transition. This is meant to send -// callbacks back to the consumer for state changes. The callback only sends the -// new state. The synchonous parameter indicates whether the callback is done -// synchronously with the StateTransition() call. -func (m *Machine) SetStateTransitionCallback(callback CallbackHandler, synchronous bool) error { - m.mu.Lock() - defer m.mu.Unlock() - - m.callback = callback - m.syncCallback = synchronous - - return nil -} - -// StateTransition triggers a transition to the toState. This function is also -// used to set the initial state of machine. -// -// Before you can transition to any state, even for the initial, you must define -// it with AddStateTransition(). If you are setting the initial state, and that -// state is not define, this will return an ErrInvalidInitialState error. -// -// When transitioning from a state, this function will return an error either -// if the state transition is not allowed, or if the destination state has -// not been defined. In both cases, it's seen as a non-permitted state transition. -func (m *Machine) StateTransition(toState string) error { - m.mu.Lock() - defer m.mu.Unlock() - - // if this is nil we cannot assume any state - if m.transitions == nil { - return newErrorStruct("the machine has no states added", ErrorMachineNotInitialized) - } - - // if the state is nothing, this is probably the initial state - if m.state == "" { - // if the state is not defined, it's invalid - if _, ok := m.transitions[toState]; !ok { - return newErrorStruct("the initial state has not been defined within the machine", ErrorStateUndefined) - } - - // set the state - m.state = toState - return nil - } - - // if we are not permitted to transition to this state... - if _, ok := m.transitions[m.state][toState]; !ok { - return newErrorStruct(fmt.Sprintf("transition from state %s to %s is not permitted", m.state, toState), ErrorTransitionNotPermitted) - } - - // if the destination state was not defined... - if _, ok := m.transitions[toState]; !ok { - return newErrorStruct(fmt.Sprintf("state %s has not been registered", toState), ErrorStateUndefined) - } - - m.state = toState - - if m.callback != nil { - if m.syncCallback { - // do not return the error - // this may be reconsidered - m.callback.StateTransitionCallback(toState) - } else { - // spin off the callback - go func() { m.callback.StateTransitionCallback(toState) }() - } - } - - return nil -} - -type ErrorCode uint - -func (e ErrorCode) String() string { - switch e { - case ErrorMachineNotInitialized: - return "MachineNotInitialized" - case ErrorTransitionNotPermitted: - return "TransitionNotPermitted" - case ErrorStateUndefined: - return "StateUndefined" - default: - return "Unknown" - } -} - -const ( - // ErrorUnknown is the default value - ErrorUnknown ErrorCode = iota - - // ErrorMachineNotInitialized is an error returned when actions are taken on - // a machine before it has been initialized. A machine is initialized by - // adding at least one state and setting it as the initial state. - ErrorMachineNotInitialized - - // ErrorTransitionNotPermitted is the error returned when trying to - // transition to an invalid state. In other words, the machine is not - // permitted to transition from the current state to the one requested. - ErrorTransitionNotPermitted - - // ErrorStateUndefined is the error returned when the requested state is - // not defined within the machine. - ErrorStateUndefined -) - -// Error is the struct representing internal errors. -// This implements the error interface -type Error struct { - message string - code ErrorCode -} - -// newErrorStruct uses messge and code to create an *Error struct. The *Error -// struct implements the 'error' interface, so it should be able to be used -// wherever 'error' is expected. -func newErrorStruct(message string, code ErrorCode) *Error { - return &Error{ - message: message, - code: code, - } -} - -// Message returns the error message. -func (e *Error) Message() string { return e.message } - -// Code returns the error code. -func (e *Error) Code() ErrorCode { return e.code } - -func (e *Error) Error() string { - return fmt.Sprintf("%s (%d): %s", e.code, e.code, e.message) -} diff --git a/lib/util/gatutil/table.go b/lib/util/gatutil/table.go deleted file mode 100644 index aafc20560f541deb95eee32eaec91e8c88a19177..0000000000000000000000000000000000000000 --- a/lib/util/gatutil/table.go +++ /dev/null @@ -1,66 +0,0 @@ -package gatutil - -import ( - "fmt" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/gat/protocol" -) - -type TableHeaderColumn struct { - Name string - Type Type -} - -func (T *TableHeaderColumn) RowDescription() protocol.FieldsRowDescriptionFields { - return protocol.FieldsRowDescriptionFields{ - Name: T.Name, - DataType: T.Type.OID(), - DataTypeSize: T.Type.Len(), - FormatCode: 0, - } -} - -type TableHeader struct { - Columns []TableHeaderColumn -} - -func (T *TableHeader) RowDescription() (pkt protocol.RowDescription) { - for _, col := range T.Columns { - pkt.Fields.Fields = append(pkt.Fields.Fields, col.RowDescription()) - } - return -} - -type TableRow struct { - Columns []any -} - -func (T *TableRow) DataRow() (pkt protocol.DataRow) { - for _, col := range T.Columns { - pkt.Fields.Columns = append(pkt.Fields.Columns, protocol.FieldsDataRowColumns{ - Bytes: []byte(fmt.Sprintf("%v\x00", col)), - }) - } - return -} - -type Table struct { - Header TableHeader - Rows []TableRow -} - -func (T *Table) Send(client gat.Client) error { - rowDescription := T.Header.RowDescription() - err := client.Send(&rowDescription) - if err != nil { - return err - } - for _, row := range T.Rows { - dataRow := row.DataRow() - err = client.Send(&dataRow) - if err != nil { - return err - } - } - return nil -} diff --git a/lib/util/gatutil/type.go b/lib/util/gatutil/type.go deleted file mode 100644 index 72cf209955124ba068e4e75132863fe010cdc74f..0000000000000000000000000000000000000000 --- a/lib/util/gatutil/type.go +++ /dev/null @@ -1,86 +0,0 @@ -package gatutil - -type Type interface { - OID() int32 - Len() int16 -} - -type Text struct{} - -func (Text) OID() int32 { - return 25 -} - -func (Text) Len() int16 { - return -1 -} - -type Int16 struct{} - -func (Int16) OID() int32 { - return 21 -} - -func (Int16) Len() int16 { - return 2 -} - -type Int32 struct{} - -func (Int32) OID() int32 { - return 23 -} - -func (Int32) Len() int16 { - return 4 -} - -type Int64 struct{} - -func (Int64) OID() int32 { - return 20 -} - -func (Int64) Len() int16 { - return 8 -} - -type Char struct{} - -func (Char) OID() int32 { - return 18 -} - -func (Char) Len() int16 { - return 1 -} - -type Bool struct{} - -func (Bool) OID() int32 { - return 16 -} - -func (Bool) Len() int16 { - return 1 -} - -type Float32 struct{} - -func (Float32) OID() int32 { - return 700 -} - -func (Float32) Len() int16 { - return 4 -} - -type Float64 struct{} - -func (Float64) OID() int32 { - return 701 -} - -func (Float64) Len() int16 { - return 8 -} diff --git a/lib/util/iter/chain.go b/lib/util/iter/chain.go deleted file mode 100644 index 9c47240a7811e4b4e55d46d31cfff502fbdda3f4..0000000000000000000000000000000000000000 --- a/lib/util/iter/chain.go +++ /dev/null @@ -1,19 +0,0 @@ -package iter - -func chain[T any](i1, i2 Iter[T]) Iter[T] { - return func() (T, bool) { - v, ok := i1() - if !ok { - return i2() - } - return v, true - } -} - -func Chain[T any](i1, i2 Iter[T], in ...Iter[T]) Iter[T] { - i := chain(i1, i2) - for _, iv := range in { - i = chain(i, iv) - } - return i -} diff --git a/lib/util/iter/empty.go b/lib/util/iter/empty.go deleted file mode 100644 index fa209b4c70a03494d57ce920346eba988a7fa405..0000000000000000000000000000000000000000 --- a/lib/util/iter/empty.go +++ /dev/null @@ -1,7 +0,0 @@ -package iter - -func Empty[T any]() Iter[T] { - return func() (T, bool) { - return *new(T), false - } -} diff --git a/lib/util/iter/filter.go b/lib/util/iter/filter.go deleted file mode 100644 index 3fe095fbc7e7e366bd452c96c4ab00511f63a6ca..0000000000000000000000000000000000000000 --- a/lib/util/iter/filter.go +++ /dev/null @@ -1,12 +0,0 @@ -package iter - -func Filter[T any](iter Iter[T], f func(T) bool) Iter[T] { - return func() (T, bool) { - for v, ok := iter(); ok; v, ok = iter() { - if f(v) { - return v, true - } - } - return *new(T), false - } -} diff --git a/lib/util/iter/flatten.go b/lib/util/iter/flatten.go deleted file mode 100644 index 3d5b83af44dbb6757a0a50ee00afc2807702b967..0000000000000000000000000000000000000000 --- a/lib/util/iter/flatten.go +++ /dev/null @@ -1,18 +0,0 @@ -package iter - -func Flatten[T any](iter Iter[Iter[T]]) Iter[T] { - i := Empty[T]() - return func() (T, bool) { - for { - v, ok := i() - if ok { - return v, true - } - i, ok = iter() - if !ok { - break - } - } - return *new(T), false - } -} diff --git a/lib/util/iter/foreach.go b/lib/util/iter/foreach.go deleted file mode 100644 index 3e9143bb83d9a3fc50063ee085316b56e9863acc..0000000000000000000000000000000000000000 --- a/lib/util/iter/foreach.go +++ /dev/null @@ -1,9 +0,0 @@ -package iter - -// ForEach is the same as doing `for v, ok := iter(); ok; v, ok = iter() {...}` but cleaner on the eyes. -// If you need to break out early, use the `for` form -func ForEach[T any](iter Iter[T], f func(T)) { - for v, ok := iter(); ok; v, ok = iter() { - f(v) - } -} diff --git a/lib/util/iter/iter.go b/lib/util/iter/iter.go deleted file mode 100644 index ea38141a2dda38927731034353434401b47803a1..0000000000000000000000000000000000000000 --- a/lib/util/iter/iter.go +++ /dev/null @@ -1,3 +0,0 @@ -package iter - -type Iter[T any] func() (T, bool) diff --git a/lib/util/iter/iter_test.go b/lib/util/iter/iter_test.go deleted file mode 100644 index 4c5de2ce1fe96cfb123af600fc7441f501f00150..0000000000000000000000000000000000000000 --- a/lib/util/iter/iter_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package iter - -import ( - "strconv" - "testing" -) - -func expect[T comparable](t *testing.T, iter Iter[T], value T) { - v, ok := iter() - if !ok { - t.Error("expected iter to have a value, but reached end") - return - } - if v != value { - t.Error("expected next value to be", value, "( got:", v, ")") - } -} - -func end[T any](t *testing.T, iter Iter[T]) { - v, ok := iter() - if ok { - t.Error("expected iter to end but got value", v) - } -} - -func TestSlice(t *testing.T) { - slice := []int{1, 2, 3} - iter := Slice(slice) - - expect(t, iter, 1) - expect(t, iter, 2) - expect(t, iter, 3) - end(t, iter) -} - -func TestMap(t *testing.T) { - slice := []int{1, 2, 3} - iter := Slice(slice) - iter2 := Map(iter, strconv.Itoa) - - expect(t, iter2, "1") - expect(t, iter2, "2") - expect(t, iter2, "3") - end(t, iter2) -} - -func TestFilter(t *testing.T) { - slice := []int{1, 2, 3} - iter := Slice(slice) - iter2 := Filter(iter, func(i int) bool { - return i%2 == 0 - }) - - expect(t, iter2, 2) - end(t, iter2) -} - -func TestChain(t *testing.T) { - slice := []int{1, 2, 3} - slice2 := []int{4, 5} - slice3 := []int{6, 7} - iter := Slice(slice) - iter2 := Slice(slice2) - iter3 := Slice(slice3) - iter4 := Chain(iter, iter2, iter3) - - expect(t, iter4, 1) - expect(t, iter4, 2) - expect(t, iter4, 3) - expect(t, iter4, 4) - expect(t, iter4, 5) - expect(t, iter4, 6) - expect(t, iter4, 7) - end(t, iter4) -} - -func TestForEach(t *testing.T) { - slice := []int{1, 2, 3} - iter := Slice(slice) - - i := 0 - ForEach(iter, func(v int) { - if slice[i] != v { - t.Error("expected index", i, "to be", slice[i], "but got", v) - } - i++ - }) - end(t, iter) -} - -func TestEmpty(t *testing.T) { - iter := Empty[int]() - - end(t, iter) -} - -func TestFlatten(t *testing.T) { - slice := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} - iter := Slice(slice) - iter2 := Map(iter, func(v []int) Iter[int] { - return Slice(v) - }) - iter3 := Flatten(iter2) - - expect(t, iter3, 1) - expect(t, iter3, 2) - expect(t, iter3, 3) - expect(t, iter3, 4) - expect(t, iter3, 5) - expect(t, iter3, 6) - expect(t, iter3, 7) - expect(t, iter3, 8) - expect(t, iter3, 9) - end(t, iter3) -} - -func BenchmarkFlatten(b *testing.B) { - b.ReportAllocs() - slice := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} - - for i := 0; i < b.N; i++ { - iter := Slice(slice) - iter2 := Map(iter, func(v []int) Iter[int] { - return Slice(v) - }) - iter3 := Flatten(iter2) - - ForEach(iter3, func(i int) {}) - } -} diff --git a/lib/util/iter/map.go b/lib/util/iter/map.go deleted file mode 100644 index 9415b0854a4aa6ae761fa5fa99bbfedd41d10c3d..0000000000000000000000000000000000000000 --- a/lib/util/iter/map.go +++ /dev/null @@ -1,11 +0,0 @@ -package iter - -func Map[T, U any](base Iter[T], f func(T) U) Iter[U] { - return func() (U, bool) { - v, ok := base() - if !ok { - return *new(U), false - } - return f(v), true - } -} diff --git a/lib/util/iter/single.go b/lib/util/iter/single.go deleted file mode 100644 index fcc4dc7d4d75230cdba3dc90f6e94fe08a0f7d2e..0000000000000000000000000000000000000000 --- a/lib/util/iter/single.go +++ /dev/null @@ -1,12 +0,0 @@ -package iter - -func Single[T any](value T) Iter[T] { - ok := true - return func() (T, bool) { - if ok { - ok = false - return value, true - } - return *new(T), false - } -} diff --git a/lib/util/iter/slice.go b/lib/util/iter/slice.go deleted file mode 100644 index 34277f168e7afcda4589dc94a5a0ff71aab5abcf..0000000000000000000000000000000000000000 --- a/lib/util/iter/slice.go +++ /dev/null @@ -1,13 +0,0 @@ -package iter - -func Slice[T any](slice []T) Iter[T] { - i := 0 - return func() (T, bool) { - if i >= len(slice) { - return *new(T), false - } - v := slice[i] - i++ - return v, true - } -} diff --git a/lib/util/maps/maps.go b/lib/util/maps/maps.go deleted file mode 100644 index 5a5a162bd4bc46deb6f5f364143a42ccdb828c28..0000000000000000000000000000000000000000 --- a/lib/util/maps/maps.go +++ /dev/null @@ -1,10 +0,0 @@ -package maps - -func FirstWhere[K comparable, V any](haystack map[K]V, predicate func(K, V) bool) (K, V, bool) { - for k, v := range haystack { - if predicate(k, v) { - return k, v, true - } - } - return *new(K), *new(V), false -} diff --git a/lib/util/race/casepool.go b/lib/util/race/casepool.go deleted file mode 100644 index bc600f64ff10317dc9f46c80cc3500e16c1720ff..0000000000000000000000000000000000000000 --- a/lib/util/race/casepool.go +++ /dev/null @@ -1,12 +0,0 @@ -package race - -import ( - "gfx.cafe/util/go/generic" - "reflect" -) - -var casePool = generic.HookPool[[]reflect.SelectCase]{ - New: func() []reflect.SelectCase { - return nil - }, -} diff --git a/lib/util/race/recv.go b/lib/util/race/recv.go deleted file mode 100644 index 5149b502c3116d116732027008e865494e26818b..0000000000000000000000000000000000000000 --- a/lib/util/race/recv.go +++ /dev/null @@ -1,25 +0,0 @@ -package race - -import ( - "reflect" - - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -func Recv[T any](next iter.Iter[<-chan T]) (T, int, bool) { - cases := casePool.Get()[:0] - defer func() { - casePool.Put(cases) - }() - iter.ForEach(next, func(ch <-chan T) { - cases = append(cases, reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(ch), - }) - }) - idx, value, ok := reflect.Select(cases) - if !ok { - return *new(T), idx, false - } - return value.Interface().(T), idx, true -} diff --git a/lib/util/race/recv_test.go b/lib/util/race/recv_test.go deleted file mode 100644 index d7f1e04e6fcb6a8e6ef5b36b16bae30f2f181c00..0000000000000000000000000000000000000000 --- a/lib/util/race/recv_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package race - -import ( - "testing" - - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -func TestRecv(t *testing.T) { - var chans []chan int - for i := 0; i < 10; i++ { - chans = append(chans, make(chan int)) - } - go func() { - v, ok := Recv(iter.Map(iter.Slice(chans), func(c chan int) <-chan int { - return c - })) - if !ok || v != 1234 { - panic("expected to receive 1234") - } - }() - chans[1] <- 1234 -} diff --git a/lib/util/race/send.go b/lib/util/race/send.go deleted file mode 100644 index 8c3b28af8d84d33cdc88d95a8f2a2751a665d7e0..0000000000000000000000000000000000000000 --- a/lib/util/race/send.go +++ /dev/null @@ -1,23 +0,0 @@ -package race - -import ( - "reflect" - - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -func Send[T any](next iter.Iter[chan<- T], value T) { - reflectValue := reflect.ValueOf(value) - cases := casePool.Get()[:0] - defer func() { - casePool.Put(cases) - }() - iter.ForEach(next, func(ch chan<- T) { - cases = append(cases, reflect.SelectCase{ - Dir: reflect.SelectSend, - Chan: reflect.ValueOf(ch), - Send: reflectValue, - }) - }) - reflect.Select(cases) -} diff --git a/lib/util/race/send_test.go b/lib/util/race/send_test.go deleted file mode 100644 index f59f5c93973dc320b29bdc11ed50fd316e790d6e..0000000000000000000000000000000000000000 --- a/lib/util/race/send_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package race - -import ( - "math/rand" - "testing" - - "gfx.cafe/gfx/pggat/lib/util/iter" -) - -func TestSend(t *testing.T) { - var chans []chan int - for i := 0; i < 10; i++ { - chans = append(chans, make(chan int)) - } - go func() { - Send(iter.Map(iter.Slice(chans), func(c chan int) chan<- int { - return c - }), 1) - Send(iter.Map(iter.Slice(chans), func(c chan int) chan<- int { - return c - }), 2) - Send(iter.Map(iter.Slice(chans), func(c chan int) chan<- int { - return c - }), 3) - }() - if <-chans[7] != 1 { - panic("expected to receive 1") - } - if <-chans[3] != 2 { - panic("expected to receive 2") - } - if <-chans[5] != 3 { - panic("expected to receive 3") - } -} - -func BenchmarkSend(b *testing.B) { - var chans []chan int - for i := 0; i < 10; i++ { - chans = append(chans, make(chan int)) - } - b.ReportAllocs() - for i := 0; i < b.N; i++ { - v := rand.Int() - go func() { - if <-chans[rand.Intn(10)] != v { - panic("expected correct value") - } - }() - Send(iter.Map(iter.Slice(chans), func(c chan int) chan<- int { - return c - }), v) - } -} - -func BenchmarkSend_Raw(b *testing.B) { - var chans []chan int - for i := 0; i < 10; i++ { - chans = append(chans, make(chan int)) - } - b.ReportAllocs() - for i := 0; i < b.N; i++ { - v := rand.Int() - go func() { - if <-chans[rand.Intn(10)] != v { - panic("expected correct value") - } - }() - // Send above is equal to the following - select { - case chans[0] <- v: - case chans[1] <- v: - case chans[2] <- v: - case chans[3] <- v: - case chans[4] <- v: - case chans[5] <- v: - case chans[6] <- v: - case chans[7] <- v: - case chans[8] <- v: - case chans[9] <- v: - } - } -} diff --git a/lib/util/slices/slices.go b/lib/util/slices/slices.go deleted file mode 100644 index 50c2902d81f023c9e0527b8e778703d91404703c..0000000000000000000000000000000000000000 --- a/lib/util/slices/slices.go +++ /dev/null @@ -1,10 +0,0 @@ -package slices - -func Contains[T comparable](haystack []T, needle T) bool { - for _, v := range haystack { - if v == needle { - return true - } - } - return false -} diff --git a/other/simple.sql b/other/simple.sql deleted file mode 100644 index 64a12488a2d12b73052054352843663635fb7860..0000000000000000000000000000000000000000 --- a/other/simple.sql +++ /dev/null @@ -1,40 +0,0 @@ - --- \setrandom aid 1 :naccounts -\set aid random(1, 100000) --- \setrandom bid 1 :nbranches -\set bid random(1, 100000) --- \setrandom tid 1 :ntellers -\set tid random(1, 100000) --- \setrandom delta -5000 5000 -\set delta random(-5000,5000) - -\set shard random(0, 2) - -SET SHARD TO :shard; - -SET SERVER ROLE TO 'auto'; - -BEGIN; - -UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; - -SELECT abalance FROM pgbench_accounts WHERE aid = :aid; - -UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid; - -UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid; - -INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); - -END; - -SET SHARDING KEY TO :aid; - --- Read load balancing -SELECT abalance FROM pgbench_accounts WHERE aid = :aid; - -SET SERVER ROLE TO 'replica'; - --- Read load balancing -SELECT abalance FROM pgbench_accounts WHERE aid = :aid; - diff --git a/postgres b/postgres deleted file mode 160000 index 382cc68007784623e365ec033468ec69535afbaf..0000000000000000000000000000000000000000 --- a/postgres +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 382cc68007784623e365ec033468ec69535afbaf diff --git a/scripts/ddos/ddos.go b/scripts/ddos/ddos.go deleted file mode 100644 index 6facf4d457a8e8463bbcbbabe95df5642662eed3..0000000000000000000000000000000000000000 --- a/scripts/ddos/ddos.go +++ /dev/null @@ -1,166 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "github.com/jackc/pgx/v5" - "io" - "log" - "math/rand" - "net/http" - "sync" - "time" -) - -// UsePrest set to true to use the prest spammer, false to use postgres spammer -const UsePrest = true - -// PrestHost only needed if UsePrest is true -const PrestHost = "http://localhost:3000" - -// PostgresHost only needed if UsePrest is false -const PostgresHost = "postgres://dev_rw:pGf63Aq0M5ck@pggat-dev.gfx.town:6432/prest" - -// ThreadCount how many concurrent spammers to run at once -const ThreadCount = 1000 - -// TestTime how long to run the test. Set to 0 to run forever -const TestTime = 0 * time.Second - -type col struct { - V int `json:"v"` -} - -func spamPrest() error { - c := col{ - V: int(rand.Int31()), - } - cstr, err := json.Marshal(c) - if err != nil { - return err - } - v, err := http.Post(PrestHost+"/prest/public/test", "application/json", bytes.NewReader(cstr)) - if err != nil { - return err - } - var body []byte - body, err = io.ReadAll(v.Body) - if err != nil { - return err - } - var rc col - err = json.Unmarshal(body, &rc) - if err != nil { - return fmt.Errorf("error unmarshaling '%s': %w", string(body), err) - } - if rc != c { - return fmt.Errorf("mismatch!!! %#v vs %#v (raw '%s')", c, rc, string(body)) - } - return nil -} - -func spamPostgres(conn *pgx.Conn) error { - rows, err := conn.Query(context.Background(), "INSERT INTO test (v) VALUES ($1);", rand.Int31()) - defer rows.Close() - return err -} - -var stats struct { - max time.Duration - total time.Duration - count int - sync.Mutex -} - -func spammer(spam func() error) error { - for { - func() { - start := time.Now() - ticker := time.NewTicker(1 * time.Second) - defer ticker.Stop() - errch := make(chan error, 1) - go func() { - errch <- spam() - close(errch) - }() - a: - for { - select { - case now := <-ticker.C: - wait := now.Sub(start) - stats.Lock() - if wait > stats.max { - stats.max = wait - } - stats.Unlock() - case err := <-errch: - if err != nil { - panic(err) - } - stats.Lock() - wait := time.Now().Sub(start) - stats.total += wait - stats.count += 1 - if wait > stats.max { - stats.max = wait - } - stats.Unlock() - break a - } - } - }() - } -} - -func postgresSpammer() error { - c, err := pgx.Connect(context.Background(), PostgresHost) - if err != nil { - return err - } - return spammer(func() error { - return spamPostgres(c) - }) -} - -func prestSpammer() error { - return spammer(spamPrest) -} - -func main() { - start := time.Now() - for i := 0; i < ThreadCount; i++ { - go func() { - var err error - if UsePrest { - err = prestSpammer() - } else { - err = postgresSpammer() - } - if err != nil { - panic(err) - } - }() - } - ticker := time.NewTicker(1 * time.Second) - var finish <-chan time.Time - if TestTime != 0 { - finish = time.After(TestTime) - } else { - finish = make(chan time.Time) - } - for { - select { - case now := <-ticker.C: - stats.Lock() - log.Printf("avg %f - max %f - %f/s", stats.total.Seconds()/float64(stats.count), stats.max.Seconds(), float64(stats.count)/now.Sub(start).Seconds()) - stats.Unlock() - case now := <-finish: - stats.Lock() - log.Printf("TEST FINISHED: avg %f - max %f - %f/s - %d requests completed", stats.total.Seconds()/float64(stats.count), stats.max.Seconds(), float64(stats.count)/now.Sub(start).Seconds(), stats.count) - stats.Unlock() - return - } - } -} diff --git a/spec/protocol/backend.yaml b/spec/protocol/backend.yaml deleted file mode 100644 index bbda8c47b669bd3e756c311da97e91508739a8a2..0000000000000000000000000000000000000000 --- a/spec/protocol/backend.yaml +++ /dev/null @@ -1,169 +0,0 @@ -Authentication: - Identifier: 'R' - Fields: - - Name: Code - Type: int32 - - Name: Salt - Type: byte - ArrayLength: 4 - If: 'T.Code == 5' - - Name: GSSAuthData - Type: byte - ArrayLength: 'payloadLength - 4' - If: 'T.Code == 8' - - Name: SASLMechanism - Type: string - If: 'T.Code == 10' - While: 'P != ""' - - Name: SASLChallenge - Type: byte - ArrayLength: 'payloadLength - 4' - If: 'T.Code == 11' - - Name: SASLAdditionalData - Type: byte - ArrayLength: 'payloadLength - 4' - If: 'T.Code == 12' -BackendKeyData: - Identifier: 'K' - Fields: - - Name: ProcessID - Type: int32 - - Name: SecretKey - Type: int32 -BindComplete: - Identifier: '2' -CloseComplete: - Identifier: '3' -CommandComplete: - Identifier: 'C' - Fields: - - Name: Data - Type: string -CopyInResponse: - Identifier: 'G' - Fields: - - Name: Format - Type: int8 - - Name: ColumnFormats - Type: int16 - LengthPrefixed: int16 -CopyOutResponse: - Identifier: 'H' - Fields: - - Name: Format - Type: int8 - - Name: ColumnFormats - Type: int16 - LengthPrefixed: int16 -CopyBothResponse: - Identifier: 'W' - Fields: - - Name: Format - Type: int8 - - Name: ColumnFormats - Type: int16 - LengthPrefixed: int16 -DataRow: - Identifier: 'D' - Fields: - - Name: Columns - Struct: - Fields: - - Name: Bytes - Type: byte - LengthPrefixed: int32 - LengthPrefixed: int16 -EmptyQueryResponse: - Identifier: 'I' -ErrorResponse: - Identifier: 'E' - Fields: - - Name: Responses - Struct: - Fields: - - Name: Code - Type: byte - - Name: Value - Type: string - If: 'T.Code != 0' - While: 'P.Code != 0' -FunctionCallResponse: - Identifier: 'V' - Fields: - - Name: Result - Type: byte - LengthPrefixed: int32 -NegotiateProtocolVersion: - Identifier: 'v' - Fields: - - Name: MinorVersion - Type: int32 - - Name: NotRecognized - Type: string - LengthPrefixed: int32 -NoData: - Identifier: 'n' -NoticeResponse: - Identifier: 'N' - Fields: - - Name: Responses - Struct: - Fields: - - Name: Code - Type: byte - - Name: Value - Type: string - If: 'T.Code != 0' - While: 'P.Code != 0' -NotificationResponse: - Identifier: 'A' - Fields: - - Name: ProcessID - Type: int32 - - Name: Channel - Type: string - - Name: Payload - Type: string -ParameterDescription: - Identifier: 't' - Fields: - - Name: Parameters - Type: int32 - LengthPrefixed: int16 -ParameterStatus: - Identifier: 'S' - Fields: - - Name: Parameter - Type: string - - Name: Value - Type: string -ParseComplete: - Identifier: '1' -PortalSuspended: - Identifier: 's' -ReadyForQuery: - Identifier: 'Z' - Fields: - - Name: Status - Type: byte -RowDescription: - Identifier: 'T' - Fields: - - Name: Fields - Struct: - Fields: - - Name: Name - Type: string - - Name: TableId - Type: int32 - - Name: AttributeNumber - Type: int16 - - Name: DataType - Type: int32 - - Name: DataTypeSize - Type: int16 - - Name: TypeModifier - Type: int32 - - Name: FormatCode - Type: int16 - LengthPrefixed: int16 diff --git a/spec/protocol/frontend.yaml b/spec/protocol/frontend.yaml deleted file mode 100644 index 4188734c8cb0c601cee34abd0609d8966e76a2f1..0000000000000000000000000000000000000000 --- a/spec/protocol/frontend.yaml +++ /dev/null @@ -1,118 +0,0 @@ -Bind: - Identifier: 'B' - Fields: - - Name: Destination - Type: string - - Name: PreparedStatement - Type: string - - Name: ParameterFormatCodes - Type: int16 - LengthPrefixed: int16 - - Name: ParameterValues - Struct: - Fields: - - Name: Value - Type: byte - LengthPrefixed: int32 - LengthPrefixed: int16 - - Name: ResultFormatCodes - Type: int16 - LengthPrefixed: int16 -Close: - Identifier: 'C' - Fields: - - Name: Which - Type: byte - - Name: Name - Type: string -CopyFail: - Identifier: 'f' - Fields: - - Name: Cause - Type: string -Describe: - Identifier: 'D' - Fields: - - Name: Which - Type: byte - - Name: Name - Type: string -Execute: - Identifier: 'E' - Fields: - - Name: Name - Type: string - - Name: MaxRows - Type: int32 -Flush: - Identifier: 'H' -FunctionCall: - Identifier: 'F' - Fields: - - Name: Function - Type: int32 - - Name: ArgumentFormatCodes - Type: int16 - LengthPrefixed: int16 - - Name: Arguments - Struct: - Fields: - - Name: Value - Type: byte - LengthPrefixed: int32 - LengthPrefixed: int16 - - Name: ResultFormatCode - Type: int16 -GSSENCRequest: - Fields: - - Name: EncryptionRequestCode - Type: int32 -AuthenticationResponse: - Identifier: 'p' - Fields: - - Name: Data - Type: byte - ArrayLength: payloadLength -Parse: - Identifier: 'P' - Fields: - - Name: PreparedStatement - Type: string - - Name: Query - Type: string - - Name: ParameterDataTypes - Type: int32 - LengthPrefixed: int16 -Query: - Identifier: 'Q' - Fields: - - Name: Query - Type: string -SSLRequest: - Fields: - - Name: SSLRequestCode - Type: int32 -StartupMessage: - Fields: - - Name: ProtocolVersionNumber - Type: int32 - - Name: ProcessKey - Type: int32 - If: 'T.ProtocolVersionNumber == 80877102' - - Name: SecretKey - Type: int32 - If: 'T.ProtocolVersionNumber == 80877102' - - Name: Parameters - If: 'T.ProtocolVersionNumber == 196608' - Struct: - Fields: - - Name: Name - Type: string - - Name: Value - Type: string - If: 'T.Name != ""' - While: 'P.Name != ""' -Sync: - Identifier: 'S' -Terminate: - Identifier: 'X' diff --git a/spec/protocol/shared.yaml b/spec/protocol/shared.yaml deleted file mode 100644 index b24d83924428c28d523a3f0e25649f52065bb606..0000000000000000000000000000000000000000 --- a/spec/protocol/shared.yaml +++ /dev/null @@ -1,8 +0,0 @@ -CopyData: - Identifier: 'd' - Fields: - - Name: Data - Type: byte - ArrayLength: payloadLength -CopyDone: - Identifier: 'c' \ No newline at end of file diff --git a/test/docker-compose.yml b/test/docker-compose.yml deleted file mode 100644 index 0567735f7cddf6916158d76e10ba0a5c8801d785..0000000000000000000000000000000000000000 --- a/test/docker-compose.yml +++ /dev/null @@ -1,40 +0,0 @@ -version: '3.9' - -services: - db: - image: postgres - restart: always - environment: - POSTGRES_PASSWORD: example - adminer: - image: adminer - restart: always - ports: - - 8080:8080 - pggat: - build: ../ - restart: always - environment: - PSQL_DB_ADMIN_USER: postgres - PSQL_DB_ADMIN_PASS: example - PSQL_DB_USER_RW: postgres - PSQL_DB_PASS_RW: example - PSQL_PRI_DB_HOST: db - PSQL_REP_DB_HOST: db - ports: - - 6432:6432 - - 6060:6060 - depends_on: - - "db" - prest: - image: prest/prest - restart: always - environment: - PREST_PG_URL: postgres://postgres:example@pggat:6432/prest - PREST_DEBUG: true - PREST_SSL_MODE: disable - ports: - - 3000:3000 - depends_on: - - "pggat" - - "db" diff --git a/www/admin.go b/www/admin.go deleted file mode 100644 index 4c3742a80f6365898a4097439332278a62d239f6..0000000000000000000000000000000000000000 --- a/www/admin.go +++ /dev/null @@ -1,32 +0,0 @@ -package www - -import ( - "html/template" - "io" - - "gfx.cafe/gfx/pggat/lib/gat/gatling" -) - -const adminTmplString = ` - - -` - -var adminTmpl = template.Must(template.New("admin_page").Parse(adminTmplString)) - -type AdminPage struct { - G *gatling.Gatling -} - -func NewAdminPage(g *gatling.Gatling) *AdminPage { - return &AdminPage{ - G: g, - } -} - -func (p *AdminPage) Renderer(w io.Writer) error { - return adminTmpl.Execute(w, p) -} - -func (p *AdminPage) test() { -} diff --git a/www/index.go b/www/index.go deleted file mode 100644 index 5fa3b9197b62e8adae7a6637b3000f693a723fce..0000000000000000000000000000000000000000 --- a/www/index.go +++ /dev/null @@ -1,23 +0,0 @@ -package www - -import ( - "io" - "net/http" -) - -type Index struct { - Pages []*Page -} - -type Page struct { - Title string - Renderer func(io.Writer) error -} - -func (p *Page) ServeHTTP(w http.ResponseWriter, r *http.Request) { - err := p.Renderer(w) - if err != nil { - w.Write([]byte(err.Error())) - return - } -}