1: e80c124c3d ! 1: 1e17a1059f common/jsonapi: support FRONTEND clients @@ Commit message memory owned by the JsonLexContext, so clients don't need to worry about freeing it. - For convenience, the backend now has destroyJsonLexContext() to mirror - other create/destroy APIs. The frontend has init/term versions of the - API to handle stack-allocated JsonLexContexts. - We can now partially revert b44669b2ca, now that json_errdetail() works correctly. - ## src/backend/utils/adt/jsonfuncs.c ## -@@ src/backend/utils/adt/jsonfuncs.c: json_object_keys(PG_FUNCTION_ARGS) - pg_parse_json_or_ereport(lex, sem); - /* keys are now in state->result */ - -- pfree(lex->strval->data); -- pfree(lex->strval); -- pfree(lex); -+ destroyJsonLexContext(lex); - pfree(sem); - - MemoryContextSwitchTo(oldcontext); - ## src/bin/pg_verifybackup/parse_manifest.c ## -@@ src/bin/pg_verifybackup/parse_manifest.c: void - json_parse_manifest(JsonManifestParseContext *context, char *buffer, - size_t size) - { -- JsonLexContext *lex; -+ JsonLexContext lex = {0}; - JsonParseErrorType json_error; - JsonSemAction sem; - JsonManifestParseState parse; -@@ src/bin/pg_verifybackup/parse_manifest.c: json_parse_manifest(JsonManifestParseContext *context, char *buffer, - parse.state = JM_EXPECT_TOPLEVEL_START; - parse.saw_version_field = false; - -- /* Create a JSON lexing context. */ -- lex = makeJsonLexContextCstringLen(buffer, size, PG_UTF8, true); -+ /* Initialize a JSON lexing context. */ -+ initJsonLexContextCstringLen(&lex, buffer, size, PG_UTF8, true); - - /* Set up semantic actions. */ - sem.semstate = &parse; @@ src/bin/pg_verifybackup/parse_manifest.c: json_parse_manifest(JsonManifestParseContext *context, char *buffer, - sem.scalar = json_manifest_scalar; - /* Run the actual JSON parser. */ -- json_error = pg_parse_json(lex, &sem); -+ json_error = pg_parse_json(&lex, &sem); + json_error = pg_parse_json(lex, &sem); if (json_error != JSON_SUCCESS) - json_manifest_parse_failure(context, "parsing failed"); -+ json_manifest_parse_failure(context, json_errdetail(json_error, &lex)); ++ json_manifest_parse_failure(context, json_errdetail(json_error, lex)); if (parse.state != JM_EXPECT_EOF) json_manifest_parse_failure(context, "manifest ended unexpectedly"); - /* Verify the manifest checksum. */ - verify_manifest_checksum(&parse, buffer, size); -+ -+ /* Clean up. */ -+ termJsonLexContext(&lex); - } - - /* ## src/bin/pg_verifybackup/t/005_bad_manifest.pl ## @@ src/bin/pg_verifybackup/t/005_bad_manifest.pl: use Test::More; @@ src/common/jsonapi.c /* * The context of the parser is maintained by the recursive descent * mechanism, but is passed explicitly to the error reporting routine -@@ src/common/jsonapi.c: IsValidJsonNumber(const char *str, int len) - return (!numeric_error) && (total_len == dummy_lex.input_length); - } - -+#ifndef FRONTEND -+ - /* - * makeJsonLexContextCstringLen - * -- * lex constructor, with or without StringInfo object for de-escaped lexemes. -+ * lex constructor, with or without a string object for de-escaped lexemes. - * - * Without is better as it makes the processing faster, so only make one - * if really required. -@@ src/common/jsonapi.c: makeJsonLexContextCstringLen(char *json, int len, int encoding, bool need_escape - { - JsonLexContext *lex = palloc0(sizeof(JsonLexContext)); - -+ initJsonLexContextCstringLen(lex, json, len, encoding, need_escapes); -+ -+ return lex; -+} -+ -+void -+destroyJsonLexContext(JsonLexContext *lex) -+{ -+ termJsonLexContext(lex); -+ pfree(lex); -+} -+ -+#endif /* !FRONTEND */ -+ -+void -+initJsonLexContextCstringLen(JsonLexContext *lex, char *json, int len, int encoding, bool need_escapes) -+{ - lex->input = lex->token_terminator = lex->line_start = json; - lex->line_number = 1; - lex->input_length = len; +@@ src/common/jsonapi.c: makeJsonLexContextCstringLen(JsonLexContext *lex, char *json, lex->input_encoding = encoding; -- if (need_escapes) + if (need_escapes) + { - lex->strval = makeStringInfo(); -- return lex; -+ lex->parse_strval = need_escapes; -+ if (lex->parse_strval) -+ { + /* + * This call can fail in FRONTEND code. We defer error handling to time -+ * of use (json_lex_string()) since there's no way to signal failure -+ * here, and we might not need to parse any strings anyway. ++ * of use (json_lex_string()) since we might not need to parse any ++ * strings anyway. + */ + lex->strval = createStrVal(); -+ } + lex->flags |= JSONLEX_FREE_STRVAL; ++ lex->parse_strval = true; + } + lex->errormsg = NULL; -+} -+ -+void -+termJsonLexContext(JsonLexContext *lex) -+{ + + return lex; + } +@@ src/common/jsonapi.c: makeJsonLexContextCstringLen(JsonLexContext *lex, char *json, + void + freeJsonLexContext(JsonLexContext *lex) + { + static const JsonLexContext empty = {0}; + -+ if (lex->strval) -+ { + if (lex->flags & JSONLEX_FREE_STRVAL) + { +#ifdef FRONTEND + destroyPQExpBuffer(lex->strval); +#else -+ pfree(lex->strval->data); -+ pfree(lex->strval); + pfree(lex->strval->data); + pfree(lex->strval); +#endif + } -+ + if (lex->errormsg) + { +#ifdef FRONTEND @@ src/common/jsonapi.c: makeJsonLexContextCstringLen(char *json, int len, int enco + pfree(lex->errormsg->data); + pfree(lex->errormsg); +#endif -+ } -+ -+ *lex = empty; + } + if (lex->flags & JSONLEX_FREE_STRUCT) + pfree(lex); ++ else ++ *lex = empty; } /* @@ src/common/jsonapi.c: json_errdetail(JsonParseErrorType error, JsonLexContext *l + return lex->errormsg->data; +} + ## src/common/meson.build ## +@@ src/common/meson.build: common_sources_frontend_static += files( + # least cryptohash_openssl.c, hmac_openssl.c depend on it. + # controldata_utils.c depends on wait_event_types_h. That's arguably a + # layering violation, but ... ++# ++# XXX Frontend builds need libpq's pqexpbuffer.h, so adjust the include paths ++# appropriately. This seems completely broken. + pgcommon = {} + pgcommon_variants = { + '_srv': internal_lib_args + { ++ 'include_directories': include_directories('.'), + 'sources': common_sources + [lwlocknames_h] + [wait_event_types_h], + 'dependencies': [backend_common_code], + }, + '': default_lib_args + { ++ 'include_directories': include_directories('../interfaces/libpq', '.'), + 'sources': common_sources_frontend_static, + 'dependencies': [frontend_common_code], + # Files in libpgcommon.a should use/export the "xxx_private" versions +@@ src/common/meson.build: pgcommon_variants = { + }, + '_shlib': default_lib_args + { + 'pic': true, ++ 'include_directories': include_directories('../interfaces/libpq', '.'), + 'sources': common_sources_frontend_shlib, + 'dependencies': [frontend_common_code], + }, +@@ src/common/meson.build: foreach name, opts : pgcommon_variants + c_args = opts.get('c_args', []) + common_cflags[cflagname] + cflag_libs += static_library('libpgcommon@0@_@1@'.format(name, cflagname), + c_pch: pch_c_h, +- include_directories: include_directories('.'), + kwargs: opts + { + 'sources': sources, + 'c_args': c_args, +@@ src/common/meson.build: foreach name, opts : pgcommon_variants + lib = static_library('libpgcommon@0@'.format(name), + link_with: cflag_libs, + c_pch: pch_c_h, +- include_directories: include_directories('.'), + kwargs: opts + { + 'dependencies': opts['dependencies'] + [ssl], + } + ## src/include/common/jsonapi.h ## @@ #ifndef JSONAPI_H @@ src/include/common/jsonapi.h: typedef enum JsonParseErrorType JSON_UNICODE_ESCAPE_FORMAT, JSON_UNICODE_HIGH_ESCAPE, @@ src/include/common/jsonapi.h: typedef enum JsonParseErrorType - JSON_SEM_ACTION_FAILED /* error should already be reported */ + JSON_SEM_ACTION_FAILED, /* error should already be reported */ } JsonParseErrorType; +/* @@ src/include/common/jsonapi.h: typedef enum JsonParseErrorType /* * All the fields in this structure should be treated as read-only. @@ src/include/common/jsonapi.h: typedef struct JsonLexContext - int lex_level; + bits32 flags; int line_number; /* line number, starting from 1 */ char *line_start; /* where that line starts within input */ - StringInfo strval; @@ src/include/common/jsonapi.h: typedef struct JsonLexContext } JsonLexContext; typedef JsonParseErrorType (*json_struct_action) (void *state); -@@ src/include/common/jsonapi.h: extern PGDLLIMPORT JsonSemAction nullSemAction; - */ - extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex, - int *elements); -+#ifndef FRONTEND - - /* -- * constructor for JsonLexContext, with or without strval element. -+ * allocating constructor for JsonLexContext, with or without strval element. - * If supplied, the strval element will contain a de-escaped version of - * the lexeme. However, doing this imposes a performance penalty, so - * it should be avoided if the de-escaped lexeme is not required. -@@ src/include/common/jsonapi.h: extern JsonLexContext *makeJsonLexContextCstringLen(char *json, - int encoding, - bool need_escapes); - -+/* -+ * Counterpart to makeJsonLexContextCstringLen(): clears and deallocates lex. -+ * The context pointer should not be used after this call. -+ */ -+extern void destroyJsonLexContext(JsonLexContext *lex); -+ -+#endif /* !FRONTEND */ -+ -+/* -+ * stack constructor for JsonLexContext, with or without strval element. -+ * If supplied, the strval element will contain a de-escaped version of -+ * the lexeme. However, doing this imposes a performance penalty, so -+ * it should be avoided if the de-escaped lexeme is not required. -+ */ -+extern void initJsonLexContextCstringLen(JsonLexContext *lex, -+ char *json, -+ int len, -+ int encoding, -+ bool need_escapes); -+ -+/* -+ * Counterpart to initJsonLexContextCstringLen(): clears the contents of lex, -+ * but does not deallocate lex itself. -+ */ -+extern void termJsonLexContext(JsonLexContext *lex); -+ - /* lex one token */ - extern JsonParseErrorType json_lex(JsonLexContext *lex); - 2: 02eea9ffe0 ! 2: e941ba5807 libpq: add OAUTHBEARER SASL mechanism @@ src/Makefile.global.in: with_ldap = @with_ldap@ with_uuid = @with_uuid@ with_zlib = @with_zlib@ - ## src/common/meson.build ## -@@ src/common/meson.build: common_sources_frontend_static += files( - # least cryptohash_openssl.c, hmac_openssl.c depend on it. - # controldata_utils.c depends on wait_event_types_h. That's arguably a - # layering violation, but ... -+# -+# XXX Frontend builds need libpq's pqexpbuffer.h, so adjust the include paths -+# appropriately. This seems completely broken. - pgcommon = {} - pgcommon_variants = { - '_srv': internal_lib_args + { -+ 'include_directories': include_directories('.'), - 'sources': common_sources + [lwlocknames_h] + [wait_event_types_h], - 'dependencies': [backend_common_code], - }, - '': default_lib_args + { -+ 'include_directories': include_directories('../interfaces/libpq', '.'), - 'sources': common_sources_frontend_static, - 'dependencies': [frontend_common_code], - }, - '_shlib': default_lib_args + { - 'pic': true, -+ 'include_directories': include_directories('../interfaces/libpq', '.'), - 'sources': common_sources_frontend_shlib, - 'dependencies': [frontend_common_code], - }, -@@ src/common/meson.build: foreach name, opts : pgcommon_variants - c_args = opts.get('c_args', []) + common_cflags[cflagname] - cflag_libs += static_library('libpgcommon@0@_@1@'.format(name, cflagname), - c_pch: pch_c_h, -- include_directories: include_directories('.'), - kwargs: opts + { - 'sources': sources, - 'c_args': c_args, -@@ src/common/meson.build: foreach name, opts : pgcommon_variants - lib = static_library('libpgcommon@0@'.format(name), - link_with: cflag_libs, - c_pch: pch_c_h, -- include_directories: include_directories('.'), - kwargs: opts + { - 'dependencies': opts['dependencies'] + [ssl], - } - ## src/include/common/oauth-common.h (new) ## @@ +/*------------------------------------------------------------------------- @@ src/interfaces/libpq/fe-auth-oauth-curl.c (new) + goto cleanup; + } + -+ initJsonLexContextCstringLen(&lex, resp->data, resp->len, PG_UTF8, true); ++ makeJsonLexContextCstringLen(&lex, resp->data, resp->len, PG_UTF8, true); + + ctx.errbuf = &actx->errbuf; + ctx.fields = fields; @@ src/interfaces/libpq/fe-auth-oauth-curl.c (new) + success = true; + +cleanup: -+ termJsonLexContext(&lex); ++ freeJsonLexContext(&lex); + return success; +} + @@ src/interfaces/libpq/fe-auth-oauth.c (new) + return false; + } + -+ initJsonLexContextCstringLen(&lex, msg, msglen, PG_UTF8, true); ++ makeJsonLexContextCstringLen(&lex, msg, msglen, PG_UTF8, true); + + initPQExpBuffer(&ctx.errbuf); + sem.semstate = &ctx; @@ src/interfaces/libpq/fe-auth-oauth.c (new) + + /* Don't need the error buffer or the JSON lexer anymore. */ + termPQExpBuffer(&ctx.errbuf); -+ termJsonLexContext(&lex); ++ freeJsonLexContext(&lex); + + if (errmsg) + return false; 3: b3a731b695 ! 3: 37eaa5ceb6 backend: add OAUTHBEARER SASL mechanism @@ src/include/libpq/auth.h: extern PGDLLIMPORT bool pg_gss_accept_delegation; ## src/include/libpq/hba.h ## @@ src/include/libpq/hba.h: typedef enum UserAuth - uaLDAP, uaCert, uaRADIUS, -- uaPeer + uaPeer, -#define USER_AUTH_LAST uaPeer /* Must be last value of this enum */ -+ uaPeer, -+ uaOAuth ++ uaOAuth, +#define USER_AUTH_LAST uaOAuth /* Must be last value of this enum */ } UserAuth; 4: b2a570114e = 4: 7177943f0c Add pytest suite for OAuth 5: 48cd916bfe = 5: bea695c317 squash! Add pytest suite for OAuth