void __table_log_restore_table_update(SPITupleTable *spi_tuptable, char *table_restore, char *table_orig_pkey, char *col_query_start, int col_pkey, int number_columns, int i, char *old_pkey_string) { int size_of_values, j, ret; char *tmp, *tmp2; /* memory for dynamic query */ int d_query_size; char *d_query; char *d_query_start; /* get the size of names and values */ size_of_values = 0; /* go through all columns in this result */ for (j = 1; j <= number_columns; j++) { /* get value */ tmp = SPI_getvalue(spi_tuptable->vals[i], spi_tuptable->tupdesc, j); /* and get name of column */ tmp2 = SPI_fname(spi_tuptable->tupdesc, j); if (tmp == NULL) { size_of_values += 6 + strlen(do_quote_ident(tmp2)) + 2; } else { size_of_values += strlen(do_quote_literal(tmp)) + strlen(do_quote_ident(tmp2)) + 3; } } /* reserve memory */ d_query_size = 250 + size_of_values + NAMEDATALEN + strlen(do_quote_literal(old_pkey_string)); d_query_start = (char *) palloc((d_query_size + 1) * sizeof(char)); d_query = d_query_start; /* build query */ sprintf(d_query, "UPDATE %s SET ", do_quote_ident(table_restore)); d_query = d_query_start + strlen(d_query_start); for (j = 1; j <= number_columns; j++) { if (j > 1) { strncat(d_query_start, (const char *)", ", d_query_size); d_query += 2; } tmp = SPI_getvalue(spi_tuptable->vals[i], spi_tuptable->tupdesc, j); tmp2 = SPI_fname(spi_tuptable->tupdesc, j); if (tmp == NULL) { snprintf(d_query, d_query_size, "%s=NULL", do_quote_ident(tmp2)); } else { snprintf(d_query, d_query_size, "%s=%s", do_quote_ident(tmp2), do_quote_literal(tmp)); } d_query = d_query_start + strlen(d_query_start); } snprintf(d_query, d_query_size, " WHERE %s=%s", do_quote_ident(table_orig_pkey), do_quote_literal(old_pkey_string)); d_query = d_query_start + strlen(d_query_start); #ifdef TABLE_LOG_DEBUG_QUERY elog(NOTICE, "query: %s", d_query_start); #endif /* TABLE_LOG_DEBUG_QUERY */ ret = SPI_exec(d_query_start, 0); if (ret != SPI_OK_UPDATE) { elog(ERROR, "could not update data in: %s", table_restore); } /* done */ }
/* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnName * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName(JNIEnv* env, jclass cls, jlong _this, jint index) { jstring result = 0; BEGIN_NATIVE PG_TRY(); { char* name; Ptr2Long p2l; p2l.longVal = _this; name = SPI_fname((TupleDesc)p2l.ptrVal, (int)index); if(name == 0) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = String_createJavaStringFromNTS(name); pfree(name); } } PG_CATCH(); { Exception_throw_ERROR("SPI_fname"); } PG_END_TRY(); END_NATIVE return result; }
static void process_delete(PgqTriggerEvent *ev, StringInfo sql) { TriggerData *tg = ev->tgdata; HeapTuple old_row = tg->tg_trigtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; char *col_ident; char *col_value; int i; int need_and = false; int attkind_idx; for (i = 0, attkind_idx = -1; i < tupdesc->natts; i++) { if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!pgqtriga_is_pkey(ev, i, attkind_idx)) continue; col_ident = SPI_fname(tupdesc, i + 1); col_value = SPI_getvalue(old_row, tupdesc, i + 1); if (need_and) appendStringInfoString(sql, " and "); else need_and = true; append_key_eq(sql, col_ident, col_value); } }
/* * plphp_htup_from_zval * Build a HeapTuple from a zval (which must be an array) and a TupleDesc. * * The return HeapTuple is allocated in the current memory context and must * be freed by the caller. * * If zval doesn't contain any of the element names from the TupleDesc, * build a tuple from the first N elements. This allows us to accept * arrays in form array(1,2,3) as the result of functions with OUT arguments. * XXX -- possible optimization: keep the memory context created and only * reset it between calls. */ HeapTuple plphp_htup_from_zval(zval *val, TupleDesc tupdesc) { MemoryContext oldcxt; MemoryContext tmpcxt; HeapTuple ret; AttInMetadata *attinmeta; HashPosition pos; zval **element; char **values; int i; bool allempty = true; tmpcxt = AllocSetContextCreate(TopTransactionContext, "htup_from_zval cxt", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(tmpcxt); values = (char **) palloc(tupdesc->natts * sizeof(char *)); for (i = 0; i < tupdesc->natts; i++) { char *key = SPI_fname(tupdesc, i + 1); zval *scalarval = plphp_array_get_elem(val, key); values[i] = plphp_zval_get_cstring(scalarval, true, true); /* * Reset the flag is even one of the keys actually exists, * even if it is NULL. */ if (scalarval != NULL) allempty = false; } /* None of the names from the tuple exists, * try to get 1st N array elements and assign them to the tuple */ if (allempty) for (i = 0, zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(val), &pos); (zend_hash_get_current_data_ex(Z_ARRVAL_P(val), (void **) &element, &pos) == SUCCESS) && (i < tupdesc->natts); zend_hash_move_forward_ex(Z_ARRVAL_P(val), &pos), i++) values[i] = plphp_zval_get_cstring(element[0], true, true); attinmeta = TupleDescGetAttInMetadata(tupdesc); MemoryContextSwitchTo(oldcxt); ret = BuildTupleFromCStrings(attinmeta, values); MemoryContextDelete(tmpcxt); return ret; }
void pgq_urlenc_row(PgqTriggerEvent *ev, HeapTuple row, StringInfo buf) { TriggerData *tg = ev->tgdata; TupleDesc tupdesc = tg->tg_relation->rd_att; bool first = true; int i; const char *col_ident, *col_value; int attkind_idx = -1; for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (pgqtriga_skip_col(ev, i, attkind_idx)) continue; if (first) first = false; else appendStringInfoChar(buf, '&'); /* quote column name */ col_ident = SPI_fname(tupdesc, i + 1); pgq_encode_cstring(buf, col_ident, TBUF_QUOTE_URLENC); /* quote column value */ col_value = SPI_getvalue(row, tupdesc, i + 1); if (col_value != NULL) { appendStringInfoChar(buf, '='); pgq_encode_cstring(buf, col_value, TBUF_QUOTE_URLENC); } } }
int create_sqlite_table(Portal *cur,sqlite3 *db,char *insert_str, char *dataset_name, char *twkb_name, char *id_name, int create) { char create_table_string[SQLSTRLEN]; char tmp_str[64]; TupleDesc tupdesc; int i, rc; int strlengd = 0; int strlengd_ins = 0; char *err_msg, sqlitetype[15] ; char field_name[128]; char value_list[256]; int strlengd_vals = 0; /*Get fielads definition by fetching 0 rows*/ SPI_cursor_fetch(*cur, true,0); if(create) { snprintf(create_table_string, sizeof(create_table_string), " %s%s%s","create table ",dataset_name,"("); strlengd = strlen(create_table_string); } snprintf(insert_str,SQLSTRLEN, " %s%s%s","insert into " ,dataset_name,"("); strlengd_ins = strlen(insert_str); snprintf(value_list,sizeof(value_list), " %s","values("); strlengd_vals = strlen(value_list); tupdesc = SPI_tuptable->tupdesc; for (i = 1; i <= tupdesc->natts; i++) { snprintf(field_name, sizeof(field_name), "%s", SPI_fname(tupdesc, i)); //convert type to sqlite type getsqlitetype(SPI_gettype(tupdesc, i), sqlitetype); if (strcmp(field_name, id_name)==0) { snprintf(tmp_str, sizeof(tmp_str), " %s%s", " id integer primary key", (i == tupdesc->natts) ? " " : ", "); //construct the insert string with field names snprintf(insert_str+strlengd_ins, SQLSTRLEN-strlengd_ins, "%s%s", "id", (i == tupdesc->natts) ? " " : ", "); strlengd_ins += 3; //adding 1 for the comma-sign } else if (strcmp(field_name, twkb_name)==0) { snprintf(tmp_str, sizeof(tmp_str), " %s%s", " twkb blob", (i == tupdesc->natts) ? " " : ", "); //construct the insert string with field names snprintf(insert_str+strlengd_ins, SQLSTRLEN-strlengd_ins, "%s%s", "twkb", (i == tupdesc->natts) ? " " : ", "); strlengd_ins += 5; //adding 1 for the comma-sign } else { //put together field name, type and comma sign if not last column snprintf(tmp_str, sizeof(tmp_str), " %s %s%s", field_name, sqlitetype, (i == tupdesc->natts) ? " " : ", "); //construct the insert string with field names snprintf(insert_str+strlengd_ins, SQLSTRLEN-strlengd_ins, "%s%s", field_name, (i == tupdesc->natts) ? " " : ", "); strlengd_ins += strlen(field_name)+1; //adding 1 for the comma-sign } if(create) { //put the column name and type in the create-table sql-string snprintf(create_table_string+strlengd, sizeof(create_table_string)-strlengd, " %s",tmp_str); strlengd += strlen(tmp_str); } //construct the value part of the insert snprintf(value_list+strlengd_vals, sizeof(value_list)-strlengd_vals, "%s%s", "?", (i == tupdesc->natts) ? " " : ", "); strlengd_vals += 2; //adding 1 for the comma-sign // elog(INFO, "strlength %d, temp: %s",strlengd_ins, insert_str); } if(create) snprintf(create_table_string+strlengd, sizeof(create_table_string)-strlengd, " %s",")"); elog(INFO, " SQLSTRLEN-strlengd_ins: %d, insert sql: %s", SQLSTRLEN-strlengd_ins, insert_str); // snprintf(insert_str+strlengd_ins, SQLSTRLEN-strlengd_ins, " %s",")"); snprintf(insert_str+strlengd_ins, SQLSTRLEN-strlengd_ins, " %s%s%s",")",value_list,")" ); elog(INFO, "sql: %s", create_table_string); elog(INFO, "insert sql: %s", insert_str); if(create) { rc = sqlite3_exec(db, create_table_string, NULL, 0, &err_msg); if (rc != SQLITE_OK ) { sqlite3_free(err_msg); sqlite3_close(db); ereport(ERROR, ( errmsg("Problem creating table: %s", err_msg))); //fprintf(stderr, "SQL error: %s\n", err_msg); } } return 0; }
Datum funny_dup17(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; TransactionId *xid; int *level; bool *recursion; Relation rel; TupleDesc tupdesc; HeapTuple tuple; char *query, *fieldval, *fieldtype; char *when; int inserted; int selected = 0; int ret; if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "funny_dup17: not fired by trigger manager"); tuple = trigdata->tg_trigtuple; rel = trigdata->tg_relation; tupdesc = rel->rd_att; if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { xid = &fd17b_xid; level = &fd17b_level; recursion = &fd17b_recursion; when = "BEFORE"; } else { xid = &fd17a_xid; level = &fd17a_level; recursion = &fd17a_recursion; when = "AFTER "; } if (!TransactionIdIsCurrentTransactionId(*xid)) { *xid = GetCurrentTransactionId(); *level = 0; *recursion = true; } if (*level == 17) { *recursion = false; return PointerGetDatum(tuple); } if (!(*recursion)) return PointerGetDatum(tuple); (*level)++; SPI_connect(); fieldval = SPI_getvalue(tuple, tupdesc, 1); fieldtype = SPI_gettype(tupdesc, 1); query = (char *) palloc(100 + NAMEDATALEN * 3 + strlen(fieldval) + strlen(fieldtype)); sprintf(query, "insert into %s select * from %s where %s = '%s'::%s", SPI_getrelname(rel), SPI_getrelname(rel), SPI_fname(tupdesc, 1), fieldval, fieldtype); if ((ret = SPI_exec(query, 0)) < 0) elog(ERROR, "funny_dup17 (fired %s) on level %3d: SPI_exec (insert ...) returned %d", when, *level, ret); inserted = SPI_processed; sprintf(query, "select count (*) from %s where %s = '%s'::%s", SPI_getrelname(rel), SPI_fname(tupdesc, 1), fieldval, fieldtype); if ((ret = SPI_exec(query, 0)) < 0) elog(ERROR, "funny_dup17 (fired %s) on level %3d: SPI_exec (select ...) returned %d", when, *level, ret); if (SPI_processed > 0) { selected = DatumGetInt32(DirectFunctionCall1(int4in, CStringGetDatum(SPI_getvalue( SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1 )))); } elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected", when, *level, inserted, selected); SPI_finish(); (*level)--; if (*level == 0) *xid = InvalidTransactionId; return PointerGetDatum(tuple); }
static void pgq_jsonenc_row(PgqTriggerEvent *ev, HeapTuple row, StringInfo buf) { Oid col_type; Datum col_datum; bool isnull; TriggerData *tg = ev->tgdata; TupleDesc tupdesc = tg->tg_relation->rd_att; bool first = true; int i; const char *col_ident, *col_value; int attkind_idx = -1; if (ev->op_type == 'R') { appendStringInfoString(buf, "{}"); return; } appendStringInfoChar(buf, '{'); for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* Skip dropped columns */ if (TupleDescAttr(tupdesc, i)->attisdropped) continue; attkind_idx++; if (pgqtriga_skip_col(ev, i, attkind_idx)) continue; if (first) first = false; else appendStringInfoChar(buf, ','); /* quote column name */ col_ident = SPI_fname(tupdesc, i + 1); pgq_encode_cstring(buf, col_ident, TBUF_QUOTE_JSON); appendStringInfoChar(buf, ':'); /* quote column value */ col_type = TupleDescAttr(tupdesc, i)->atttypid; col_datum = SPI_getbinval(row, tupdesc, i + 1, &isnull); col_value = NULL; if (isnull) { appendStringInfoString(buf, "null"); continue; } switch (col_type) { case BOOLOID: if (DatumGetBool(col_datum)) { appendStringInfoString(buf, "true"); } else { appendStringInfoString(buf, "false"); } break; case TIMESTAMPOID: timestamp_to_json(col_datum, buf); break; case TIMESTAMPTZOID: timestamptz_to_json(col_datum, buf); break; case DATEOID: date_to_json(col_datum, buf); break; case INT2OID: appendStringInfo(buf, "%d", (int)DatumGetInt16(col_datum)); break; case INT4OID: appendStringInfo(buf, "%d", (int)DatumGetInt32(col_datum)); break; case INT8OID: col_value = SPI_getvalue(row, tupdesc, i + 1); appendStringInfoString(buf, col_value); break; default: col_value = SPI_getvalue(row, tupdesc, i + 1); pgq_encode_cstring(buf, col_value, TBUF_QUOTE_JSON); break; } if (col_value) pfree((void*)col_value); } appendStringInfoChar(buf, '}'); }
/* __table_log() helper function for table_log() parameter: - trigger data - change mode (INSERT, UPDATE, DELETE) - tuple to log (old, new) - pointer to tuple - number columns in table - logging table - flag for writing session user return: none */ static void __table_log (TriggerData *trigdata, char *changed_mode, char *changed_tuple, HeapTuple tuple, int number_columns, char *log_table, int use_session_user, char *log_schema) { char *before_char; int i, col_nr, found_col; /* start with 100 bytes */ int size_query = 100; char *query; char *query_start; int ret; #ifdef TABLE_LOG_DEBUG elog(NOTICE, "calculate query size"); #endif /* TABLE_LOG_DEBUG */ /* add all sizes we need and know at this point */ size_query += strlen(changed_mode) + strlen(changed_tuple) + strlen(log_table) + strlen(log_schema); /* calculate size of the columns */ col_nr = 0; for (i = 1; i <= number_columns; i++) { col_nr++; found_col = 0; do { if (trigdata->tg_relation->rd_att->attrs[col_nr - 1]->attisdropped) { /* this column is dropped, skip it */ col_nr++; continue; } else { found_col++; } } while (found_col == 0); /* the column name */ size_query += strlen(do_quote_ident(SPI_fname(trigdata->tg_relation->rd_att, col_nr))) + 3; /* the value */ before_char = SPI_getvalue(tuple, trigdata->tg_relation->rd_att, col_nr); /* old size plus this char and 3 bytes for , and so */ if (before_char == NULL) { size_query += 6; } else { size_query += strlen(do_quote_literal(before_char)) + 3; } } if (use_session_user == 1) { /* add memory for session user */ size_query += NAMEDATALEN + 20; } #ifdef TABLE_LOG_DEBUG // elog(NOTICE, "query size: %i", size_query); #endif /* TABLE_LOG_DEBUG */ #ifdef TABLE_LOG_DEBUG elog(NOTICE, "build query"); #endif /* TABLE_LOG_DEBUG */ /* allocate memory */ query_start = (char *) palloc(size_query * sizeof(char)); query = query_start; /* build query */ sprintf(query, "INSERT INTO %s.%s (", do_quote_ident(log_schema), do_quote_ident(log_table)); query = query_start + strlen(query); /* add colum names */ col_nr = 0; for (i = 1; i <= number_columns; i++) { col_nr++; found_col = 0; do { if (trigdata->tg_relation->rd_att->attrs[col_nr - 1]->attisdropped) { /* this column is dropped, skip it */ col_nr++; continue; } else { found_col++; } } while (found_col == 0); sprintf(query, "%s, ", do_quote_ident(SPI_fname(trigdata->tg_relation->rd_att, col_nr))); query = query_start + strlen(query_start); } /* add session user */ if (use_session_user == 1) { sprintf(query, "trigger_user, "); query = query_start + strlen(query_start); } /* add the 3 extra colum names */ sprintf(query, "trigger_mode, trigger_tuple, trigger_changed) VALUES ("); query = query_start + strlen(query_start); /* add values */ col_nr = 0; for (i = 1; i <= number_columns; i++) { col_nr++; found_col = 0; do { if (trigdata->tg_relation->rd_att->attrs[col_nr - 1]->attisdropped) { /* this column is dropped, skip it */ col_nr++; continue; } else { found_col++; } } while (found_col == 0); before_char = SPI_getvalue(tuple, trigdata->tg_relation->rd_att, col_nr); if (before_char == NULL) { sprintf(query, "NULL, "); } else { sprintf(query, "%s, ", do_quote_literal(before_char)); } query = query_start + strlen(query_start); } /* add session user */ if (use_session_user == 1) { sprintf(query, "SESSION_USER, "); query = query_start + strlen(query_start); } /* add the 3 extra values */ sprintf(query, "%s, %s, NOW());", do_quote_literal(changed_mode), do_quote_literal(changed_tuple)); query = query_start + strlen(query_start); #ifdef TABLE_LOG_DEBUG_QUERY elog(NOTICE, "query: %s", query_start); #else #ifdef TABLE_LOG_DEBUG elog(NOTICE, "execute query"); #endif /* TABLE_LOG_DEBUG */ #endif /* TABLE_LOG_DEBUG_QUERY */ /* execute insert */ ret = SPI_exec(query_start, 0); if (ret != SPI_OK_INSERT) { elog(ERROR, "could not insert log information into relation %s (error: %d)", log_table, ret); } #ifdef TABLE_LOG_DEBUG elog(NOTICE, "done"); #endif /* TABLE_LOG_DEBUG */ /* clean up */ pfree(query_start); }
Datum serialize_record( PG_FUNCTION_ARGS ) { // FILE* log; // log = fopen("/var/lib/postgresql/serializer.log", "a"); HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); HeapTupleData tuple; bool needComma = false; int i; Datum *values; bool *nulls; StringInfoData buf; char *conversion_buf; /* Extract type info from the tuple itself */ Oid tupType = HeapTupleHeaderGetTypeId(rec); int32 tupTypmod = HeapTupleHeaderGetTypMod(rec); TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); int ncolumns = tupdesc->natts; /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; // fprintf(log, "Doing serialize_record\n"); // fflush(log); values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); /* And build the result string */ initStringInfo(&buf); appendStringInfoChar(&buf, '{'); for (i = 0; i < ncolumns; i++) { Oid column_type = tupdesc->attrs[ i ]->atttypid; char *value; char *column_name; char type_category; HeapTuple type_tuple; FmgrInfo flinfo; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; if (nulls[i]) { /* emit nothing... */ continue; } if (needComma) appendStringInfoChar(&buf, ','); needComma = true; /* obtain column name */ column_name = SPI_fname( tupdesc, i + 1 ); /* obtain type information from pg_catalog */ type_tuple = SearchSysCache1( TYPEOID, ObjectIdGetDatum(column_type) ); if (!HeapTupleIsValid( type_tuple )) elog(ERROR, "cache lookup failed for relation %u", column_type); type_category = ((Form_pg_type) GETSTRUCT( type_tuple ))->typcategory; ReleaseSysCache( type_tuple ); /* append column name */ appendStringInfoChar(&buf, '"'); appendStringInfoString(&buf, column_name); appendStringInfoString(&buf, "\":"); switch( type_category ) { // http://www.postgresql.org/docs/current/static/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE case 'A': //array //call to serialize_array( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_array; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, values[ i ] ) ); appendStringInfoString(&buf, value); break; case 'C': //composite //recursive call to serialize_record( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_record; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, values[ i ] ) ); appendStringInfoString(&buf, value); break; case 'N': //numeric conversion_buf = NULL; // get column text value // fprintf(log, "Calling ConvertToText\n"); // fflush(log); value = ConvertToText( values[ i ], column_type, fcinfo->flinfo->fn_mcxt, &conversion_buf ); // fprintf(log, "ConvertToText succeded\n"); // fflush(log); appendStringInfoString(&buf, value); // fprintf(log, "append.... succeded\n"); // fflush(log); if(conversion_buf != NULL) { pfree(conversion_buf); conversion_buf = NULL; } break; case 'B': //boolean appendStringInfoString(&buf, // get column boolean value DatumGetBool( values[ i ] ) ? "true" : "false" ); break; default: //another conversion_buf = NULL; // get column text value // fprintf(log, "Calling ConvertToText\n"); // fflush(log); value = ConvertToText( values[ i ], column_type, fcinfo->flinfo->fn_mcxt, &conversion_buf ); // fprintf(log, "ConvertToText succeded\n"); // fflush(log); appendStringInfoQuotedString(&buf, value); // fprintf(log, "append.... succeded\n"); // fflush(log); if(conversion_buf != NULL) { pfree(conversion_buf); conversion_buf = NULL; } } } appendStringInfoChar(&buf, '}'); pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); // fclose(log); PG_RETURN_TEXT_P( PG_CSTR_GET_TEXT( buf.data ) ); }
Datum _Slony_I_logTrigger(PG_FUNCTION_ARGS) { TransactionId newXid = GetTopTransactionId(); Slony_I_ClusterStatus *cs; TriggerData *tg; Datum argv[4]; text *cmdtype = NULL; int rc; Name cluster_name; int32 tab_id; char *attkind; int attkind_idx; int cmddata_need; /* * Don't do any logging if the current session role isn't Origin. */ if (SessionReplicationRole != SESSION_REPLICATION_ROLE_ORIGIN) return PointerGetDatum(NULL); /* * Get the trigger call context */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "Slony-I: logTrigger() not called as trigger"); tg = (TriggerData *) (fcinfo->context); /* * Check all logTrigger() calling conventions */ if (!TRIGGER_FIRED_AFTER(tg->tg_event)) elog(ERROR, "Slony-I: logTrigger() must be fired AFTER"); if (!TRIGGER_FIRED_FOR_ROW(tg->tg_event)) elog(ERROR, "Slony-I: logTrigger() must be fired FOR EACH ROW"); if (tg->tg_trigger->tgnargs != 3) elog(ERROR, "Slony-I: logTrigger() must be defined with 3 args"); /* * Connect to the SPI manager */ if ((rc = SPI_connect()) < 0) elog(ERROR, "Slony-I: SPI_connect() failed in createEvent()"); /* * Get all the trigger arguments */ cluster_name = DatumGetName(DirectFunctionCall1(namein, CStringGetDatum(tg->tg_trigger->tgargs[0]))); tab_id = strtol(tg->tg_trigger->tgargs[1], NULL, 10); attkind = tg->tg_trigger->tgargs[2]; /* * Get or create the cluster status information and make sure it has the * SPI plans that we need here. */ cs = getClusterStatus(cluster_name, PLAN_INSERT_LOG); /* * Do the following only once per transaction. */ if (!TransactionIdEquals(cs->currentXid, newXid)) { int32 log_status; bool isnull; /* * Determine the currently active log table */ if (SPI_execp(cs->plan_get_logstatus, NULL, NULL, 0) < 0) elog(ERROR, "Slony-I: cannot determine log status"); if (SPI_processed != 1) elog(ERROR, "Slony-I: cannot determine log status"); log_status = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); SPI_freetuptable(SPI_tuptable); switch (log_status) { case 0: case 2: cs->plan_active_log = cs->plan_insert_log_1; break; case 1: case 3: cs->plan_active_log = cs->plan_insert_log_2; break; default: elog(ERROR, "Slony-I: illegal log status %d", log_status); break; } cs->currentXid = newXid; } /* * Determine cmdtype and cmddata depending on the command type */ if (TRIGGER_FIRED_BY_INSERT(tg->tg_event)) { HeapTuple new_row = tg->tg_trigtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; char *col_ident; char *col_value; int len_ident; int len_value; int i; int need_comma = false; char *OldDateStyle; char *cp = VARDATA(cs->cmddata_buf); /* * INSERT * * cmdtype = 'I' cmddata = ("col" [, ...]) values ('value' [, ...]) */ cmdtype = cs->cmdtype_I; /* * Specify all the columns */ *cp++ = '('; for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* * Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_comma) *cp++ = ','; else need_comma = true; memcpy(cp, col_ident, len_ident); cp += len_ident; } /* * Append the string ") values (" */ *cp++ = ')'; *cp++ = ' '; *cp++ = 'v'; *cp++ = 'a'; *cp++ = 'l'; *cp++ = 'u'; *cp++ = 'e'; *cp++ = 's'; *cp++ = ' '; *cp++ = '('; /* * Append the values */ need_comma = false; OldDateStyle = GetConfigOptionByName("DateStyle", NULL); if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", "ISO", PGC_USERSET, PGC_S_SESSION, true, true); for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* * Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; if ((col_value = SPI_getvalue(new_row, tupdesc, i + 1)) == NULL) { col_value = "NULL"; } else { col_value = slon_quote_literal(col_value); } cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_comma) *cp++ = ','; else need_comma = true; memcpy(cp, col_value, len_value); cp += len_value; } if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", OldDateStyle, PGC_USERSET, PGC_S_SESSION, true, true); /* * Terminate and done */ *cp++ = ')'; *cp = '\0'; SET_VARSIZE(cs->cmddata_buf, VARHDRSZ + (cp - VARDATA(cs->cmddata_buf))); } else if (TRIGGER_FIRED_BY_UPDATE(tg->tg_event)) { HeapTuple old_row = tg->tg_trigtuple; HeapTuple new_row = tg->tg_newtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; Datum old_value; Datum new_value; bool old_isnull; bool new_isnull; char *col_ident; char *col_value; int len_ident; int len_value; int i; int need_comma = false; int need_and = false; char *OldDateStyle; char *cp = VARDATA(cs->cmddata_buf); /* * UPDATE * * cmdtype = 'U' cmddata = "col_ident"='value' [, ...] where * "pk_ident" = 'value' [ and ...] */ cmdtype = cs->cmdtype_U; for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* * Ignore dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; old_value = SPI_getbinval(old_row, tupdesc, i + 1, &old_isnull); new_value = SPI_getbinval(new_row, tupdesc, i + 1, &new_isnull); /* * If old and new value are NULL, the column is unchanged */ if (old_isnull && new_isnull) continue; /* * If both are NOT NULL, we need to compare the values and skip * setting the column if equal */ if (!old_isnull && !new_isnull) { Oid opr_oid; FmgrInfo *opr_finfo_p; /* * Lookup the equal operators function call info using the * typecache if available */ #ifdef HAVE_TYPCACHE TypeCacheEntry *type_cache; type_cache = lookup_type_cache( SPI_gettypeid(tupdesc, i + 1), TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO); opr_oid = type_cache->eq_opr; if (opr_oid == ARRAY_EQ_OP) opr_oid = InvalidOid; else opr_finfo_p = &(type_cache->eq_opr_finfo); #else FmgrInfo opr_finfo; opr_oid = compatible_oper_funcid(makeList1(makeString("=")), SPI_gettypeid(tupdesc, i + 1), SPI_gettypeid(tupdesc, i + 1), true); if (OidIsValid(opr_oid)) { fmgr_info(opr_oid, &opr_finfo); opr_finfo_p = &opr_finfo; } #endif /* * If we have an equal operator, use that to do binary * comparision. Else get the string representation of both * attributes and do string comparision. */ if (OidIsValid(opr_oid)) { if (DatumGetBool(FunctionCall2(opr_finfo_p, old_value, new_value))) continue; } else { char *old_strval = SPI_getvalue(old_row, tupdesc, i + 1); char *new_strval = SPI_getvalue(new_row, tupdesc, i + 1); if (strcmp(old_strval, new_strval) == 0) continue; } } if (need_comma) *cp++ = ','; else need_comma = true; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); if (new_isnull) col_value = "NULL"; else { OldDateStyle = GetConfigOptionByName("DateStyle", NULL); if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", "ISO", PGC_USERSET, PGC_S_SESSION, true, true); col_value = slon_quote_literal(SPI_getvalue(new_row, tupdesc, i + 1)); if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", OldDateStyle, PGC_USERSET, PGC_S_SESSION, true, true); } cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } /* * It can happen that the only UPDATE an application does is to set a * column to the same value again. In that case, we'd end up here with * no columns in the SET clause yet. We add the first key column here * with it's old value to simulate the same for the replication * engine. */ if (!need_comma) { for (i = 0, attkind_idx = -1; i < tg->tg_relation->rd_att->natts; i++) { if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!attkind[attkind_idx]) elog(ERROR, "Slony-I: no key columns found in logTrigger() attkind parameter"); if (attkind[attkind_idx] == 'k') break; } col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1)); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } *cp++ = ' '; *cp++ = 'w'; *cp++ = 'h'; *cp++ = 'e'; *cp++ = 'r'; *cp++ = 'e'; *cp++ = ' '; for (i = 0, attkind_idx = -1; i < tg->tg_relation->rd_att->natts; i++) { /* * Ignore dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!attkind[attkind_idx]) break; if (attkind[attkind_idx] != 'k') continue; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1)); if (col_value == NULL) elog(ERROR, "Slony-I: old key column %s.%s IS NULL on UPDATE", NameStr(tg->tg_relation->rd_rel->relname), col_ident); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_and) { *cp++ = ' '; *cp++ = 'a'; *cp++ = 'n'; *cp++ = 'd'; *cp++ = ' '; } else need_and = true; memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } *cp = '\0'; SET_VARSIZE(cs->cmddata_buf, VARHDRSZ + (cp - VARDATA(cs->cmddata_buf))); } else if (TRIGGER_FIRED_BY_DELETE(tg->tg_event)) { HeapTuple old_row = tg->tg_trigtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; char *col_ident; char *col_value; int len_ident; int len_value; int i; int need_and = false; char *cp = VARDATA(cs->cmddata_buf); /* * DELETE * * cmdtype = 'D' cmddata = "pk_ident"='value' [and ...] */ cmdtype = cs->cmdtype_D; for (i = 0, attkind_idx = -1; i < tg->tg_relation->rd_att->natts; i++) { if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!attkind[attkind_idx]) break; if (attkind[attkind_idx] != 'k') continue; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1)); if (col_value == NULL) elog(ERROR, "Slony-I: old key column %s.%s IS NULL on DELETE", NameStr(tg->tg_relation->rd_rel->relname), col_ident); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_and) { *cp++ = ' '; *cp++ = 'a'; *cp++ = 'n'; *cp++ = 'd'; *cp++ = ' '; } else need_and = true; memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } *cp = '\0'; SET_VARSIZE(cs->cmddata_buf, VARHDRSZ + (cp - VARDATA(cs->cmddata_buf))); } else elog(ERROR, "Slony-I: logTrigger() fired for unhandled event"); /* * Construct the parameter array and insert the log row. */ argv[0] = Int32GetDatum(tab_id); argv[1] = PointerGetDatum(cmdtype); argv[2] = PointerGetDatum(cs->cmddata_buf); SPI_execp(cs->plan_active_log, argv, NULL, 0); SPI_finish(); return PointerGetDatum(NULL); }
static void process_insert(PgqTriggerEvent *ev, StringInfo sql) { TriggerData *tg = ev->tgdata; HeapTuple new_row = tg->tg_trigtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; int i; int need_comma = false; int attkind_idx; /* * Specify all the columns */ appendStringInfoChar(sql, '('); attkind_idx = -1; for (i = 0; i < tupdesc->natts; i++) { char *col_ident; /* Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; /* Check if allowed by colstring */ attkind_idx++; if (pgqtriga_skip_col(ev, i, attkind_idx)) continue; if (need_comma) appendStringInfoChar(sql, ','); else need_comma = true; /* quote column name */ col_ident = SPI_fname(tupdesc, i + 1); pgq_encode_cstring(sql, col_ident, TBUF_QUOTE_IDENT); } /* * Append the string ") values (" */ appendStringInfoString(sql, ") values ("); /* * Append the values */ need_comma = false; attkind_idx = -1; for (i = 0; i < tupdesc->natts; i++) { char *col_value; /* Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; /* Check if allowed by colstring */ attkind_idx++; if (pgqtriga_skip_col(ev, i, attkind_idx)) continue; if (need_comma) appendStringInfoChar(sql, ','); else need_comma = true; /* quote column value */ col_value = SPI_getvalue(new_row, tupdesc, i + 1); if (col_value == NULL) appendStringInfoString(sql, "null"); else pgq_encode_cstring(sql, col_value, TBUF_QUOTE_LITERAL); } /* * Terminate and done */ appendStringInfoChar(sql, ')'); }
static int process_update(PgqTriggerEvent *ev, StringInfo sql) { TriggerData *tg = ev->tgdata; HeapTuple old_row = tg->tg_trigtuple; HeapTuple new_row = tg->tg_newtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; Datum old_value; Datum new_value; bool old_isnull; bool new_isnull; char *col_ident; char *col_value; int i; int need_comma = false; int need_and = false; int attkind_idx; int ignore_count = 0; attkind_idx = -1; for (i = 0; i < tupdesc->natts; i++) { /* * Ignore dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; old_value = SPI_getbinval(old_row, tupdesc, i + 1, &old_isnull); new_value = SPI_getbinval(new_row, tupdesc, i + 1, &new_isnull); /* * If old and new value are NULL, the column is unchanged */ if (old_isnull && new_isnull) continue; /* * If both are NOT NULL, we need to compare the values and skip * setting the column if equal */ if (!old_isnull && !new_isnull) { Oid opr_oid; FmgrInfo *opr_finfo_p; /* * Lookup the equal operators function call info using the * typecache if available */ TypeCacheEntry *type_cache; type_cache = lookup_type_cache(SPI_gettypeid(tupdesc, i + 1), TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO); opr_oid = type_cache->eq_opr; if (opr_oid == ARRAY_EQ_OP) opr_oid = InvalidOid; else opr_finfo_p = &(type_cache->eq_opr_finfo); /* * If we have an equal operator, use that to do binary * comparision. Else get the string representation of both * attributes and do string comparision. */ if (OidIsValid(opr_oid)) { if (DatumGetBool(FunctionCall2(opr_finfo_p, old_value, new_value))) continue; } else { char *old_strval = SPI_getvalue(old_row, tupdesc, i + 1); char *new_strval = SPI_getvalue(new_row, tupdesc, i + 1); if (strcmp(old_strval, new_strval) == 0) continue; } } if (pgqtriga_skip_col(ev, i, attkind_idx)) { /* this change should be ignored */ ignore_count++; continue; } if (need_comma) appendStringInfoChar(sql, ','); else need_comma = true; col_ident = SPI_fname(tupdesc, i + 1); col_value = SPI_getvalue(new_row, tupdesc, i + 1); append_normal_eq(sql, col_ident, col_value); } /* * It can happen that the only UPDATE an application does is to set a * column to the same value again. In that case, we'd end up here with * no columns in the SET clause yet. We add the first key column here * with it's old value to simulate the same for the replication * engine. */ if (!need_comma) { /* there was change in ignored columns, skip whole event */ if (ignore_count > 0) return 0; for (i = 0, attkind_idx = -1; i < tupdesc->natts; i++) { if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (pgqtriga_is_pkey(ev, i, attkind_idx)) break; } col_ident = SPI_fname(tupdesc, i + 1); col_value = SPI_getvalue(old_row, tupdesc, i + 1); append_key_eq(sql, col_ident, col_value); } appendStringInfoString(sql, " where "); for (i = 0, attkind_idx = -1; i < tupdesc->natts; i++) { /* * Ignore dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!pgqtriga_is_pkey(ev, i, attkind_idx)) continue; col_ident = SPI_fname(tupdesc, i + 1); col_value = SPI_getvalue(old_row, tupdesc, i + 1); if (need_and) appendStringInfoString(sql, " and "); else need_and = true; append_key_eq(sql, col_ident, col_value); } return 1; }